Angular q all dynamic results - javascript

I need to wait for two promises to resolve before doing something.The problem is that sometimes I can have only one promise to send based on what the client want, and I need to count on the results that comes from this $q.all. How can I dynamically define the vars?
var makePurchaseToResolve = [];
if( CartService.items.draws.length ) {
var drawOrder = {};
drawOrder.price = $scope.getTotal();
drawOrder.paymentMethodId = $scope.payment.paymetMethodId;
drawOrder.orderItems = CartService.items.draws;
drawOrder.payFromBalance = false;
makePurchaseToResolve.push(DrawService.makePurchase(drawOrder));
}
if( CartService.items.hunters.length ) {
var hunterOrder = {};
hunterOrder.paymentMethodId = $scope.payment.paymetMethodId;
hunterOrder.orderItems = CartService.items.hunters;
makePurchaseToResolve.push(HunterService.makePurchase(hunterOrder));
}
$q.all(makePurchaseToResolve).then(function( res ) {
$q.all([HunterService.getPurchase(res[0].data.data),DrawService.getPurchase(res.data.data)]).then(function() {
//here is the problem, because i dont know if i have the two promises to resolve or only one
$scope.hunters = res[0].data.data[0];
$scope.drawOrder = res[1].data.data;
})
}

A little known fact is that $q.all can also take an object rather than an array which can simplify your life in this case:
var purchases = {}; // an object rather than an array for named properties
if( CartService.items.draws.length ) {
//...
purchases.drawOrder = DrawService.makePurchase(drawOrder); // named assign
}
if( CartService.items.hunters.length ) {
//...
purchases.hunters = HunterService.makePurchase(hunterOrder);
}
// any more additions to the objects here..
$q.all(purchases).then(function(resolved){
// resolve anything further you need
$scope.purchases = resolved; // Add $scope.purchases.hunters etc
});
The promises are resolved in an object so they're named, instead of having an array you get a resolved object with properties so instead of doing arr[0] which might be out of order you'd do resolved.hunters or resolved.drawOrder. This solution just nests into $scope one level and does it automatically.

You can also keep the order of the promises, if you'd like, and pass in null for those that were not applied based on your condition.
This is an alternative for when your service gets a dynamic number of purchase requests from the caller and naming is not an option.
$q.all([null, huntersOrderPromise])
.then(function(data){
$scope.drawOrder = data[0]; // will be null
$scope.hunterOrder = data[1];
});

Related

Determine if observable has changed in computed

I am implementing a cache function in a computed observable.
Is there any way to invalidate the cache below if the items collection differs since the last call?
I have seen examples of dirty checking where a serialized version of the observable is used to determine if the collection has changed, but it's too expensive for me, since there may be hundreds of items.
var itemCache;
var manipulatedItems = ko.pureComputed(function(){
var items = someObervable();
if(!itemCache /* || someObervable.hasChangedSinceLastCall */) {
itemCache = heavyWork(items);
}
return itemCache;
});
var heavyWork = function(items){
// do some heavy computing with items
return alteredItems;
};
In my viewmodel:
myViewModel.itemList = ko.pureComputed(function(){
var result = manipulatedItems();
return result;
});
Since computed observables always cache the last value, there's no reason to store it separately. In fact, storing it separately can cause trouble with getting the latest data in your application.
var manipulatedItems = ko.pureComputed(function () {
var items = someObervable();
return heavyWork(items);
});
var heavyWork = function (items) {
// do some heavy computing with items
return alteredItems;
};
Yes, but you need to use .subscribe and keep the relevant moments in vars inside your own closure. There is no "last-modified-moment" property on observables or inside ko utils to be found.
In your repro, you could do something like this:
var lastChange = new Date();
var lastCacheRebuild = null;
var itemCache;
someObervable.subscribe(function(newObsValue) { lastChange = new Date(); });
var manipulatedItems = ko.pureComputed(function(){
var items = someObervable();
if(!itemCache || !lastCacheRebuild || lastChange > lastCacheRebuild) {
lastCacheRebuild = new Date();
itemCache = heavyWork(items);
}
return itemCache;
});
As far as the repro is concerned you could even put the items = someObservable() bit inside the if block.
PS. This is not recursive, i.e. the subscription is only on someObservable itself, not on the observable properties of things inside that observable. You'd have to manually craft that, which is specific to the structure of someObservable.

Difference between array of objects in Angularjs

I'm using Angular. I'm trying to compare two arrays of objects
I was able to get it working doing it like this:
var compareUsers = function () {
//Comparing assigned groups with all to return available users
var assignedUsersIds = {};
var usersIds = {};
availableUsers = []; //declared higher up, populated with user objects on load
//assignedUsers, declaired higher up, populated by function calling compareUsers
assignedUsers.forEach(function (el, i) {
assignedUsersIds[el.id] = assignedUsers[i];
});
allUsers.forEach(function (el, i) {
usersIds[el.id] = allUsers[i];
});
for (var i in usersIds) {
if (!assignedUsersIds.hasOwnProperty(i)) {
availableUsers.push(usersIds[i]);
}
};
console.log(availableUsers);
return availableUsers;
}
I found a better way to do it so I refactored it to this, using lodash:
var compareUsers = function () {
availableUsers = _.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
});
console.info(availableUsers);
return availableUsers;
}
However, I'm not getting the correct results and not sure what I messed up. The new methods returns availableUsers which are in the assignedUsers list for some groups. The first time it runs, it seems to work but if I keep changing what group i'm looking at the results are all off and don't add up.
I found this method here.
In the first example you are using Array.push, which is always adding up (obviously), no matter how often you call compareUsers.
In the second example you are overwriting availableUsers each time you are calling compareUsers with the result of _.filter
I'd suggest that instead of doing:
availableUsers = _.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
});
you do:
availableUsers = availableUsers.concat(_.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
}));
This should work. It will concat the availableUsers array with the result of _.filter.

