Asyncronous knockout observableArray select options loading - javascript

I hava a selectOptions ajax-based asincronous loader; it accepts remote address and returns an observable array, correctly populated with descriptions and keyvalues to be accepted by the following binding
<select data-bind="value: selectedVal, options: opts, optionsText: 'desc', optionsValue:'key', optionsCaption: ''"/></div>
The fact is that, being asincronous, when I trigger a select options change, based on some user actions, I assign it to my model observable array, I do not get the select popuated, but remains empty.
mymodel.opts = loadOptions("<remoteaddress>");
I know when the second line is called the anwer is not arrived yet, but the returned value is an observableArray, so it should respond correctly whenever is populated, having been assigned to an observable array binded with the ui.
If I hardcode the returned object from the ajax call (when it returns) taking it from console.log in Firefox, or if I pass the observable array opts into the loadOptions, and change it to build up the opts inside it, then it works, but I really need to use loadOptions as is, asincronous. I also tried to append mymodel.opts.valueHasMutated(), but yet ko cannot use the newlly arrived observableArray.
If possible leaving intact the options loader, and if possible without using a custom binding, can I use the incoming observable array for binding when it will be ready?

The problem you've got is that when this line runs:
mymodel.opts = loadOptions("<remoteaddress>");
it's replacing the entire observable array with a different observableArray, rather than updating the current one. You need to update the existing one - can you change loadOptions to return a normal array, rather than an observable one? You can then do:
//clear any existing entries
mymodel.opts.removeAll();
//push the new entries in
mymodel.opts.push.apply(mymodel.opts, loadOptions("<remoteaddress>"));

Related

Strange knockout js select option binding issue

Does anybody have a minute to take a look at a puzzling problem I'm having with Knockout JS and binding select lists?
The person's favourite color should be selected in the list by calling value: favColorId in the select list, rather than with the function-based call of value: favColorId(), something very strange is going on here, I've never used () in the past, it's also causing some other weird issues where it won't recall the value into the span (so changing the selected item does nothing).. I have tried recreating a simple sample as best I can demonstrating the issue.
http://jsfiddle.net/goneale/ph8Jw/
I have included my mapDictToArray() function but it simply converts a
javascript object into a key-value JS array. I wouldn't think that is
contributing to the problem.
Actually, that was part of the problem. The function returns a JavaScript array, not an observable array and therefor can't be used properly by Knockout. I've made the following changes to your code:
// The "mapDictToArray" makes a normal JS array, not a ko.observableArray();
// You can't simply "merge" a JS array with an observableArray(); you'll need
// some extra functionality for that: ko.utils.arrayPushAll()
// viewModel.colors(mapDictToArray(dict));
ko.utils.arrayPushAll(viewModel.colors(), mapDictToArray(dict));
// Apply the bindings *after* you've added the contents to the "colors" observable, in order to
// get the correct selected value
ko.applyBindings(viewModel);
That should do the trick (with the correct HTML without the () )!
JSFiddle.
UPDATE
I thought about my solution, but something wasn't correct. The only thing that was correct, was the part that you need to apply the bindings after you've added the contents of the colors observable. This is your fiddle, with that part moved down.
This works in your case, but you'll need to use the arrayPushAll method when there is already data inside the observableArray. This method merges, while you overwrite it when not using it (example with data inside the observable).

Can you modify multiple entities in an observable array, but have Knockout fire only a single change notification?

I have an observable array from which I need to remove one item and add another. If I just call .remove(myItem) on the observable array and then push my new item onto it, Knockout will fire two change notifications for that array, but in this case I would rather see this as a single change, firing only one notification.
What I've done to accomplish this is that I take out the underlying array and keep it in a temporary variable, I then do the modifications to that temporary variable, and when I'm done I put the whole thing back into the observable array. Something like this (somewhat simplified from what I actually do):
vm.MyArray = ko.observableArray(["foo", "bar"]);
// Later when I need to alter the observable array, I do something like this
var temp = vm.MyArray();
temp.splice(0, 1);
temp.push("some");
vm.MyArray(temp);
Though this accomplish my goal, it doesn't feel like the right way of doing it.
Is there a more proper way to make multiple changes to an observable array, but only have a single notification fired?
Update:
The reason I don't want two notifications fired is because I subscribe to changes on the array, and make an AJAX-request to load new data whenever it changes. Because of this, I don't want to fire unnecessary notifications. I don't want notifications to be fired until I'm "done" editing the array.
To achieve such behavior you should work with unwrapped observable array and then call valueHasMutated method:
vm.MyArray = ko.observableArray(["foo", "bar"]);
vm.MyArray().remove(myItem);
vm.MyArray().push(newItem);
vm.MyArray.valueHasMutated();

