Accessing promise's elements which are objects and not simple types - angularjs

I have a study, which has multiple cases and multiple executionMessages. Also each case has multiple executionSteps. I am able to access the study, the case and even each case's executionSteps. I cannot figure out why I cannot access the complete executionMessages. By that I meant each executionMessage has a type, message which are accessible but any objects inside executionMessage is not accessing. Here it the code
StudyService.studies.get({id: $routeParams.studyIdentifier}).$promise.then(function(study) {
$scope.study = study;
StudyService.executionMessagesForStudy.get({id: study.id}).$promise.then(function(executionMessages){
$scope.study.executionMessages = executionMessages;
});
for(var i=0;i<study.cases.length;i++){
var callback = callbackCreator(i);
StudyService.executionstepsForCase.get({id: $routeParams.studyIdentifier,caseId:study.cases[i].id})
.$promise.then(callback);
}
});
function callbackCreator(i) {
return function(executionSteps) {
$scope.study.cases[i].executionSteps = executionSteps;
}
}

It looks like you are using $resource for your service types (a guess at the .get().$promise pattern you are using), From the docs:
...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. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view.
see: https://docs.angularjs.org/api/ngResource/service/$resource
So your code can be simplified like this:
// get() returns an object that will "fill-in" with the attributes when promise is resolved
$scope.study = StudyService.studies.get({id: $routeParams.studyIdentifier})
$scope.study.$promise.then(function(study) {
// again, executionMessages gets a placeholder that will be filled in on the resolved promise
$scope.study.executionMessages = StudyService.executionMessagesForStudy.get({id: study.id});
angular.forEach(study.cases, function(case, idx) {
// again, executionSteps is a placeholder that will be filled in on the resolved promise
var executionSteps = StudyService.executionstepsForCase.get({id: $routeParams.studyIdentifier, caseId: case.id})
// not sure if ordering is important, the service calls can return in any order
// so using push can produce list with elements in any order.
$scope.study.cases.push(executionSteps);
});
});
You still need to use the study.$promise to get the nested data after the first service call resolves.

Related

after angular $save and push to array item is not added unless I re-run a query

