Knockout Options Object Binding - javascript

I'm struggling with Knockout.js Options binding, to an Object.
I'm attempting to create a workflow for the user that allows them to add an Item, edit its properties and then save/cancel to propagate those changes.
I've accomplished this type of task before with jquery. However I'd like to avoid the complicated stack calls that jquery would require. (if possible).
I've created an example:
http://jsfiddle.net/nAE2f/
Whats working in the example:
The Add button, creates a new object.
The Save Button will save it to the array.
The Select Dialog will update with a new Option.
Unfortunately, this is where my progress has halted. While the Select Option is created, it doesn't reflect the underlying objects Name. Also Switching between objects doesn't change the forms properties as I would expect.
I've tried assigning the optionValue to the id, but in that case the Select Options isn't created on save.

The problem with the way you binding member name to item. In your case saved item always has empty name that's why select also display empty text. I fixed this by creating selectedMember property within ViewModel to handle selected member and assigning member name to item on Save.
Check fiddle for example

Related

How to check if any value was changed in an editable Object without using FormGroup?

I am having nested objects with multiple properties. I have created an input field on the UI for every property and value is changed by using [(ngModel)]. I want to implement a functionality where if any value in the object is changed I should be able to detect it and enable the reset option to show initial values again.
I have tried exploring this and the majority of answers were related to form controls only.
if you bind with two-way binding to data you will not be able to hold old record. You can write a fn to compare to objects if you bind with [ngModel].Here is pseudo.
in .ts
value = {}
onValueChange(data:any){
const changedData = getChangedData(this.value, data);
}
in .html
<input [ngModel]="value" (ngModelChange)="onValueChange($event)">
}

How to reasign object with `this` in Angular component?

I have 2 variables inside an Angular component/class FilterComponent that point to an object. The first one (localStorage) stores the results of the current filtering choices that the user makes and is connected with toogle buttons with [(ngValue)]. The second one (globalStorage) gets the results of filtering choices that the user made earlier within other components (if you for example click on fruit on home page,globalStorage.food gets updated, but not the localStorage within FilterComponent). I have a method that should assign gloablStorage to localStorage when the user open the filter that looks like this:
openFilter() {
this.localStorage = this.globalStorage; //this line is responsible for strange behaviour
...
}
This works exactly like it should, but only after the apply button on filter (within FilterComponent) is clicked, which is responsible for sending the contents of localStorage to globalStorage (via next/behaviourSubject).
For some mysterious reason, before that happens, the values of toggleButtons get assign to globalStorage as well as to localStorage (although they are connected only to localStorage). Why is this happening and how can I fix it?
when you do this.localStorage = this.globalStorage; and these 2 variables are objects, it means this.localStorage now point/refer to this.globalStorage, so from now on this 2 variable will refer to 1 place.
If you just want to copy data inside it you should do a copy instead of simple assign by using either Object.assign(shallow copy) or this.local = {...this.global} for loop deep copy
Thank you all for your help. The solution (that's good enough for now) was to reassign each property:
this.localStorage.property1 = this.localStorage.property1;
this.localStorage.property2 = this.localStorage.property2;

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).

knockout.js referring to parent object

I have a complex knockout.js object which is effectively an observableArray with another observableArray inside. I have a remove function which asynchronously removes chosen element from the second array. An item is being archived in the database, while one of its observable properties on client side is being set to false making it to disappear from the screen.
A remove button event is created using $root keyword:
Remove
What gives me access to details of the chosen element using "this" keyword. My problem is, that while deleting item from the second array I would like to change something to its parent item in the first array. As I mentioned "this" keyword refers to the child item, is there any way I could access the parent item at the same time as well?
mhu's answer is a antipattern because it creates a dependency between ViewModel and the structure of the View.
Instead do
Remove
Parent Viewmodel
removeActivity: function(activity) {
this.activities.remove(activity);
}

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/

Categories