knockout.js - Knockout component with observable object doesn't update data -


i have following component:

<template id="fruits-tpl">     <p>name: <input data-bind="value: name" /></p>     <p>type: <input data-bind="value: color" /></p> </template>   ko.components.register('fruits', {     viewmodel: function(params) {         this.name = params.name;         this.color   = params.color;     },     template: { element: 'fruits-tpl' } }); 

i'm using component view model below, items in observable list of different types , have different properties:

function fruit(data) {     this.name = ko.observable(data.name);     this.color = ko.observable(data.color); } function dessert(data) {     this.name = ko.observable(data.name);     this.packaging = ko.observable(data.packaging); } function vm(){     var data = [{name:"apples",color:"yellow"},{name:"cookies",packaging:"box"}];     this.items = ko.observablearray([new fruit(data[0]),new dessert(data[1])]);     this.items.choice = ko.observable(this.items()[0]); } 

this component works , underlying data updated every time change text in input boxes:

<div data-bind="component: {name: 'fruits', params: items.choice}"></div> 

now, encapsulate logic of observables component itself, changed component way:

ko.components.register('fruits', {     viewmodel: function(params) {         this.name = ko.observable(params.name);         this.color   = ko.observable(params.color);     },     template: { element: 'fruits-tpl' } }); 

... , have observable items.choice data inside:

function vm(){     var data = [{name:"apples",color:"yellow"},{name:"cookies",packaging:"box"}];     this.items = ko.observablearray(data);     this.items.choice = ko.observable(this.items()[0]); } 

why goes underlying data in main viewmodel not updated in second example, though items.choice still observable? i'm sure i'm missing concepts, maybe each item in observable array should observable, don't have understand if there way make second example work.

first example: http://jsfiddle.net/5739ht0q/2/ second example: http://jsfiddle.net/079tx0nn/

there several ways update data main view model.

let's refine little bit main view model:

function vm(data) {   var self = this;   self.items = ko.observablearray(ko.utils.arraymap(data, function(item) {     return ko.observable(item);   }));   self.items.choice = ko.observable(0);   self.items.choice.data = ko.computed(function() {     return self.items()[self.items.choice()];   }); } 

1) first quick & dirty way pass component function has been defined inside main view model:

<div data-bind="component: {     name: 'fruits',     params: {index: items.choice(), data: items.choice.data(), update: items.update} }"></div> 

inside component can call function save changes:

self.data = ko.computed(function(){     params.update(params.index, ko.tojs(self)); }); 

the update function in main view model obvious, no need spend more words that.

2) second way use subscribable establish communication along view models:

ko.intramodels = new ko.subscribable(); 

from component, send notification:

self.data = ko.computed(function(){   ko.intramodels.notifysubscribers(ko.tojs(self), "updatefruits"); }); 

the subscription inside main view model receive , save changes, more or less this:

  ko.intramodels.subscribe(function(newvalue) {     self.items.replace(self.items()[self.items().index], newvalue);   }, self, "updatefruits"); 

of course done hand above, great ryan niemeyer's post box library optimal choice here: https://github.com/rniemeyer/knockout-postbox.

i tested both solutions, unfortunately had trouble when activated - new in knockout 3.4 - deferred update option: ko.options.deferupdates = true; received "maximum call stack size exceeded" error.

3) final solution: because not want give , miss new wonderful performance enhancement of knockout 3.4, , because error means more or less: "circular dependencies design error, please rethink part of implementation", changed view model keep dependency tracking chain working in 1 direction using 1 observable whole component data:

ko.components.register('fruits', {   viewmodel: function(params) {     var self = this;     self.data = params.peek();     self.item = {};     self.item.name = ko.observable(self.data.name);     self.item.color = ko.observable(self.data.color);     self.update = ko.computed(function() {        params(ko.tojs(self.item));     });   },   template: {     element: 'fruits-tpl'   } }); 

this far more evident nested components have data handling , observable creation within them, , main view model does't have know inside children , why -- pass , receive observable object:

<div data-bind="component:{name:'fruits',params:items.choice.data}"></div>  <template id="containers-tpl">   <div data-bind="foreach: containers">     <p><input data-bind="textinput: quantity"><span data-bind="text: name"></span></p>   </div> </template>  <template id="fruits-tpl">   <p>name:<input data-bind="textinput: item.name"></p>   <p>color:<input data-bind="textinput: item.color"></p>   <div data-bind="component:{name:'containers',params:item.containers}"</div> </template> 

key points here are: pass observable component, not data, , break dependency chain of params inside component.

complete fiddle nested components: http://jsfiddle.net/jo37q7ul/


Comments

Popular posts from this blog

c# - How Configure Devart dotConnect for SQLite Code First? -

java - Copying object fields -

c++ - Clear the memory after returning a vector in a function -