Intern: Chaining operations not returning promise

Using Intern I have to get an hidden json object from the page and then build a dictionary. After this, querying this dictionary I should perform an other action on the DOM.
The problem is that I do not know how to bind these 2 things, because I want the second operation is executed after the first one is completed.
My code is something like:
var self._formMap = null;
if(self._formMap === null || Object.keys(self._formMap).length === 0) {
return remote.findByXpath(selector)
.getAttribute('value')
.then(function(value) {
var jsonValue = JSON.parse(value);
var formMap = {};
for (var item in jsonValue) {
if (jsonValue.hasOwnProperty(item)) {
var key = jsonValue[item][0].split(/[\/]+/).pop();
formMap[key] = item;
}
}
return formMap;
}).then(function (map) {
self._formMap = map;
return _super_.setInputInForm.call(this, [..., formMap, ..]); // function in another file, but that shares the same remote object.
});
}
In the second step, when I call the setInputInForm, it's like the remote is undefined. Is it maybe because I'm returning the formMap in the first step? Could be a problem of promise?
Furthermore, I would like to isolate the first steps, and put it into a function, always returning a promise.
Thanks.

How can I make Ember.js handlebars #each iterate over objects?

I'm trying to make the {{#each}} helper to iterate over an object, like in vanilla handlebars. Unfortunately if I use #each on an object, Ember.js version gives me this error:
Assertion failed: The value that #each loops over must be an Array. You passed [object Object]
I wrote this helper in attempt to remedy this:
Ember.Handlebars.helper('every', function (context, options) {
var oArray = [];
for (var k in context) {
oArray.push({
key : k,
value : context[k]
})
}
return Ember.Handlebars.helpers.each(oArray, options);
});
Now, when I attempt to use {{#every}}, I get the following error:
Assertion failed: registerBoundHelper-generated helpers do not support use with Handlebars blocks.
This seems like a basic feature, and I know I'm probably missing something obvious. Can anyone help?
Edit:
Here's a fiddle: http://jsfiddle.net/CbV8X/
Use {{each-in}} helper. You can use it like like {{each}} helper.
Example:
{{#each-in modelWhichIsObject as |key value|}}
`{{key}}`:`{{value}}`
{{/each-in}}
JS Bin demo.
After fiddling with it for a few hours, I came up with this hacky way:
Ember.Handlebars.registerHelper('every', function(context, options) {
var oArray = [], actualData = this.get(context);
for (var k in actualData) {
oArray.push({
key: k,
value: actualData[k]
})
}
this.set(context, oArray);
return Ember.Handlebars.helpers.each.apply(this,
Array.prototype.slice.call(arguments));
});
I don't know what repercussions this.set has, but this seems to work!
Here's a fiddle: http://jsfiddle.net/CbV8X/1/
I've been after similar functionality, and since we're sharing our hacky ways, here's my fiddle for the impatient: http://jsfiddle.net/L6axcob8/1/
This fiddle is based on the one provided by #lxe, with updates by #Kingpin2k, and then myself.
Ember: 1.9.1, Handlebars: 2.0.0, jQuery 2.1.3
Here we are adding a helper called every which can iterate over objects and arrays.
For example this model:
model: function() {
return {
properties: {
foo: 'bar',
zoo: 'zar'
}
};
}
can be iterated with the following handlebars template:
<ul class="properties">
{{#every p in properties}}
<li>{{p.key}} : {{p.value}}</li>
{{/every}}
</ul>
every helper works by creating an array from the objects keys, and then coordinating changes to Ember by way of an ArrayController. Yeah, hacky. This does however, let us add/remove properties to/from an object provided that object supports observation of the [] property.
In my use case I have an Ember.Object derived class which notifies [] when properties are added/removed. I'd recommend looking at Ember.Set for this functionality, although I see that Set been recently deprecated. As this is slightly out of this questions scope I'll leave it as an exercise for the reader. Here's a tip: setUnknownProperty
To be notified of property changes we wrap non-object values in what I've called a DataValueObserver which sets up (currently one way) bindings. These bindings provide a bridge between the values held by our internal ArrayController and the object we are observing.
When dealing with objects; we wrap those in ObjectProxy's so that we can introduce a 'key' member without the need to modify the object itself. Why yes, this does imply that you could use #every recursively. Another exercise for the reader ;-)
I'd recommend having your model be based around Ember.Object to be consistent with the rest of Ember, allowing you to manipulate your model via its get & set handlers. Alternatively, as demonstrated in the fiddle, you can use Em.Get/Em.set to access models, as long as you are consistent in doing so. If you touch your model directly (no get/set), then every won't be notified of your change.
Em.set(model.properties, 'foo', 'asdfsdf');
For completeness here's my every helper:
var DataValueObserver = Ember.Object.extend({
init: function() {
this._super();
// one way binding (for now)
Em.addObserver(this.parent, this.key, this, 'valueChanged');
},
value: function() {
return Em.get(this.parent, this.key);
}.property(),
valueChanged: function() {
this.notifyPropertyChange('value');
}
});
Handlebars.registerHelper("every", function() {
var args = [].slice.call(arguments);
var options = args.pop();
var context = (options.contexts && options.contexts[0]) || this;
Ember.assert("Must be in the form #every foo in bar ", 3 == args.length && args[1] === "in");
options.hash.keyword = args[0];
var property = args[2];
// if we're dealing with an array we can just forward onto the collection helper directly
var p = this.get(property);
if (Ember.Array.detect(p)) {
options.hash.dataSource = p;
return Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
}
// create an array that we will manage with content
var array = Em.ArrayController.create();
options.hash.dataSource = array;
Ember.Handlebars.helpers.collection.call(this, Ember.Handlebars.EachView, options);
//
var update_array = function(result) {
if (!result) {
array.clear();
return;
}
// check for proxy object
var result = (result.isProxy && result.content) ? result.content : result;
var items = result;
var keys = Ember.keys(items).sort();
// iterate through sorted array, inserting & removing any mismatches
var i = 0;
for ( ; i < keys.length; ++i) {
var key = keys[i];
var value = items[key];
while (true) {
var old_obj = array.objectAt(i);
if (old_obj) {
Ember.assert("Assume that all objects in our array have a key", undefined !== old_obj.key);
var c = key.localeCompare(old_obj.key);
if (0 === c) break; // already exists
if (c < 0) {
array.removeAt(i); // remove as no longer exists
continue;
}
}
// insert
if (typeof value === 'object') {
// wrap object so we can give it a key
value = Ember.ObjectProxy.create({
content: value,
isProxy: true,
key: key
});
array.insertAt(i, value);
} else {
// wrap raw value so we can give it a key and observe when it changes
value = DataValueObserver.create({
parent: result,
key: key,
});
array.insertAt(i, value);
}
break;
}
}
// remove any trailing items
while (array.objectAt(i)) array.removeAt(i);
};
var should_display = function() {
return true;
};
// use bind helper to call update_array if the contents of property changes
var child_properties = ["[]"];
var preserve_context = true;
return Ember.Handlebars.bind.call(context, property, options, preserve_context, should_display, update_array, child_properties);
});
Inspired by:
How can I make Ember.js handlebars #each iterate over objects?
http://mozmonkey.com/2014/03/ember-getting-the-index-in-each-loops/
https://github.com/emberjs/ember.js/issues/4365
https://gist.github.com/strathmeyer/1371586
Here's that fiddle again if you missed it:
http://jsfiddle.net/L6axcob8/1/

Create array of objects Javascript

I created this Object with 3 properties:
Node = {
name : "",
isOkay : true,
rotation : 0.0
};
How would i go creating an array of these objects, in size of 100.
So later i could do something like this:
nodeList[74].name = "Peter";
nodeList[74].isOkay = false;
nodeList[74].rotation = 1.3;
or similar...
I'm really new to this, i found couple of topics about this, but it never compiles properly.
I would be really grateful if anyone could show the proper syntax, Thanks!
I would use this way:
var Node = function() {
this.name = "";
this.isOkay = true;
this.rotation = 0.0
}
var nodeList = [];
for (var i = 0; i < 10; i++)
{
nodeList.push(new Node());
}
nodeList[0].name = "test";
So you could create a new object(really new) in order to manage it later. Look here.
EDIT:
What I have done is created an object with a constructor method, you can check it on MDN here.
Creating an object like you have done:
var Node = { /* ... */ }
Is like having one object initiated. To have another, you'll have to write another one and so on. With that contructor you may create any instances you want based on that model.
You might want to do this lazily
Depending on the situation might be helpful to do this lazily
var Node = function(name, isOkay,rotation){
if(!(this instanceof Node)) return new Node(name,isOkay,rotation);
else {
this.name = name;
this.isOkay = isOkay;
this.rotation = rotation;
}
}
var NodeCollective = function(numberOfNodes){
if(!(this instanceof NodeCollective)) return new NodeCollective(numberOfNodes);
else{
var _collective={};
var _defaultName = "", _defaultIsOkay = true, _defaultRotation=0.0;
this.length = numberOfNodes;
this.getNode=function(nodeNumber){
if(!_collective.hasOwnProperty(nodeNumber) && nodeNumber < numberOfNodes){
_collective[nodeNumber]=
Node(_defaultName,_defaultIsOkay,_defaultRotation);
}
//I am just assuming I am not going to get garbage
//you can put in checks to make sure everything is kosher
//if you really want to
return _collective[nodeNumber];
};
}
}
but it also depends on what you are trying to do... if you might not be getting all of the nodes in your program then implementing them in some way that avoids greedily creating them could save you a lot of time if the code is executed often, but if the piece of code isn't executed often this might be over kill.
var nodeList = []; // create empty array
nodeList.push(Node); // add the object to the end of the array
nodeList[0].rotation = 1.3; // set rotation property of the object
console.log(nodeList[0]); // prints the object to console

Categories

Resources