我有一个使用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>
感兴趣的行是:
self.shouldFocus = ko.computed(function() {
return self.isEditState();
});
注意:尽管我在这里只使用
self.isEditState()
,但实际上我会再使用一个或两个表达式,因此需要使用computed
。 最佳答案
在提出解决方案之前,我想向您介绍问题的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>
通过这种重构,我发现:问题可能是同时处理
visible
和hasFocus
更新,而没有给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>
成功!现在,敲除会更新输入的可见性,给DOM时间更新,然后才添加焦点。