分配了计算属性

时间:2018-01-31 02:24:02

标签: javascript knockout.js

我有一个使用Knockout构建的可编辑网格,它遵循所讨论的网格线here

我希望为hasfocus分配一个计算的可观察对象(与在上述链接帖子上完成的内容相反,其中hasfocus绑定分配了相同的内容可观察为visible),但它不起作用 - 编辑的元素没有被聚焦。

这是我的代码(这里是fiddle):



$(document).ready(function() {
  var viewModel = new Grid.ViewModel();
  viewModel.init();
  ko.applyBindings(viewModel);   
});

ko.bindingHandlers.clickToEdit = {
   init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
     if (element.className == "editable") {
        var observable = valueAccessor();
        span = document.createElement("span");
        var input = document.createElement("input");

        element.appendChild(span);
        element.appendChild(input);

        ko.applyBindingsToNode(span, {
          text: observable,
          visible: ko.computed(function() { return !viewModel.isEditState(); }),
        });

        ko.applyBindingsToNode(input, {
          value: observable,
          visible: viewModel.isEditState,
          hasfocus: viewModel.shouldFocus, // this seems to not work (but it works if we assign it with isEditState)
        });
      }
    },
}

var Grid = {
  RowViewModel: function() {
     var self = this;
     self.district = ko.observable();
     self.team = ko.observable();
     self.factory = ko.observable();
     self.isEditState = ko.observable(false);
     self.shouldFocus = ko.computed(function() {
    	return self.isEditState();
     });
    
     self.init = function (data) {
        self.district(data.District);
        self.team(data.Team);
        self.factory(data.Factory);
     }
    
     self.setEditState = function(){
        self.isEditState(!self.isEditState());
     }	
   },
	
  ViewModel: function() {
     var self = this;
     self.data = ko.observableArray();
     self.init = function() {
         var data = self.getData();
         self.data(data);
     }
     self.getData = function() {
       var data = [];
       var rows = [
         { District: "North", Team: "Jim", Factory: "Mars" },
         { District: "South", Team: "John", Factory: "Pluto" },
       ];
       rows.forEach(function (element, index) { 
         var row = new Grid.RowViewModel();
         row.init(element);
         data.push(row); 
       });	

       return data;
     }
  },
}

td, tr > th {
    text-align: center;
    vertical-align: middle;
}

a {
    text-decoration: none !important;
}

input {
  width: 100%;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>


<div class="grid-container">
  <table class="table table-bordered table-condensed" style="table-layout:fixed">
      <thead>
          <tr class="active">
            <th></th>
            <th>District</th>
            <th>Team</th>
            <th>Factory</th>
            <th>Action</th>
          </tr>
      </thead>
      <tbody data-bind="foreach: data">
          <tr>
            <td>
                <input type="checkbox" />
            </td>
            <td data-bind="text: district, clickToEdit: district"></td>
            <td class="editable" data-bind="clickToEdit: team"></td>
            <td data-bind="text: factory, clickToEdit: factory"></td>
          <td>
            <a href="#" data-bind="visible: !isEditState(), click: setEditState">
              <i class="glyphicon glyphicon-pencil"></i>
            </a>
            <a href="#" data-bind="visible: isEditState(), click: setEditState">
              <i class="glyphicon glyphicon-ok"></i>
            </a>
          </td>
        </tr>
      </tbody>
  </table>
</div>
&#13;
&#13;
&#13;

感兴趣的行是:

self.shouldFocus = ko.computed(function() {
    return self.isEditState();
});

注意:虽然我在这里仅使用self.isEditState(),但我实际上使用了一两个表达式,因此需要使用computed

2 个答案:

答案 0 :(得分:2)

在我建议解决方案之前,我想向您展示minimal, complete, and verifiable example问题。注意看看发生了什么变得容易多了?

const vm = {};

vm.visible = ko.observable(false);
vm.hasFocus = ko.computed(vm.visible);
vm.toggle = () => vm.visible(!vm.visible());

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

<input data-bind="visible: visible, hasFocus: hasFocus">
<button data-bind="click: toggle">toggle</button>

从这次重建中我发现:问题可能是一次处理visiblehasFocus更新,而不是给DOM足够的时间来处理它们?毕竟,浏览器无法将焦点添加到隐藏元素......

让我们尝试制作hasFocus计算deferred。延迟计算在事件循环结束时运行,有点像setImmediate(cb)setTimeout(cb, 0)

const vm = {};

vm.visible = ko.observable(false);
vm.hasFocus = ko.computed(vm.visible)
  .extend({ deferred: true });
vm.toggle = () => vm.visible(!vm.visible());

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

<input data-bind="visible: visible, hasFocus: hasFocus">
<button data-bind="click: toggle">toggle</button>

成功!现在,knockout会更新输入的可见性,为DOM提供更新时间,只会添加焦点。

答案 1 :(得分:0)

将您的computed更改为pureComputed,它会有效。

您可以看到纯函数指南here

self.shouldFocus = ko.pureComputed(function() {
    return self.isEditState();
 });