我有简单的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,但它似乎无法工作。
答案 0 :(得分:1)
您的代码存在一些问题。问题不一定是可观察数组 - 这是您使用组件的问题。组件的目的列在here中。有些事情需要注意:
observable
。所以不需要var vm = ko.observable(new AppViewModel());
只需使用var vm = new AppViewModel();
并推送到您的阵列。但是,如果你想使用一个组件,那么这也不正确!正在做的是基本上两次推出AppViewModels!你能明白为什么吗?你在上面的行中明确地执行了一次,然后组件隐式地执行一次。每次创建组件时,它都与AppViewModel相关联 - 这是注册组件时发生的情况。 <script></script>
标记的内部复制并粘贴到<li><li>
中,它应该可以正常工作。我不确定这是否是您正在寻找的答案,但这应该做您想要的。如果您愿意,可以使用组件,但是您必须进行更多更改并且需要一些不必要的复杂性。我冒昧地改变你的变量名。我认为他们可能更合适。希望这有帮助!
修改强>
我已编辑代码段以使用组件。这个特定实现的问题是组件现在通过名称observableArray与AppViewModel
紧密耦合。在您的情况下这可能没问题,但通常这会导致不良行为,尤其是一旦您的视图模型开始变得更大和更复杂。当然,你可以传递firstName
和lastName
的展开值,以便更好地解耦,但是你会回到正方形,因为<pre data-bind="text: ko.toJSON(names, null, 2)"></pre>
不会注册任何更改取名为observableArray。
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;