2016-02-27 9 views
0

Я следующий компонент:Нокаута компонент с наблюдаемым объектом не обновляет данные

<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' } 
}); 

Я использую этот компонент вместе с моделью зрения ниже, где элементы в моем наблюдаемом списке имеют различные типов и имеют различные свойства:

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]); 
} 

Этот компонент работает очень хорошо, и лежащие в основе данных обновляется каждый раз, когда я изменить текст в моих полях ввода:

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

Теперь я хотел бы, чтобы инкапсулировать логику моих наблюдаемые в самом компонент, поэтому я изменил компонент таким образом:

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

... и теперь у меня есть моя наблюдаемая items.choice с только лишь данные внутри:

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]); 
} 

Почему идут исходные данные в основном ViewModel не обновляются в моем втором примере, хотя items.choice еще наблюдаемая? Я уверен, что мне не хватает некоторых концепций, возможно, каждый элемент в моем наблюдаемом массиве также должен быть наблюдаемым, но я не понимаю, есть ли способ заставить второй пример работать.

Первый пример: http://jsfiddle.net/5739ht0q/2/ Второй пример: http://jsfiddle.net/079tx0nn/

+1

Вы создаете наблюдаемые внутри компонента. Там они обновляются. То, что вы печатаете, - это данные, которые передаются вашему компоненту, и он не обновляется. Вы не можете смотреть на данные, созданные внутри вашего компонента из-за пределов его. –

+0

Да, есть ли способ обновить данные до массива элементов? – deblocker

ответ

1

Существует несколько способов обновления данных до основной модели представления.

Давайте уточним только немного основной вид модели:

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) Первый быстрый & грязный способ, чтобы перейти к компоненту функцию, которая была определена внутри основной модели представления:

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

Внутри компонента можно вызвать функцию, чтобы сохранить изменения:

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

Функция обновления в модели основного вида теперь очевидна, нет необходимости тратить больше слов на это.

2) Второй способ будет использовать subscribable установить связь по просмотру моделей:

ko.intramodels = new ko.subscribable(); 

От компонента, отправить уведомление:

self.data = ko.computed(function(){ 
    ko.intramodels.notifySubscribers(ko.toJS(self), "updateFruits"); 
}); 

Подписка внутри основная модель обзора получит и сохранит изменения, более-менее примерно так:

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

Конечно, это может быть сделано вручную, как показано выше, но оптимальная выборная библиотека колонок Райана Нимейера будет здесь: https://github.com/rniemeyer/knockout-postbox.

Я протестировал оба решения, но, к сожалению, у меня были некоторые проблемы, когда я активировал - новый в нокауте 3.4 - вариант отсроченного обновления: ko.options.deferUpdates = true;, поскольку я получил ошибку «Максимальный размер стека вызовов».

3) Окончательное решение: Потому что я не хочу сдаваться и пропустить это новое замечательное повышение производительности нокаута 3.4, и потому, что эта ошибка означает также более или менее: «круговые зависимости ошибка проектирования, пожалуйста, пересмотреть какую-то часть вашей реализации», я изменил модель представления, чтобы сохранить цепочку отслеживания зависимостей работают только в одном направлении, но с использованием только один наблюдаемым для всего данных компоненты:

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' 
    } 
}); 

Это гораздо более очевидно, с вложенными компонентами, которые имеют всю обработку данных и ervable создание в них, а основной вид модели does't должны ничего знать о том, что находится внутри детей и почему - только лишь пройти и получить обратно наблюдаемый объект:

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

Ключевые моменты : передать наблюдаемому компоненту, а не только данные, и разбить цепочку зависимостей параметров внутри компонента.

Полная Fiddle с вложенными компонентами: http://jsfiddle.net/jo37q7uL/

0

Наблюдаемые находятся в центре нокаута и позволяет 2 способ связывания между видом и ViewModel. Для поля JSON для обновления компонента необходимо изменить наблюдаемое, к которому имеет доступ «VM».

Во избежание определения функций фруктов и десертов вы можете использовать Knockout mapping для преобразования вашего массива данных в ko.observableArray. Это задает поля каждого объекта в массиве для наблюдаемых, чтобы они могли быть переданы в привязку компонента.

+0

, который будет таким же, как в моем первом примере, где каждое свойство каждого элемента массива уже является наблюдаемым с самого начала, только что сделанное с помощью плагина сопоставления. Это то, о чем я прошу, если можно этого избежать. – deblocker