可观察数组中的可观察视图模型不适用于KnockoutJs

时间:2016-10-10 16:34:01

标签: knockout.js viewmodel observable

我有简单的HTML标记,其中包含我的视图模型列表和模板。另外,我在<pre>部分中有我的viewmodel:

<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 observable,但它似乎无法工作。

1 个答案:

答案 0 :(得分:1)

您的代码存在一些问题。问题不一定是可观察数组 - 这是您使用组件的问题。组件的目的列在here中。有些事情需要注意:

  1. 可观察数组元素不需要observable。所以不需要var vm = ko.observable(new AppViewModel());只需使用var vm = new AppViewModel();并推送到您的阵列。但是,如果你想使用一个组件,那么这也不正确!正在做的是基本上两次推出AppViewModels!你能明白为什么吗?你在上面的行中明确地执行了一次,然后组件隐式地执行一次。每次创建组件时,它都与AppViewModel相关联 - 这是注册组件时发生的情况。
  2. 组件与您相关联,您可以将其视为具有自己的属性,功能,计算等的迷你viewModel。因此,在您的情况下,您可能不需要使用组件 - &gt;将<script></script>标记的内部复制并粘贴到<li><li>中,它应该可以正常工作。
  3. 我不确定这是否是您正在寻找的答案,但这应该做您想要的。如果您愿意,可以使用组件,但是您必须进行更多更改并且需要一些不必要的复杂性。我冒昧地改变你的变量名。我认为他们可能更合适。希望这有帮助!

    修改

    我已编辑代码段以使用组件。这个特定实现的问题是组件现在通过名称observableArray与AppViewModel紧密耦合。在您的情况下这可能没问题,但通常这会导致不良行为,尤其是一旦您的视图模型开始变得更大和更复杂。当然,你可以传递firstNamelastName的展开值,以便更好地解耦,但是你会回到正方形,因为<pre data-bind="text: ko.toJSON(names, null, 2)"></pre>不会注册任何更改取名为observableArray。

    &#13;
    &#13;
     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());
    &#13;
    <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>
    &#13;
    &#13;
    &#13;