2013-04-24 2 views
1

Я изучаю KO и пытаюсь создать простой элемент списка add/remove. но у меня есть следующие вопросы.binding и ko.toJSON не работает как ожидалось

Q1: Если вы нажмете «Добавить» без ввода имени, элемент, который вы только что добавите, будет иметь имя result. почему и как предотвратить его, пока пустые, как действительные входы?

Q2: Я хотел бы использовать тег <pre>, чтобы показать JSON модели текущего вида, но это ничего не показывает. Но кнопка показывает что-то. Я понял это. Мне нужно использовать ko.toJSON($data, null, 4).

Q3: Это связано с Q2, кнопка показывает что-то, но то, что оно показывает, кажется странным. После ввода нескольких элементов нажмите кнопку debug. Вы увидите, что все элементы в модели просмотра заменены на последний элемент, который вы изменили.

Вот fiddle.

CSS:

input[type=text], select { 
    width:100px; 
} 

JS:

function foodie() { 
    this.name; 
    this.food; 
} 

function foodieApp() { 
    var self = this; 
    self.foodies = ko.observableArray(); 
    self.foodieToAdd = ko.observable(new foodie()); 

    self.addFoodie = function() { 
     this.foodies.push(this.foodieToAdd()); 
    }; 

    self.delFoodie = function (foodieToDel) { 
     self.foodies.remove(foodieToDel); 
    }; 

    self.debug = function() { 
     alert(ko.toJSON(self, null, 4)); 
    }; 
} 

var app = new foodieApp(); 
ko.applyBindings(app); 

HTML:

<table> 
    <thead> 
     <tr> 
      <td>Foodie's Name 
       <br/> 
       <input type=text data-bind='value: foodieToAdd().name' /> 
      </td> 
      <td>Foodie's Food 
       <br/> 
       <select data-bind='value: foodieToAdd().food'> 
        <option value=apple>Apple</option> 
        <option value=banana>Banana</option> 
        <option value=cherry>Cherry</option> 
       </select> 
      </td> 
      <td> 
       <input type=button value=Add data-bind='click: addFoodie' /> 
      </td> 
     </tr> 
    </thead> 
    <tbody data-bind='foreach: foodies'> 
     <tr> 
      <td> 
       <input type=text data-bind='value: name' /> 
      </td> 
      <td> 
       <select data-bind='value: food'> 
        <option value=apple data-bind>Apple</option> 
        <option value=banana>Banana</option> 
        <option value=cherry>Cherry</option> 
       </select> 
      </td> 
      <td> 
       <input type=button value=del data-bind='click: $parent.delFoodie' /> 
      </td> 
     </tr> 
    </tbody> 
</table> 
<input type=button value=debug data-bind='click: debug' /> 
<!-- why the following <pre> tag is empty? --> 
<pre data-bind='text: ko.toJSON(app, null, 4)'></pre> 

ответ

2

Вот обновленный скрипку: http://jsfiddle.net/jearles/TgD6a/3/

-

  • Q1: Вы не инициализирует ваш foodie объект
  • Q2: app не является частью вашей ViewModel - это ViewModel. Используйте $root, чтобы получить доступ к верхнему уровню ViewModel
  • Q3: Это связано с тем, как вы определяете foodieToAdd. В основном вы только создаете один экземпляр, поэтому нокаут продолжает добавлять один и тот же объект снова и снова. Поскольку у вас нет наблюдаемых свойств, нокаут не обновлял пользовательский интерфейс.

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

EDIT: см. Комментарии ниже, почему нокаут поднимает result.

-

function foodie(foodie) { 
    this.name = ko.observable(foodie ? foodie.name() : ''); 
    this.food = ko.observable(foodie ? foodie.food() : ''); 
} 

function foodieApp() { 
    var self = this; 
    self.foodies = ko.observableArray(); 
    self.foodieToAdd = new foodie(); 

    self.addFoodie = function() { 
     this.foodies.push(new foodie(this.foodieToAdd)); 
    }; 

    self.delFoodie = function (foodieToDel) { 
     self.foodies.remove(foodieToDel); 
    }; 

    self.debug = function() { 
     alert(ko.toJSON(self, null, 4)); 
    }; 
} 

var app = new foodieApp(); 
ko.applyBindings(app); 
+0

Это все правильно, но я не могу понять, почему 'result' становится помещен в' name' переплета. Есть идеи? –

+0

Я не совсем понимаю, почему, кроме того, что он связан со связыванием со свойствами, не инициализируется. Это похоже на что-то в DOM-рендеринге логики Knockout. Если вы посмотрите на эту скрипку (http://jsfiddle.net/jearles/TgD6a/4/), вы увидите рендеринг отладки. Вы не увидите имени в своем объекте, но вы можете изменить 'result' на' res', и если вы нажмете add, оно примет это значение. –

+0

Я сделал некоторую отладку этим утром, перейдя в отладочную версию нокаута. Причина «результата» набирается на самом деле довольно просто, но она включает в себя малоиспользуемый оператор JavaScript: 'with'. Нокаут оценивает эту функцию: 'с ($ контекста) {с ($ данных || {}) {возврата { 'значение': имя, ...}}}' Параметр 'with' заявления добавить '$ context' и' data' в цепочку областей, где будет пытаться решить 'name'. Ни у одного из них нет свойства 'name', но' this' (который есть Window) делает ... и это 'result'. Попробуйте изменить 'name' на' foodieName', и у нас больше нет неожиданного «результата». –

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