我生活在一个knockout.js子视图模型observableArray
依赖的observable
它们的父视图模型。 父母的observable
被绑定到一个select
下拉,和孩子们需要的时候就知道价值变化。
我已经通过了整个父视图模型,只是observable
,并且还认购母公司内部的observable
,以更新的孩子“手动”。 所有这些工作的结合的目的。
问题是如果我再清除出孩子observableArray
以腾出空间给新来的孩子,或者干脆更换新的孩子的孩子,老孩子在记忆棒周围,因为他们仍然通过knockout.js成立了反依存引用。
我开到任何一个不同的设计模式或方式来告诉淘汰赛停止握住依赖。 在另一个例子中,我试图cleanNode(),但似乎并没有清理这些反向相关性。
码:
function FamilyViewModel(name) {
this.name = name;
this.children = ko.observableArray();
}
function ChildViewModel(firstName, lastName, selectedFamilyObservable) {
this.firstName = ko.observable(firstName);
this.lastName = ko.observable(lastName);
this.fullName = ko.computed(function() {
return this.firstName() + " " + this.lastName();
}, this);
this.isSelected = ko.computed(function() {
return selectedFamilyObservable().name == this.lastName();
}, this);
}
function PageViewModel() {
this.families = ko.observableArray([
new FamilyViewModel("Smith"),
new FamilyViewModel("Jones"),
new FamilyViewModel("Brown")
]);
this.selectedFamily = ko.observable();
this.addChild = _.bind(function() {
this.selectedFamily().children.push(new ChildViewModel("Frank", this.selectedFamily().name, this.selectedFamily));
}, this);
this.resetChildren = _.bind(function() {
this.selectedFamily().children([]);
}, this);
}
$(function() {
ko.applyBindings(new PageViewModel());
});
小提琴:
http://jsfiddle.net/cygnl7/xhzkC/1/
小提琴解决方案参见前面。
你的问题的关键是,你要不仅跟踪当前选定的家庭(的.selectedFamily
),但你也想,如果他们选择要知道每个家庭。 我认为,最简单的办法是订阅selectedFamily
它改变之前监视它的价值和它的变化来设定家庭的后isSelected
值。 我喜欢这种方法,因为没有必要通过系列阵列循环才能做到这一点。
self.selectedFamily = ko.observable();
self.selectedFamily.subscribe(function (oldValue) {
if (oldValue) {
oldValue.isSelected(false);
}
}, null, 'beforeChange');
self.selectedFamily.subscribe(function (newValue) {
if (newValue) {
newValue.isSelected(true);
}
});
订阅默认功能通过新值后的值已经改变。 在这里,您将有机会获得新选择的家庭,你可以将其设置isSelected
属性为true。 但是,订阅有两个内置在得到由默认调用的主题 。 首先是“改变”的话题,我们刚才讨论的,另一种是“beforeChange”。 这得到了价值变动之前打来电话,让你执行任何业务逻辑之前的值是即将改变,像一个设置有机会isSelected
标志设置为false。
----------
请注意,这接下来的部分是一些考虑我的经历和额外的信息。 以上是适当的解决方案,但对于那些谁想要知道更多,请继续阅读...
----------
另一种方法是使selectedFamily
读/写计算,像这样的私人观察到的后盾:
var _selectedFamily = ko.observable();
self.selectedFamily = ko.computed({
read: _selectedFamily,
write: function (newValue) {
var oldValue = _selectedFamily();
if (oldValue) {
oldValue.isSelected(false);
}
if (newValue) {
newValue.isSelected(true);
}
_selectedFamily(newValue);
}
});
我怎么有这样的设置是不是防弹但应该在这里给你概念的想法。 这是个不错的办法,而且是一个我会用,如果我有需要,使基于当前及未来价值的决定将决定设置计算的结果的结果去了。 但是,在这种情况下,我没有设定的粉丝isSelected
在这里的标志,因为它的结果值无关。 假设有必要建立一个计算,以容纳该确定时设置,我仍然会与订阅解决方案选择,如前面所描述的值的结果逻辑,但在私下里支持_selectedFamily
观察到,在关机的机会,有需要设置的私人背部观察到的值,而通过计算去。
另一块,我在代码中解决的问题是按钮上的点击绑定。 我裹着他们在一个with
约束力的。 绑定到功能绑定在默认情况下传递到他们当前上下文的对象。 现在,而不必这样做:
self.addChild = function () {
var selectedFamily = self.selectedFamily();
// ...code...
};
你可以做:
self.addChild = function (selectedFamily) {
};
我还移动的<span data-bind="visible: isSelected">| Selected!</span>
<span data-bind="visible: isSelected">| Selected!</span>
结合了孩子们的环境并进入家庭背景,因为我觉得这是什么用意。 这似乎不可思议,你就会有“| isSelected”每个孩子,而不是姓氏,但是旁边的文字,如果按每个孩子想要它,你可以这样做:
<li data-bind="text: fullName"><span data-bind="visible: $parent.isSelected">| Selected!</span></li>
关于KO的最后一个功能foreach
结合与具有数据/作为属性的对象绑定。 它可以使你的标记更可读的,真正与访问父链帮助。 比方说,我们也想用家里的isSelected
标志来显示由孩子的名字的东西。 前面我们已经使用引用$parent
。 在某些情况下,你有多重嵌套foreach
绑定,你可能会发现自己写的东西像<span data-bind="text: $parents[2].name"></span>
其给出了关于你的背景资料很少尝试引用。 使用这种方法,你可以代替写:
<div data-bind="foreach: { data: $root.families, as: 'family' }">
<ul data-bind="foreach: { data: $family.children, as: 'child }>
<li>
<span data-bind="text: child.firstName">
<span data-bind="text: family.name">
</li>
</ul>
</div>
通过这种方式,你甚至不会需要一个计算写出来的第一和lastName。 当我想到我将有一个复杂的嵌套上下文结合结构和/或当我想作一个简单的嵌套结构似乎更可读的,我会用这种方法。
http://jsfiddle.net/34ZjB/1/
检查我的意见,因为我认为他们会帮你一下。
我做了一些修改,以你的小提琴。 我不明白为什么你列入强调,因为它并不需要绑定的事件。 此外,您childViewModel和familyViewModel的都是实际上只是模型。 对家庭IsSelected属性只是一个额外的约束力每次改变selectedFamily时候你不得不走出去,做额外的更新的意思。 最后,如果你想给孩子重设selectedFamily而已,只是删除ko.utils.arrayForEach功能,只是做selectedFamily()儿童([])。 我不知道你为什么会想只重设所选家庭的儿童,但它是很容易做到的。
需要代码 -
var addChild = function () {
self.selectedFamily().push(new childModel(self.selectedFamily().name());
};
http://jsfiddle.net/xhzkC/2/