I have a single page app built using the MEAN stack. I am trying to get a list of items to update after a new item is added in angular but it's not working.
$scope.storages = Storages.query();
This works fine and returns my array of storages that I display in the view.
I then have a field to add a new array... to simplify the code it looks like this:
// create a new storage object from $scope.newStorage
var storage = new Storages($scope.newStorage);
I then do various things to the data and finally...
storage.$save(function(){
$scope.storages.push(storage); // doesn't seem to do anything
console.log("new storage", storage); //shows up fine
$scope.newStorage = []; // clear textbox
$scope.showHide.addItemPanel = false; // hides the newStorage form
return $scope.storages;
});
It seems like $scope.storages.push(storage) is not doing anything.
If I use this code at the end:
$scope.storages = Storages.query();
return $scope.storages;
Then it works. But I don't want to have to keep getting all the data from the server each time. How can I solve this and why isn't this working?
If I console.log(JSON.stringify($scope.storages)) it looks like this:
[{"_id":"XXXXXXXXXXXXXXX","_title":"XXXXXXXXX","__v":0,"files":[],"comments":[],"fields":[{"0":{"Title":"XXXXXXXXXXXXX"},"1":{"Category":"coding"},"2":{"Details":"XXXXXXXXX
Thanks in advance!
Read the comments below
storage.$save(function(){
//storage is promise here, not a Storage object you expect
$scope.storages.push(storage);
//because console.log binds to reference
//so after promise resolves, value on reference is changed
console.log("new storage", storage); //shows up fine
//not required at all
return $scope.storages;
});
Simple solution
//bind this to $scope
var storage = new Storages($scope.newStorage);
//like this
$scope.storage = new Storages($scope.newStorage);
//so Resource.$save is promise, when it completes, it will trigger digest
//and your array will be populated
$scope.storage.$save();
$scope.newStorage = [];
$scope.showHide.addItemPanel = false;
$scope.storages.push($scope.storage);
Better solution
//$save accepts callback
storage.$save(function(newStorageFromServer){
//newStorageFromServer is not promise, but actual result from server
//which is Storage instance by default
$scope.storages.push(newStorageFromServer);
//other staff
$scope.newStorage = []; // clear textbox
$scope.showHide.addItemPanel = false; // hides the newStorage form
//return statement is useless
});

angular - fail to update array from service

I got a service that contain some contacts (name,phone). The controller has array that contain a reference to the array from the service so for every change on the array all gets updated.
Service:
app.service('ContactManagerService', function()
{
this.Contacts=[];
...
this.AddContact=function(Contact){...};
this.RemoveContact=function(Contact){...};
...
});
First question: Is this a good approach? Should every controller/directive need to have a direct reference to the original array from the service? I have read a lot about setting up some events from the service to the controllers when the array has been changed, but it sound stupid because the array on the controller will be change anyway (because its the same array) and the ng-repeat will be updated automatically.
Second problem: The service has a method that replace the array to new one:
this.ReplaceContacts=function(NewContacts)
{
this.Contacts=NewContacts;
});
The ng-repeat does not update because the controller still got the old reference to the old array. So a refresh need to be done.
I tried to replace the code to this one so the same array's reference wont change, but when the the code enter the foreach, this.Contacts array is undefined and the code stops. Why ?!
this.ReplaceContacts=function(NewContacts)
{
this.Contacts.splice(0, this.Contacts.length); //remove all contacts
NewContacts.forEach(function (contact, index)
{
this.Contacts.push(contact);//place the new ones
});
});
The controller code:
app.controller("myCtrl",
function ($scope,ContactManagerService)
{
$scope.Contacts = ContactManagerService.Contacts;
$scope.AddContact= function (Contact1) {
ContactManagerService.AddContact(Contact1);
}
$scope.RemoveContact = function (ContactID) {
ContactManagerService.RemoveContact(ContactID);
}
});
I hope everything is clear,
Thanks.
Because the callback function passed to forEach isn't bound to the service instance. So this, inside the callback, is not the service.
My advice: avoid this like the plague. It's an enormous source of bugs in JavaScript.
Use
var self = this;
at the top of the service, and use self instead of this everywhere.
Or bind the callback function to the service instance:
NewContacts.forEach(function (contact, index) {
...
}, this);
You can simply push elements to Contacts using Array.prototype.push()
The push() method adds one or more elements to the end of an array and returns the new length of the array.
this.ReplaceContacts=function(NewContacts){
this.Contacts.splice(0, this.Contacts.length); //remove all contacts
Array.prototype.push(this.Contacts, NewContacts);
});
As mentioned in previous anser, context of this in forEach loop is not what you think it is.
A simplification would be to use Array.prototype.concat():
var self = this;
self.ReplaceContacts = function (NewContacts) {
self.Contacts.splice(0, this.Contacts.length); //remove all contacts
self.Contacts.concat(NewContacts);
});

Cannot set priority on $asObject()'s child keys using Angularfire 0.8

According to the Angularfire docs, when working with an object returned through $asObject(), you can set priority for said object by defining a $priority property on the object and then using $save().
My code works great, but $priority isn't doing anything. Here's some code with complete explanations in the comments:
app.factory('MyService', function($rootScope, $firebase) {
// The complete Firebase url
var ref = *obfuscated*;
// Returning the dataset as an object containing objects
var data = $firebase(ref).$asObject;
// This object is what's returned by MyService
var Data = {
// Method to create a new object within the data set,
// keyed by myId. Attempting to set priority for the
// record via $priority. returnData.uid is a valid string.
create: function(returnData, myId) {
data[myId] = {
myId: myId,
$priority: returnData.uid
};
// No need to explain the stuff between here and the
// $rootScope listener below, just added for context
data.$save().then(function() {
setMyId(myId);
});
},
findByMyId: function(myId) {
if (myId) {
return data[myId];
}
}
};
function setMyId(myId) {
$rootScope.myId = User.findByMyId(myId);
}
// This event listener works fine, fires
// at user login and returns data
$rootScope.$on('$firebaseSimpleLogin:login', function(e, returnData) {
// returnData.uid has the correct value - this
// function should return the object(aka record) with
// a priority matching returnData.uid
var query = $firebase(ref.startAt(returnData.uid).endAt(returnData.uid)).$asObject();
// console shows an object with the normal $firebase
// properties, but no records. If I define query without
// limiting the set ($firebase(ref).$asObject()), it returns
// the full set just fine. The priority on all records is still
// null.
console.log(query);
query.$loaded(function() {
setData(query.myId);
});
});
return Data;
});
Yes, I'm following Thinkster.io's tutorial and I'm in Chapter 7. No, this is not a duplicate of the other questions about that chapter, I already found my way around the pre-Angularfire 0.8 code present in their examples, just can't set $priority, and I've spent about 5 hours so far trying to find a solution through my own efforts and on the web.
Any takers?
When viewed in the light of how JavaScript works with objects (i.e. unordered), how JSON handles objects (i.e. unordered), and in light of the expectation that AngularFire's $asObject() method is intended for storing key/value pairs, and singular records that are not used as a collection, this starts to make some sense.
Internally, the synchronize'd object's $save method calls Firebase's setWithPriority. In set or setWithPriority calls, the child nodes are replaced. Any meta data like priorities on those children are replaced.
In AngularFire, $asArray is intended to handle ordered collections, and provides the ability to set $priority on child nodes (only one level deep, of course, as it treats its children as singular records that are not themselves collections).
Since, in your case, you want to work with fixed keys rather than push ids, you'll probably want to override the $add method using $extendFactory and do something like the following:
angular.module(MY_APP).factory('FixedKeysFactory', function($FirebaseArray, $firebaseUtils) {
return $FirebaseArray.$extendFactory({
$add: function(data) {
this._assertNotDestroyed('$add');
if( angular.isObject(data) && typeof data.$id === 'string' ) {
return this.$inst().$set(data.$id, $firebaseUtils.toJSON(data));
}
else {
return this.$inst().$push($firebaseUtils.toJSON(data));
}
}
});
});
You could then pass this into your $firebase instance in place of the default factory:
var list = $firebase(ref, {arrayFactory: FixedKeysFactory}).$asArray();
A simpler but less awesomatic™ solution would be to manually add your objects to the array, manually giving them a $id, then call $save:
var list = $firebase(ref).$asArray();
var i = list.length;
list.push({ foo: 'bar', $id: 'kato' });
list.$save(i);
Some notes on the future: It will soon be possible to use any field as sort criteria and there will be no need to set priorities (yay!). It will probably be possible to set your own $id before calling $add on a synchronized array in AngularFire as soon as I clear that with the other devs (like the 0.8.3 release).

restangular filter object to array

I have this big resource object that has lots of methods inside of it with more objects, etc. I'm also converting xml into json for this.
One in particular is a resourceType object that sometimes has more than one resourceTypes. When 2 or more are present, it's an Array. When it's 1 it's just an object and I can't loop through it consistantly in my view.
I made a filter that checks if it's just an object then casts it to an array if it's not already one i'm just having trouble how to put it in my service call. Right now I have it has:
getResources: function(resourceId){
var self = this;
return Restangular.one('resource/resourceState', resourceId).get().then(function(response){
$filter('castToArray')(response.resources.resourceState.resourceTypes)
self.resources = response;
return self.resources;
});
},
This doesn't work. I want to maintain the integrity of the entire resources object but I want the resourceTypes method/object to cast to an array if only 1 is present.
In case anyone stumbles on this the fix was quite simple.
I just set the above to:
getResources: function(resourceId){
var self = this;
return Restangular.one('resource/resourceState', resourceId).get().then(function(response){
self.resources = response;
self.resources.resourceState.resourceTypes = $filter('castToArray')(response.resourceState.resourceTypes);
return self.resources;
});
},
I just needed to set the the self.resources method to the response method with the filter casted on it. Okay I'm done.

How to extend an object with angular.extend?

I'm trying to extend an object in angularJs using multiple ajax calls results. However only the last results extends my empty object.
$rootScope.progress = {}; // the objkect to extend
$http.get('data.json').
then(function(result){angular.extend($rootScope.progress,result.data)}),
$http.get('error.json').
then(function(result){angular.extend($rootScope.progress,result.data)}),
$http.get('data2.json').
then(function(result){angular.extend($rootScope.progress,result.data)}),
$http.get('data3.json').
then(function(result) {angular.extend($rootScope.progress,result.data)})
You could see a plunker here:http://plnkr.co/edit/iDmsTpDpFUnvrv1pCUU9?p=preview
It is because all your data return is array instead of object.
Changed all the data.json to object.
Refer to http://plnkr.co/edit/Gq22dsksxlPYCryM1BOs?p=preview
If your json always return array, then your $scope.result should changed to array.
you can use $scope.result = $scope.result.concat(value.data);

Resources