2016-10-10 3 views
1

У меня простая разметка HTML со списком моих моделей просмотра в ней и шаблоном. Кроме того, у меня есть свой ViewModel в <pre> разделе:Наблюдаемые режимы просмотра в наблюдаемом массиве не работают в KnockoutJs

<div class="container"> 
    <div class="row"> 
    <div class="col-md-6"> 
     <div class="col-md-12"> 
     <div class="panel panel-primary"> 
      <div class="panel-heading"> 
      Names 
      <button type="button" class="btn btn-link" data-bind="click: addName"> 
       <span class="glyphicon glyphicon-plus"></span> 
      </button> 
      </div> 
      <div class="panel-body"> 
      <ul class="list-group" id="namesList" data-bind="foreach: Names"> 
       <li class="list-group-item"><my-comp></my-comp></li> 
      </ul> 
      </div> 
     </div> 
     </div> 
    </div> 
    <div class="col-md-6"> 
     <div class="col-md-12"> 
     <pre data-bind="text: ko.toJSON(Names, null, 2)"></pre> 
     </div> 
    </div> 
    </div> 
</div> 
<script type="text/html" id="fullNameTmpl"> 
    <p>First name: <strong data-bind="text: firstName"></strong></p> 
    <p>Last name: <strong data-bind="text: lastName"></strong></p> 

    <p>First name: <input data-bind="textInput: firstName" /></p> 
    <p>Last name: <input data-bind="textInput: lastName" /></p> 

    <p>Full name: <input data-bind="textInput: fullName"></p> 

    <button data-bind="click: capitalizeLastName">Go caps</button> 
</script> 
<script> 
    function AppViewModel() { 
    var self = this; 
    self.firstName = ko.observable("Bert"); 
    self.lastName = ko.observable("Bertington"); 

    this.fullName = ko.pureComputed({ 
     read: function() { 
     return self.firstName() + " " + self.lastName(); 
     }, 
     write: function (value) { 
     var lastSpacePos = value.lastIndexOf(" "); 
     if (lastSpacePos > 0) { 
      self.firstName(value.substring(0, lastSpacePos)); 
      self.lastName(value.substring(lastSpacePos + 1)); 
     } 
     }, 
     owner: self 
    }); 

    self.capitalizeLastName = function() { 
     var currentVal = this.lastName();   
     this.lastName(currentVal.toUpperCase()); 
    }; 
    } 
    ko.components.register('my-comp', { 
    viewModel: AppViewModel, 
    template: { element: "fullNameTmpl" } 
    }); 

    function NamesViewModel() { 
    var self = this; 

    self.Names = ko.observableArray(); 

    self.addName = function() { 
     var vm = ko.observable(new AppViewModel()); 
     self.Names.push(vm); 
    } 
    } 

    ko.applyBindings(new NamesViewModel()); 
</script> 

Это хорошо работает, я могу добавить элемент в список и связывания данных в этих пунктов работает. Когда я добавляю новый AppViewModel, я могу видеть его в разделе <pre>, в Names массив. Проблема в том, что когда я изменяю некоторые значения в вложенной среде просмотра, например, набирая новую фамилию, независимо от того, нет изменений в разделе <pre> в текущей модели просмотра в массиве Names.

Как я могу сделать действительно наблюдаемый массив наблюдаемых? Какой самый выгодный способ добавить такие компоненты? Я уже сделал вложенную viewmodel наблюдаемой, но, похоже, она не работает.

+0

