当observableArray是其他模型的属性时,Knockout不会更新UI

时间:2016-03-12 17:24:18

标签: javascript jquery knockout.js

我有2个observableArray相互连接。当一个"功能"点击,我试图显示"任务"它的。但是KO,在我点击某个功能时不会更新UI。在控制台上,我可以跟踪我的viewModel,我可以看到在selectedFeature上成功加载的任务。但是,UI不会更新,即使所有数组都定义为可观察。

这是一个实时demo on fiddle.

请告诉我我失踪的地方?

function GetFeatures() {
    var url = "/Project/GetFeatures";

    $.get(url, "", function (data) {
        $.each(JSON.parse(data), function (i, item) {
            projectVM.features.push(new featureViewModelCreator(item, projectVM.selectedFeature));
        });
    });

};

function GetTasks(selectedFeature) {
    var url = "/Task/GetTaskList";
    $.get(url, { "FeatureId": selectedFeature.FeatureId }, function (data) {

        $.each(JSON.parse(data), function (i, item) {
            selectedFeature.tasks.push(new taskViewModelCreator(item, selectedFeature.selectedTask));
        });
    });
};

function taskViewModelCreator(data, selected) {
    var self = this;
    self.TaskId = data.TaskId;
    self.Title = data.Name;
    self.Status = data.Status.Name;
    self.CreatedDate = data.CreatedDate;
    self.UserCreatedFullName = data.UserCreated.FullName;

    this.IsSelected = ko.computed(function () {
        return selected() === self;
    });
}

function featureViewModelCreator(data, selected) {
    var self = this;
    self.FeatureId = data.FeatureId;
    self.Name = data.Name;
    self.Status = data.Status.Name;
    self.CreatedDate = data.CreatedDate;
    self.UserCreatedFullName = data.UserCreated.FullName;

    self.tasks = ko.observableArray();

    this.IsSelected = ko.computed(function () {
        return selected() === self;
    });

    self.selectedTask = ko.observable();
    self.taskClicked = function (clickedTask) {
        var selection = ko.utils.arrayFilter(self.model.tasks(), function (item) {
            return clickedTask === item;
        })[0];
        self.selectedTask(selection);
    }
}

function projectViewModelCreator() {
    var self = this;
    self.ProjectId = 1;
    self.features = ko.observableArray();
    self.selectedFeature = ko.observable();

    self.featureClicked = function (clickedFeature) {
        self.selectedFeature(clickedFeature);
        GetTasks(clickedFeature);
    }
}
var projectVM = new projectViewModelCreator();


ko.applyBindings(projectVM, $('.taskmanTable')[0]);

GetFeatures();

在用户界面上

<div class="taskmanTable">
    <table class="table table-hover featureList">
        <thead>
            <tr>
                <th>Title</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: features">
            <tr data-bind="click: $root.featureClicked, css: { active : IsSelected } ">
                <td><span data-bind="text: Name"> </span></td>
            </tr>
        </tbody>
    </table>
    <table class="table table-hover taskList">
        <thead>
            <tr>
                <th>Title</th>
            </tr>
        </thead>
        <tbody data-bind="foreach: selectedFeature.tasks">
            <tr>
                <td><span data-bind="text:Title"></span></td>
            </tr>
        </tbody>
    </table>
</div>

1 个答案:

答案 0 :(得分:2)

以下是包含主要注释的正确版本:here。 KO文档非常详细。