Iterating through models for a select field with Rivet.js & Coffeescript

I'm attempting to populate a select field with options using bootstrapped data. I'm encountering an issue when binding my array of models to the jQuery select object...
The HTML
<select data-each-project="projects" id="project-selection">
<option data-value="project:description"></option>
</select>
The Binding
project_array = new Array()
_.each projects, (project) ->
projects_array.push project
rivets.bind #el.select, projects:projects_array
The Result
I receive an error indicating that the object has no .on method -> which it doesn't because it is an array of models not a model it self...
How should this really be done? Thanks!
When you subscribe to an iteration binding rivets does two things:
Subscribes to the entire array so if it changes it will rerun the
iteration
Subscribe to all children of the array that need binding
Rivets isn't subscribing to your children because you are not using any bindings that require it.
project:description = non-subscribe binding
project.description = subscribe binding
If you don't want to subscribe to array changes (I think that's what you're asking for) you can do data-each-project=":projects"

Knockout.js and twoway binding of checkboxes

First: I am new to knockout js, and trying to wrap my head around the knockout/mvvm way of thinking, so please forgive me if my question turns out to be trivial.
What I have is a the following case: I have a knockout viewmodel containing an observableArray of selected ticket objects. This array represents a user-defined selection/subset of a bigger set of tickets. The whole set of tickets are listed in a jqgrid table, and each row has a checkbox which is supposed to be telling whether each ticket is selected or not. This means that the value of the checkbox needs to be updated whenever the "selectedTickets" array changes. In addition to this I also want the user to be able to click each checkbox in order to add/remove a ticket from the selection. Would seem like a fairly acceptable piece of functionality, right?
I do however have trouble seeing how I could use the knockout "checked" binding in order to achieve this. My first idea was to use a computed/dependent observable on the viewmodel object called "isSelected" which would reflect changes in the selectedTickets array and return true or false based on whether a ticket is in the selectedTickets array or not. The first problem here is that I then need to pass a parameter to the computed observable saying which ticket ID it's supposed to look up, and from what I can see that only works on a writable computed observable. Getting the state for the checkbox does however not seem like a write operation, so something already started to smell. Next issue is that the binding needed to be twoway, as I wanted the user to be able to change the state of each checkbox and having the selectedTickets array being updated accordingly. This is a different operation, as it would actually remove/add tickets to the selectedTickets array. Which would again trigger the computed observable trying to set the state of the checkbox. Seems like these two use cases may end up like an infinite loop if I try to do it this way. I haven't found a good way of combining these two use cases by just using the checked binding for the checkboxes.
I could of course do the event handling on the checkboxes manually, by hooking up listeners to the changed event on the checkboxes and to the selectedTickets array in the knockout viewmodel, but I was hoping this was possible to more automated with knockout bindings.
Hope there are some knockout masters out there who can guide me onto a good path, as I feel I've steered off-track with this one.
When using knockout.js, you need to stop doing things by halves - if you have a list of items, the data belongs in the viewmodel (not only the select items), and only the appearance is defined by the view.
Thus, I'd recommend an observable array items of type Item, which has a property isSelected - the selected items can then be made accessible via a computed observable:
var Item = function(name) {
this.name = ko.observable(name);
this.isSelected = ko.observable(false);
};
var ViewModel = function() {
var self = this;
self.items = ko.observableArray([
new Item('Foo'), new Item('Bar'), new Item('Foo Bar')
]);
self.selectedItems = ko.computed(function() {
return ko.utils.arrayFilter(self.items(), function(item) {
return item.isSelected();
});
});
};
http://jsfiddle.net/htZfX/

KnockoutJS observableArray to update when inner observable is changed

I need to know how to trigger the update for an observableArray when an observable is changed inside the observableArray.
I have an observableArray that represents a binary tree. I'm using a storage mapping function to get and set the values in the array so it has to be balance even thought it might only contain an empty observable. E.g. nodes()[9] maybe null but when that node is updated I would call nodes()[9](set new value) and need to trigger the observableArray to update
Ended up using .replace() on the observableArray
Managed to look through the code and find observableArray.replace ()
RELATED: How to replace a given index element in knockoutjs

Categories