In my angular-app, I have 3 queries, each depends on the others. I am putting promise() to each of them. In this case how can i get $q.all for all this depended promise once done?
Can anyone clarify this for me, please?
Here is my code :
"use strict";
angular.module("tcpApp")
.controller("projectSummaryController",
['$scope', '$routeParams', '$location', 'server', 'modalService', '$q',
function ( $scope, $routeParams, $location, server, modalService, $q ) {
$scope.projectId = $routeParams.projectId;
$scope.subProjectId = $routeParams.subProjectId;
$scope.phaseId = 0;
$scope.disciplineId = 0;
$scope.contractorId = 0;
$scope.queryConractorInfo = function ( contractorId ) {
server.contractorInfo.get({
projectId:$scope.projectId,
subProjectId : $scope.subProjectId,
contractId : $scope.contractorId,
disciplineId : $scope.disciplineId,
staticId : 0 /* at present static */
}).$promise.then(function ( contractorInfo ) {
$scope.contractorInfo = contractorInfo;
})
}
$scope.queryConractorList = function ( phaseId, disciplineId ) {
var base = 'http://azvsptcsdev02:678/_vti_bin/CPMD.WEBSERVICE/ProjectInfoService.svc/Contracts/'
console.log( base+$scope.projectId+'/'+$scope.subProjectId+'/'+phaseId+'/'+disciplineId );
server.contractorsList.query(
{
projectId:$scope.projectId,
subProjectId : $scope.subProjectId,
phaseId : phaseId,
disciplineId: disciplineId
}).$promise.then(function ( contractorsList ) {
$scope.contractorId = contractorsList[0].Id; //setting the first contractor as default;
$scope.queryConractorInfo( $scope.contractorId );
});
}
$scope.queryProject = function ( prjId, subPrjId ) {
server.projectSummary.get({id:$scope.projectId})
.$promise.then(function (data) {
//only setting phase id and desciple id to get list of contractors
$scope.phaseId = data.PhaseIds[0].Id; //setting the first phase as default;
$scope.disciplineId = data.DisciplineIds[0].Id; //setting the first descipline as default;
$scope.queryConractorList( $scope.phaseId, $scope.disciplineId );
});
}
if($scope.projectId) {
$scope.queryProject();
}
$q.all([ $scope.queryProject, $scope.queryConractorList, $scope.queryConractorInfo ])
.then(function ( data1, data2, data3 ) {
console.log( data1, data2, data3 );// i get nothing here!
})
}]);
What is the best practice to handle this?
Related
Hi I have a method that does multiple request based on _.each iteration. What I want to do is initialize vm.job = job in the getAllJobSublinesByJobline after the _.each iteration
var vm = this;
function getAllJobSublinesByJobline () {
return $resource( '/api/joblines/get_all_jobsublines_by_jobline/:pageLimit', {
'jobLineId' : '#jobLineId',
'page' : '#page',
'search' : '#search',
'sortField' : '#sortField',
'sortType' : '#sortType'
} );
}
function getJobsublinesByJoblineId ( joblines, job ) {
_.each( joblines, function ( jobline ) {
if ( parseInt( jobline.num_sublines ) ) {
var jobsublineFetchDetails = {
'pageLimit' : 10,
'jobLineId' : jobline.id
};
return getAllJobSublinesByJobline().save( jobsublineFetchDetails ).$promise.then( function ( jobsubline ) {
jobline.jobsublines = jobsubline.data;
job.joblines.push( jobline );
} );
}
job.joblines.push( jobline );
} );
vm.job = job; // initializes right away even though _.each iteration is not finished yet
}
My problem is that it initializes right away even though the _.each iteration has not finished fetching data yet. Why is this happening?
From the Docs:
It is important to realize that invoking a $resource object method immediately returns an empty reference (object or array depending on isArray). Once the data is returned from the server the existing reference is populated with the actual data.
-- AngularJS $resource API Reference
So as said in another answer, push promises, and use $q.all.
function getJobsublinesByJoblineId ( joblines, job ) {
var promises = [];
_.each( joblines, function ( jobline ) {
if ( parseInt( jobline.num_sublines ) ) {
var jobParams = {
'pageLimit' : 10,
'jobLineId' : jobline.id
};
var details = getAllJobSublinesByJobline().save( jobParams );
promises.push(details.$promise);
}
} );
$q.all(promises).then (function (detailsArray) {
//assemble job object
vm.job = job;
});
}
You could probably push all promises to an array and use something like
$q.all(yourPromises).then(function(allCompletedResponse){...}).
Since I needed to initialize the jobline on having the jobsubline what I did with mine was similar to the answer above.
function getJobsublinesByJoblineId ( joblines, job ) {
var promises = _.map( joblines, function ( jobline ) {
var jobParams = {
'jobLineId' : jobline.id
};
var deferred = $q.defer();
apiService.getAllJobSublinesByJobline().save( jobParams ).$promise.then( function ( jobsubline ) {
jobline.jobsublines = jobsubline.data;
deferred.resolve( jobline );
} );
return deferred.promise;
} );
$q.all( promises ).then( function ( joblines ) {
job.joblines = joblines
vm.job = job;
} );
}
I'm having difficulty with NgTable, however the functionality I'm looking for may be a limitation on the table framework.
I'm using an API call within the getData, and the data is being grouped (via the groupBy property in the settings param).
I want to be able to use a global filter on the data, I can't seem to get it work with grouping. There's two examples, except they don't mix:
Grouping: http://ng-table.com/#/grouping/demo-grouping-basic
Global filtering: http://ng-table.com/#/filtering/demo-api
Any suggestions?
Table declaration/config
$scope.tableNotesParams = new ngTableParams({
page: 1, // show first page
count: 10, // count per page: use total result set in this case,
sorting: {
created_at: 'desc'
}
}, {
groupBy: function( note ) {
return moment( note.created_at ).format( 'YYYY' );
},
getData: function ( $defer, params ) {
$scope.request.notes.state = 'started';
$scope.request.notes.notesSpinner = true;
var offset = params.count() * ( params.page() - 1 );
// Default
var urlQueryParams = {
'email': member.accounts.email,
'offset': offset,
'limit': params.count() || 10
};
notesApiService.getNotes( urlQueryParams ).then( function ( results ) {
$scope.notes = results.data;
$scope.noteMembers = extractionService.getAllUniqueMembers( $scope.notes );
// Get the range values, expecting value to be: items 1-10/655
var noteHeaders = results.headers();
var notesRangeValues = noteHeaders['content-range'].match( /(\d{1,})/g );
$scope.tableNotesMetaData = {
offsetStart: notesRangeValues[0] || 0,
offsetEnd : notesRangeValues[1] || 0,
totalCount : notesRangeValues[2] || 0
};
// Update parent controller count
$scope.tabs.notes.count = notesRangeValues[2] || 0;
// Update the total
params.total( $scope.tableNotesMetaData.totalCount );
var orderedData = params.sorting() ?
$filter('orderBy')($scope.notes, params.orderBy()) :
$scope.notes;
$defer.resolve( orderedData );
$scope.request.notes.state = 'completed';
$scope.request.notes.notesSpinner = false;
});
}
});
Edit:
The filtering example for a global filter doesn't do anything to the grouped data:
function applyGlobalSearch(){
var term = self.globalSearchTerm;
if (self.isInvertedSearch){
term = "!" + term;
}
self.tableParams.filter({ $: term });
}
I don't think it's performant to query your notesApiService.getNotes() in the getData()-function, but whatever. Since we don't have the HTML or a JSBin to work with, it's mostly guestimate:
notesApiService.getNotes( urlQueryParams ).then( function ( results ) {
var term = $scope.globalSearchTerm.toLowerCase();
if (term.length == 0) {
$scope.notes = angular.copy(results.data, []);
} else if (term.length > 1) {
$scope.notes = results.data.filter(function(item) {
var val = JSON.stringify(item).toLowerCase();
return (val.indexOf(term) != -1);
});
}
I have different sections in Firebase with normalized data, and I have routines to get the information, but I cannot loop through the returned records to get data. I want to use the keys in the $firebaseArray() to get data from other $firebaseObject().
GetOneTeam() .... {
var DataRef = GetFireBaseObject.DataURL(Node + '/'); // xxx.firebaseio.com/Schedules/
var OneRecordRef = DataRef.child(Key); // Schedule Key - 1
return $firebaseObject(OneRecordRef);
}
...
var Sched = GetOneSchedule('Schedules', 1);
... // For Loop getting data - Put in HomeId
var TeamRec = GetOneTeam('Teams', HomeId);
var Name = TeamRec.TeamName; // Does not TeamName value from Schedule/1
The following is more of the actual code in case the snippet above is not clear enough. Sample common routine for getting data:
angular.module('MyApp')
.constant('FIREBASE_URL', 'https://xxxxxxxx.firebaseio.com/');
angular.module('MyApp')
.factory('GetFireBaseObject', function(FIREBASE_URL) {
return {
BaseURL: function() {
return new Firebase(FIREBASE_URL);
},
DataURL: function(Node) {
return new Firebase(FIREBASE_URL + Node);
}
};
}
);
// Common code for getting Array/Object from Firebase.
angular.module('MyApp')
.factory("FireBaseData", ["$firebaseArray", "$firebaseObject", "GetFireBaseObject",
function($firebaseArray, $firebaseObject, GetFireBaseObject) {
return {
AllRecords: function(Node) {
var DataRef = GetFireBaseObject.DataURL(Node + '/');
return $firebaseArray(DataRef);
},
OneRecordAllChildren: function(Node, Key) {
var DataRef = GetFireBaseObject.DataURL(Node + '/');
var ParentRecordRef = DataRef.child(Key);
return $firebaseArray(ParentRecordRef);
},
OneRecord: function(Node, Key) {
var DataRef = GetFireBaseObject.DataURL(Node + '/');
var OneRecordRef = DataRef.child(Key);
return $firebaseObject(OneRecordRef);
},
AddRecord: function(Node, Record) {
var DataRef = GetFireBaseObject.DataURL(Node + '/');
var AddRecordRef = DataRef.child(Record.Key);
AddRecordRef.update(Record);
return $firebaseObject(AddRecordRef); // Return Reference to added Record
},
DeleteRecord: function(Node, Key) {
var DataRef = GetFireBaseObject.DataURL(Node + '/');
var DeleteRecordRef = DataRef.child(Key);
DeleteRecordRef.remove();
}
};
}
]);
Individual Controller's retrieval of records from firebase.io:
angular.module('MyApp').service("ScheduleData", ["FireBaseData",
function(FireBaseData) {
var DataPath = 'Schedules';
this.AllSchedules = function() {
return FireBaseData.AllRecords(DataPath);
};
this.AddSchedule = function(GameInfo) {
return FireBaseData.AddRecord(DataPath, GameInfo);
};
this.DeleteSchedule = function(GameKey) {
FireBaseData.DeleteRecord(DataPath, GameKey);
};
this.GetOneSchedule = function(GameKey) {
return FireBaseData.OneRecord(DataPath, GameKey);
};
}
]);
// Structure of a record, including named fields to come from another object (Team/Venue using the OneRecord FireBaseData call to get a $firebaseObject
angular.module('MyApp').factory("ScheduleRecord", function() {
return {
Clear: function(GameInfo) {
GameInfo.Key = "";
GameInfo.HomeTeamId = "";
GameInfo.HomeTeamName = "";
GameInfo.AwayTeamId = "";
GameInfo.AwayTeamName = "";
GameInfo.VenueId = "";
GameInfo.VenueName = "";
GameInfo.GameDate = "";
GameInfo.GameTime = "";
}
};
}
);
Controller module start:
angular.module('MyApp').controller('ScheduleCtrl', ["$scope", "ScheduleData", "ScheduleRecord", "TeamData", "VenueData",
function ($scope, ScheduleData, ScheduleRecord, TeamData, VenueData) {
var ClearEditData = function() {
$scope.ScheduleEditMode = false;
ScheduleRecord.Clear($scope.schedule);
};
var GameSchedules = ScheduleData.AllSchedules();
This next piece is where my question lies. Once the promise returns the static schedule list, I want to loop through each record and translate the Team Id (Home/Away) and Venue Id to the names.
GameSchedules.$loaded().then(function() {
angular.forEach(GameSchedules, function(GameInfo) {
var HomeTeam = TeamData.GetOneTeam(GameInfo.HomeTeamId);
GameInfo.HomeTeamName = HomeTeam.Name;
The GetOneTeam returns a $firebaseObject, based on the HomeTeamId child record. This returns null all the time.
This is the TeamData.GetOneTeam return using the FireBaseData as well.
angular.module('MyApp').service("TeamData", ["FireBaseData",
function(FireBaseData) {
var DataPath = 'Teams';
this.AllTeams = function() {
return FireBaseData.AllRecords(DataPath);
};
this.AddTeam = function(TeamInfo) {
return FireBaseData.AddRecord(DataPath, TeamInfo);
};
this.DeleteTeam = function(TeamKey) {
FireBaseData.DeleteRecord(DataPath, TeamKey);
};
this.GetOneTeam = function(TeamKey) {
return FireBaseData.OneRecord(DataPath, TeamKey);
};
}
]);
As I have a Firebase Object, how can I get my named data objects from the $firebaseObject?
This is a mess. Use $firebaseArray for collections, not $firebaseObject. Most of these strange wrapper factories are unnecessary. AngularFire services already have methods for add, remove, and so on, and all these factories attempt to make AngularFire into a CRUD model and don't actually provide any additional functionality or enhancements.
app.factory('Ref', function(FIREBASE_URL) {
return new Firebase(FIREBASE_URL);
});
app.factory('Schedules', function($firebaseArray, Ref) {
return $firebaseArray(Ref.child('Schedules'));
});
// or if you want to pass in the path to the data...
//app.factory('Schedules', function($firebaseArray, Ref) {
// return function(pathToData) {
// return $firebaseArray(Ref.child(pathToData));
// };
//});
app.factory('Schedule', function($firebaseObject, Ref) {
return function(scheduleId) {
return $firebaseObject(Ref.child('Schedules').child(scheduleId));
}
});
app.controller('...', function(Schedules, Schedule, Ref) {
$scope.newSchedule(data) {
Schedules.$add(data);
};
$scope.removeSchedule(key) {
Schedules.$remove(key);
};
$scope.updateSchedule(key, newWidgetValue) {
var rec = Schedules.$getRecord(key);
rec.widgetValue = newWidgetValue;
Schedules.$save(rec);
};
// get one schedule
var sched = Schedule(key);
sched.$loaded(function() {
sched.widgetValue = 123;
sched.$save();
});
});
I had in my old project this bit of code for an API:
.factory('Api', ['$resource', 'apiUrl', function ($resource, api) {
var Api = $resource(api + ':path', {
path: '#path'
});
return Api;
}])
and then I had an Order model which extended this factory class like this:
.factory('Order', ['$filter', 'Api', function ($filter, api) {
var Order = api;
angular.extend(Order.prototype, {
getDescription: function () {
var rolls = 0,
cuts = 0,
skus = [],
lines = $filter('orderBy')(this.lines, 'sku');
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
switch (line.type) {
case 0: // cut
cuts++;
break;
case 1: // roll
rolls++
break;
}
if (skus.indexOf(line.sku) == -1) {
skus.push(line.sku);
}
}
var description = '';
description += cuts > 0 ? cuts > 1 ? cuts + ' x cuts' : cuts + ' x cut' : '';
description += rolls > 0 && description.length > 0 ? ', ' : '';
description += rolls > 0 ? rolls > 1 ? rolls + ' x rolls' : rolls + ' x roll' : '';
description += skus.length == 1 ? ' of ' + skus[0] : '';
return description;
},
getStatus: function () {
var lines = this.lines,
status = lines[0].status;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (status !== line.status)
return 'Multiple';
}
return status;
},
getDeliveryDate: function () {
var lines = this.lines,
date = lines[0].dates.delivery;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (date !== line.dates.delivery)
return 'Multiple';
}
date = new Date(date);
return date;
},
getDispatchDate: function () {
var lines = this.lines,
date = lines[0].orderDate;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
if (date !== lines.orderDate)
return 'Multiple';
}
date = new Date(date);
return date;
}
});
return Order;
}]);
Now, I have recently changed my API factory to this:
// ---
// CONSTANTS.
// ---
.constant('apiUrl', 'http://localhost:54326/')
//.constant('apiUrl', 'http://localhost:81/')
// ---
// SERVICES.
// ---
.service('Api', ['$http', 'HttpHandler', 'apiUrl', function ($http, handler, apiUrl) {
// Private function to build our request
var buildRequest = function (url, method, data, params) {
var model = {
method: method,
url: apiUrl + url,
data: data,
params: params
};
return $http(model);
}
// GET
this.get = function (url, params) {
return handler.loadData(buildRequest(url, 'GET', null, params));
}
// POST
this.post = function (url, data) {
return handler.loadData(buildRequest(url, 'POST', data));
}
// PUT
this.put = function (url, data) {
return handler.loadData(buildRequest(url, 'PUT', data));
}
// DELETE
this.delete = function (url, data) {
return handler.loadData(buildRequest(url, 'DELETE', data));
}
}])
When I did this, my order model no longer works. I get an error stating:
Cannot read property '$$hashKey' of undefined
Is there a way I can get my Order model to use the new API factory? Specifically I want to attach functions each object returned by the API.
I have a form with firstname, lastName, zipcode..etc, in addition to multi value input, in which the user check many checkboxes (which will be an array).
I wanna submit this form using angular js $http, and pass them as a query string, currently I have it like this:
firstName=test&lastName=test&homeZip=44551&carInterested=honda%2Cford%2Ctoyota
but my requirement is to be like this:
firstName=test&lastName=test&homeZip=44551&carInterested=honda&carInterested=ford&carInterested=toyota
here is my code:
$scope.signUp = function() {
$scope.formData.carInterested= $scope.selection
console.log ($scope.formData);
$http({
method : 'POST',
url : '/sonmeUr/../',
transformRequest: transformRequestAsFormPost,
data : $scope.formData
})
.success(function(data) {
console.log(data);
});
}
and in my service I have this:
.factory( "transformRequestAsFormPost", function() {
function transformRequest( data, getHeaders ) {
var headers = getHeaders();
headers[ "Content-type" ] = "application/x-www-form-urlencoded; charset=utf-8";
return( serializeData( data ) );
}
return( transformRequest );
function serializeData( data ) {
if ( ! angular.isObject( data ) ) {
return( ( data == null ) ? "" : data.toString() );
}
var buffer = [];
for ( var model in data ) {
if ( ! data.hasOwnProperty( model ) ) {
continue;
}
var value = data[ model ];
buffer.push(
encodeURIComponent( model ) +
"=" +
encodeURIComponent( ( value == null ) ? "" : value )
);
}
var source = buffer
.join( "&" )
.replace( /%20/g, "+" )
;
return( source );
}
}
);