Я думаю, что проблема здесь в том, что нокаута observableArray обрабатывает массив себя как obversable, уведомляя о событиях, как толчок и удалить, но не известит, если одно свойство из элемента в массиве изменения. Я уверен, что вы останетесь самим реализовать это (в документах есть «ключевой момент», который очень кратко упоминает об этом - http://knockoutjs.com/documentation/observableArrays.html) –

+0

@NickDeFazio, но в 'addName', я добавляю' ko.observable', этого недостаточно? –

ответ

1

Существует несколько проблем с вашим кодом. Проблема не обязательно с наблюдаемым массивом - это проблема с использованием вами компонента. Назначение компонентов перечислены here. Некоторые примечания:

  1. Элементы наблюдаемого массива не обязательно должны быть сделаны observable. Так что не нужно делать var vm = ko.observable(new AppViewModel()); Просто используйте var vm = new AppViewModel(); и нажмите на свой массив. Однако, если вы хотите использовать компонент, это тоже неверно! Что делать, это в основном обновлять AppViewModels дважды! Вы можете понять, почему? Вы делаете это один раз явно в строке выше, а затем компонент делает это один раз неявно. Каждый раз, когда компонент создается, он связан с AppViewModel - это то, что происходит, когда вы регистрируете компонент.
  2. Компонент, ассоциированный с вами, может считаться мини-viewModel с его собственными свойствами, функциональностью, вычислениями и т. Д. Поэтому в вашем случае вам, вероятно, не нужно будет использовать компонент -> скопировать и вставить внутренние элементы ваших <script></script> тегов в <li><li>, и он должен просто работать.

Я не уверен, что это ответ, который вы ищете, но это должно делать то, что вы хотите. Вы можете использовать компоненты, если хотите, но вы должны сделать больше изменений и крошечный бит ненужной сложности. Я позволил изменить имена переменных. Я думаю, что они могут быть более уместными. Надеюсь это поможет!

Редактировать

Я редактировал фрагмент кода для работы с компонентом. Проблема с этой конкретной реализацией заключается в том, что компонент теперь тесно связан с AppViewModel через имена observableArray. В вашем случае это может быть хорошо, но часто это может привести к нежелательному поведению, особенно после того, как ваши модели просмотра начнут становиться все более сложными. Конечно, вы могли бы передать развернутые значения firstName и lastName, чтобы быть лучше развязаны, но затем вы вернетесь на квадрат, потому что <pre data-bind="text: ko.toJSON(names, null, 2)"></pre> не будет регистрировать изменений, внесенных в имена наблюдаемыхArray.

ko.components.register('my-comp', { 
 
    viewModel: function(params) { 
 
    var self = this; 
 
    self.firstName = params.data.firstName 
 
    self.lastName = params.data.lastName 
 
    self.fullName = ko.pureComputed({ 
 
     read: function() { 
 
     return self.firstName() + " " + self.lastName(); 
 
     }, 
 
     write: function(value) { 
 
     var lastSpacePos = value.lastIndexOf(" "); 
 
     if (lastSpacePos > 0) { 
 
      self.firstName(value.substring(0, lastSpacePos)); 
 
      self.lastName(value.substring(lastSpacePos + 1)); 
 
     } 
 
     }, 
 
     owner: self 
 
    }); 
 

 
    self.capitalizeLastName = function() { 
 
     var str = self.lastName(); 
 
     self.lastName(str.toUpperCase()); 
 
    }; 
 
    }, 
 
    template: { 
 
    element: "fullNameTmpl" 
 
    } 
 
}); 
 

 
var Name = function(){ 
 
    this.firstName = ko.observable("Bert"); 
 
    this.lastName = ko.observable("McBertington"); 
 
} 
 

 

 
function AppViewModel() { 
 
    var self = this; 
 
    self.names = ko.observableArray(); 
 
    self.addName = function() { 
 
    self.names.push(new Name()); 
 
    } 
 
} 
 

 

 
ko.applyBindings(new AppViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 
 

 
Names 
 
<button type="button" class="btn btn-link" data-bind="click: addName"> 
 
    Add Dude 
 
</button> 
 

 
<ul class="list-group" id="namesList" data-bind="foreach: names"> 
 
    <my-comp params="data: $data"></my-comp> 
 
    <!--<li class="list-group-item"> 
 
    <p>First name: <strong data-bind="text: firstName"></strong></p> 
 
    <p>Last name: <strong data-bind="text: lastName"></strong></p> 
 
    <p>First name: <input data-bind="textInput: firstName" /></p> 
 
    <p>Last name: <input data-bind="textInput: lastName" /></p> 
 
    <p>Full name: <input data-bind="textInput: fullName"></p> 
 
    <button data-bind="click: capitalizeLastName">Go caps</button> 
 
    </li>--> 
 
</ul> 
 

 

 
<pre data-bind="text: ko.toJSON(names, null, 2)"></pre> 
 

 
<script type="text/html" id="fullNameTmpl"> 
 
    <li> 
 
    <p>First name: <strong data-bind="text: firstName"></strong></p> 
 
    <p>Last name: <strong data-bind="text: lastName"></strong></p> 
 
    <p>First name: <input data-bind="textInput: firstName" /></p> 
 
    <p>Last name: <input data-bind="textInput: lastName" /></p> 
 
    <p>Full name: <input data-bind="textInput: fullName"></p> 
 
    <button data-bind="click: capitalizeLastName">Go caps</button> 
 
    </li> 
 
</script>

+0

Спасибо за ответ! Оно работает! Однако в первом случае вы сказали: _ Каждый раз, когда компонент создается, он связан с AppViewModel - это то, что происходит, когда вы регистрируете компонент., Как я могу правильно зарегистрировать и затем вызвать мой компонент? В моем случае мне действительно нужно использовать компоненты, потому что мне нужно использовать этот редактор имен повсюду. Насколько я понимаю, что-то не так с компонентами, не могли бы вы привести пример, как я могу работать с ними, в моем случае? –

+0

Привет, я обновил ответ. Надеюсь, это поможет! –

+0

Я не могу понять, почему нам нужна модель просмотра «Имя», какова ее цель? –

Смежные вопросы