您已经提到了一个关于UI代码风格的有趣说明:&#34;据我所知,我们不会在UI&#34;上使用()。我之前没有注意过这个事实。

  1. 我们确实可以省略可观察的括号:ko observable;
  2. 视图包含带有无括号

    的可观察对象
    <label>
    <input type="checkbox" data-bind="checked: displayMessage" /> Display message
    </label>
    

    源代码:

    ko.applyBindings({
        displayMessage: ko.observable(false)
    });
    
    1. 我们可以省略UI上可观察数组的括号:ko observable array
    2. 视图包含:<ul data-bind="foreach: people">,而 查看模型有:

      self.people = ko.observableArray([
              { name: 'Bert' },
              { name: 'Charles' },
              { name: 'Denise' }
          ]);
      
      1. 我们可以省略UI上的括号,以便&#39; leaf&#39; observables或observables数组。这是您修改后的code sampledata-bind="if: selectedFeature"data-bind="foreach: selectedFeature().tasks">仅省略了叶子可观察的括号。

      2. 最后,我们可以省略“父母”的括号吗?观测?我们可以通过添加another ko UI-statement(代替if,示例2)来实现。

      3.   

        with binding将动态添加或删除后代元素   取决于相关值是否为null / undefined

        1. 但是,我相信,我们不能省略UI语句之外的父节点的括号,因为它等于javascript语句:projectVM.selectedfeature().tasks。 Othervise projectVM.selectedfeature.tasks将不起作用,因为observables没有此类属性任务。相反,一个observable包含一个具有该属性的对象,该属性通过使用方括号()调用而退出。实际上,在knockoutjs introduction页面上有一个例子。 <button data-bind="enable: myItems().length < 5">Add</button>
        2. 以下代码使用以下事实(可以找到here, example 2):

            

          重要的是要理解if绑定确实至关重要   使此代码正常工作。没有它,就会出现错误   试图在“Mercury”的上下文中评估capital.cityName   资本是空的。在JavaScript中,您不能进行评估   null属性或未定义值的子属性。

          &#13;
          &#13;
          function GetFeatures() {
            var data = {
              Name: "Test Feature",
              FeatureId: 1
            }
            projectVM.features.push(new featureViewModelCreator(data, projectVM.selectedFeature));
          
          };
          
          function GetTasks(selectedFeature) {
            var data = {
                Title: "Test Feature",
                TaskId: 1
            }
            selectedFeature().tasks.push(new taskViewModelCreator(data, selectedFeature().selectedTask));
          };
          
          function taskViewModelCreator(data, selected) {
            var self = this;
            self.TaskId = data.TaskId;
            self.Title = data.Title;
            // Step 3: you can omit $root declaration, I have removed it
            // just to show that the example will work without $root as well.
            // But you can define the root prefix explicitly (declaring explicit
            // scope may help you when you models become more complicated).
            
            // Step 4: data-bind="if: selectedFeature() statement was added
            // to hide the table when it is not defined, this statement also
            // helps us to avoid 'undefined' error.
            
            // Step 5: if the object is defined, we should referense 
            // the observable array via -> () as well. This is the KnockoutJS
            // style we have to make several bugs of that kind in order
            // to use such syntax automatically.
          
            this.IsSelected = ko.computed(function() {
              return selected() === self;
            });
          }
          
          function featureViewModelCreator(data, selected) {
            var self = this;
            self.FeatureId = data.FeatureId;
            self.Name = data.Name;
          
          
            self.tasks = ko.observableArray();
          
            this.IsSelected = ko.computed(function() {
              return selected() === self;
            });
          
            self.selectedTask = ko.observable();
            
          }
          
          function projectViewModelCreator() {
            var self = this;
            self.ProjectId = 1;
            self.features = ko.observableArray();
            self.selectedFeature = ko.observable();
          
            self.featureClicked = function(clickedFeature) {
              self.selectedFeature(clickedFeature);
              GetTasks(self.selectedFeature);
            }
          }
          
          var projectVM = new projectViewModelCreator();
          
          ko.applyBindings(projectVM, $('.taskmanTable')[0]);
          
          GetFeatures();
          &#13;
          <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
          <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
          <div class="taskmanTable">
            <table class="table table-hover featureList">
              <thead>
                <tr>
                  <th>Title</th>
                </tr>
              </thead>
              <tbody data-bind="foreach: features">
                <tr data-bind="click: $root.featureClicked, css: { active : IsSelected } ">
                  <td><span data-bind="text: Name"> </span></td>
                </tr>
              </tbody>
            </table>
            <hr/>
            <table data-bind="if: selectedFeature()" class="table table-hover taskList">
              <thead>
                <tr>
                  <th>Title</th>
                </tr>
              </thead>
              <tbody data-bind="foreach: selectedFeature().tasks()"><!-- $root -->
                <tr>
                  <td><span data-bind="text: Title"></span></td>
                </tr>
              </tbody>
            </table>
          </div>
          &#13;
          &#13;
          &#13;