Javascript 加载时击倒计算属性火灾
我从knockout开始,当viewmodel被实例化时,我的computed observative似乎总是启动,我不知道为什么 为了测试,我把这个问题简化为荒谬的问题:computed属性只是在控制台中打印一条消息,它没有绑定到DOM中的任何元素。这是:Javascript 加载时击倒计算属性火灾,javascript,knockout.js,Javascript,Knockout.js,我从knockout开始,当viewmodel被实例化时,我的computed observative似乎总是启动,我不知道为什么 为了测试,我把这个问题简化为荒谬的问题:computed属性只是在控制台中打印一条消息,它没有绑定到DOM中的任何元素。这是: (function() { function HomeViewModel() { var self = this; (...) self.FullName = ko.computed(functi
(function() {
function HomeViewModel() {
var self = this;
(...)
self.FullName = ko.computed(function () {
console.log("INSIDE");
});
(...)
};
ko.applyBindings(new HomeViewModel());
})();
如何避免呢
更新:
以下是ViewModel的完整代码,仅供您更好地理解:
function HomeViewModel() {
var self = this;
self.teachers = ko.observableArray([]);
self.students = ko.observableArray([]);
self.FilterByName = ko.observable('');
self.FilterByLastName = ko.observable('');
self.FilteredTeachers = ko.observableArray([]);
self.FilteredStudents = ko.observableArray([]);
self.FilteredUsersComputed = ko.computed(function () {
var filteredTeachers = self.teachers().filter(function (user) {
return (user.name.toUpperCase().includes(self.FilterByName().toUpperCase()) &&
user.lastName.toUpperCase().includes(self.FilterByLastName().toUpperCase())
);
});
self.FilteredTeachers(filteredTeachers);
var filteredStudents = self.students().filter(function (user) {
return (user.name.toUpperCase().includes(self.FilterByName().toUpperCase()) &&
user.lastName.toUpperCase().includes(self.FilterByLastName().toUpperCase())
);
});
self.FilteredStudents(filteredStudents);
$("#LLAdminBodyMain").fadeIn();
}).extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 800 } });
self.FilteredUsersComputed.subscribe(function () {
setTimeout(function () { $("#LLAdminBodyMain").fadeOut(); }, 200);
}, null, "beforeChange");
$.getJSON("/api/User/Teacher", function (data) {
self.teachers(data);
});
$.getJSON("/api/User/Student", function (data) {
self.students(data);
});
}
ko.applyBindings(new HomeViewModel());
})()
我需要它不要在加载时执行,因为加载时,self.students
和self.teachers
数组未被填充
注意:只想强调的是,在这两个代码(荒谬和完整)中,计算属性都是在加载时执行的(或者在ViewModel第一次实例化时执行)。在您的方法中有两个主要错误
- 你有一个单独的可观察的过滤用户。那没必要。
将填充该角色,无需将计算结果存储在任何位置。(computed是缓存的,它们在内部存储自己的值。重复调用computed不会重新计算其值。)ko.computed
- 您正在从视图模型与DOM交互。这通常应该避免,因为它将viewmodel与视图相耦合。viewmodel应该能够在不知道如何渲染的情况下运行
- 不要限制你的过滤结果。速率限制包含筛选器字符串的可观察值
- 不要调用您的计算属性
-这与您的观点无关,没有理由指出它。对于视图中的所有实际用途,计算值和观察值是完全相同的…computed
- 如果教师和学生是同一件事,即要在同一列表中显示的用户对象,为什么要将它们放在两个单独的列表中?在viewmodel中只有一个列表,这样就不需要进行两次筛选,这样会更有意义吗
- 可观测的是函数。这意味着
$.getJSON(“…”,函数(数据){someObservable(数据)})代码>
可缩短为
$.getJSON(“…”,一些可观察的)代码>
function HomeViewModel() {
var self = this;
self.teachers = ko.observableArray([]);
self.students = ko.observableArray([]);
self.filterByName = ko.observable().extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 800 } });
self.filterByLastName = ko.observable().extend({ rateLimit: { method: "notifyWhenChangesStop", timeout: 800 } });
function filterUsers(userList) {
var name = self.filterByName().toUpperCase(),
lastName = self.filterByLastName().toUpperCase(),
allUsers = userList();
if (!name && !lastName) return allUsers;
return allUsers.filter(function (user) {
return (!name || user.name.toUpperCase().includes(name)) &&
(!lastName || user.lastName.toUpperCase().includes(lastName));
});
}
self.filteredTeachers = ko.computed(function () {
return filterUsers(self.teachers);
});
self.filteredStudents = ko.computed(function () {
return filterUsers(self.students);
});
self.filteredUsers = ko.computed(function () {
return self.filteredTeachers().concat(self.filteredStudents());
// maybe sort the result?
});
$.getJSON("/api/User/Teacher", self.teachers);
$.getJSON("/api/User/Student", self.students);
}
有了这一点,立即计算已计算的值就不再重要了。您可以将视图绑定到filteredTeachers
、filteredStudents
或filteredUsers
,视图将始终反映事件状态
当涉及到让用户界面元素对viewmodel状态更改做出反应时,无论反应是“更改HTML”还是“淡入/淡出”都没有区别。这不是viewmodel的工作。这始终是绑定的任务 如果没有满足您需要的“库存”绑定。这一条直接来自: 它在绑定元素中淡入/淡出,具体取决于绑定值。空数组
[]
的计算结果为false是很实际的,因此您可以在视图中执行此操作:
在绑定值更改前后淡入元素的自定义绑定如下所示
- 我们在绑定的
阶段订阅值init
- 绑定中没有
阶段,它需要做的一切都由订阅完成update
- 当DOM元素消失时(例如,因为会触发较高的
或if
绑定),那么我们的绑定也会清理订阅foreach
ko.bindingHandlers.fadeDuringChange = {
init: function(element, valueAccessor) {
var value = valueAccessor();
var beforeChangeSubscription = value.subscribe(function () {
$(element).delay(200).fadeOut();
}, null, "beforeChange");
var afterChangeSubscription = value.subscribe(function () {
$(element).fadeIn();
});
// dispose of subscriptions when the DOM node goes away
// see http://knockoutjs.com/documentation/custom-bindings-disposal.html
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
// see http://knockoutjs.com/documentation/observables.html#explicitly-subscribing-to-observables
beforeChangeSubscription.dispose();
afterChangeSubscription.dispose();
});
}
};
用法同上:
您正在寻找的是这一问题的可能重复:您能解释一下为什么计算机立即“触发”是一个问题吗?我用更多的代码更新了这个问题,只是为了让您更好地理解我为什么要问这个问题以及可能解决方案的限制。谢谢。是的,你的方法有几个错误。让我写一个更好的。谢谢你,我已经尝试过你的解决方案,它对我来说不起作用。fadeVisible绑定检查valueAccesor(filteredUsers)是否真实,并且始终如此,因此始终处于淡入状态。我需要它在filteredUsers更新之前淡出,在更新之后淡出。我能找到的唯一解决方案是我的代码的方式:订阅函数中的淡入在应用任何更改之前以中间方式执行,而在filteredUsers结束其rateLimit超时后淡出。无论如何,谢谢你的建议。我已经添加了一个绑定,看看。你上次的更新效果很好。我只需要在“init”的开头添加一个“$(element).hide()”,以避免在触发淡出之前短暂地显示一个空列表,但效果很好。所以,虽然我不能将这个答案标记为正确的,因为它没有解释如何避免计算机在加载时开火,但你帮了我很多,让我开始使用击倒。所以我只想谢谢你。敬礼!“如何防止计算机立即开火”是一个错误的问题,这就是为什么我没有回答它,而是解决了实际问题。有很多方法可以治疗这种症状(通过使用惰性评估或纯计算,就像其他一些评论所建议的那样),但实际上这里没有必要这样做。只要从正确的角度来处理问题,就不会有任何奇怪的行为需要纠正。这就是我想指出的,所以我认为这是一个答案。
ko.bindingHandlers.fadeDuringChange = {
init: function(element, valueAccessor) {
var value = valueAccessor();
var beforeChangeSubscription = value.subscribe(function () {
$(element).delay(200).fadeOut();
}, null, "beforeChange");
var afterChangeSubscription = value.subscribe(function () {
$(element).fadeIn();
});
// dispose of subscriptions when the DOM node goes away
// see http://knockoutjs.com/documentation/custom-bindings-disposal.html
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
// see http://knockoutjs.com/documentation/observables.html#explicitly-subscribing-to-observables
beforeChangeSubscription.dispose();
afterChangeSubscription.dispose();
});
}
};