javascript 从 HTML 标记中为 observable 赋予初始值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12125143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Giving initial value to observable from the HTML markup
提问by Kassem
I'm trying to create an HtmlHelper
extension that outputs some HTML to the view. In this HTML I'm wiring up some KnockoutJS binding. I'm new to KO so I'm still struggling in getting some things done. Anyway, what I'm trying to do is generate input fields (in the server-side code) bound to observables on my client-side code, then set the initial values of the observables through the value of the hidden fields. Unfortunately, that is not working for me. So I'm wondering if there any way I could get this done (even if I have to do it completely different).
我正在尝试创建一个HtmlHelper
将一些 HTML 输出到视图的扩展。在这个 HTML 中,我连接了一些 KnockoutJS 绑定。我是 KO 的新手,所以我仍在努力完成一些事情。无论如何,我想要做的是生成绑定到我的客户端代码上的 observables 的输入字段(在服务器端代码中),然后通过隐藏字段的值设置 observables 的初始值。不幸的是,这对我不起作用。所以我想知道是否有任何方法可以完成这项工作(即使我必须完全不同)。
Here's what I'm basically doing:
这是我主要做的事情:
In my client side view model I have the following:
在我的客户端视图模型中,我有以下内容:
self.dataSource = ko.observable();
self.pageSize = ko.observable();
And my extension method outputs the following:
我的扩展方法输出以下内容:
<input type="hidden" value="/Employee/Get" data-bind="value: dataSource" />
<input type="hidden" value="30" data-bind="value: pageSize" />
But when the page renders, when I inspect the elements I notice that the value
of the input fields is being set to an empty string, which I believe is because of the way observables are being declared. But is there a way to override this behavior or something?
但是当页面呈现时,当我检查元素时,我注意到value
输入字段的 被设置为一个空字符串,我相信这是因为 observables 的声明方式。但是有没有办法覆盖这种行为?
采纳答案by RP Niemeyer
One alternative you can use to keep your code a bit cleaner is to use a custom binding that wraps the value binding by initializing it with the element's current value.
您可以用来使代码更简洁的一种替代方法是使用自定义绑定,该绑定通过使用元素的当前值对其进行初始化来包装值绑定。
You can even have it create observables on your view model, if they don't exist.
你甚至可以让它在你的视图模型上创建 observables,如果它们不存在的话。
The binding might look something like:
绑定可能类似于:
ko.bindingHandlers.valueWithInit = {
init: function(element, valueAccessor, allBindingsAccessor, data) {
var property = valueAccessor(),
value = element.value;
//create the observable, if it doesn't exist
if (!ko.isWriteableObservable(data[property])) {
data[property] = ko.observable();
}
data[property](value);
ko.applyBindingsToNode(element, { value: data[property] });
}
};
You would use it like:
你会像这样使用它:
<input value="someValue" data-bind="valueWithInit: 'firstName'" />
Note that the property name is in quotes, this allows the binding to create it, if it does not exist rather than error out from an undefined value.
请注意,属性名称在引号中,这允许绑定创建它,如果它不存在而不是从未定义的值中出错。
Here is a sample: http://jsfiddle.net/rniemeyer/BnDh6
这是一个示例:http: //jsfiddle.net/rniemeyer/BnDh6
回答by Joe
A little late here. I wasn't actually satisfied with RP's answer, because it breaks the declarative nature of Knockout. Specifically, if you use valueWithInit to define your property, you can't useit in an earlier binding. Here's a fork of his jsfiddle to demonstrate.
这里有点晚了。我实际上对 RP 的回答并不满意,因为它打破了 Knockout 的声明性。具体来说,如果您使用 valueWithInit 来定义您的属性,则不能在早期绑定中使用它。这是他的 jsfiddle的一个分支来演示.
You use it the same way, yet it's still document-wide declarative under the hood:
你以同样的方式使用它,但它仍然是引擎盖下的文档范围的声明:
<input data-bind="valueWithInit: firstName" value="Joe" />
Extending on the idea, you can also use this to separate the initialization and the binding:
扩展这个想法,您还可以使用它来分离初始化和绑定:
<input data-bind="initValue: lastName, value: lastName" value="Smith" />
This is a little redundant, but becomes useful when you're using a plugin instead of the built-in bindings:
这有点多余,但是当您使用插件而不是内置绑定时会变得有用:
<input data-bind="initValue: lastName, myPlugin: lastName" value="Smith" />
Expanding a little more, I also needed a way to initialize checkboxes:
再扩展一点,我还需要一种方法来初始化复选框:
<input type="checkbox" data-bind="checkedWithInit: isEmployed" checked />
And here are the handlers:
这是处理程序:
ko.bindingHandlers.initValue = {
init: function(element, valueAccessor) {
var value = valueAccessor();
if (!ko.isWriteableObservable(value)) {
throw new Error('Knockout "initValue" binding expects an observable.');
}
value(element.value);
}
};
ko.bindingHandlers.initChecked = {
init: function(element, valueAccessor) {
var value = valueAccessor();
if (!ko.isWriteableObservable(value)) {
throw new Error('Knockout "initChecked" binding expects an observable.');
}
value(element.checked);
}
};
ko.bindingHandlers.valueWithInit = {
init: function(element, valueAccessor, allBindings, data, context) {
ko.applyBindingsToNode(element, { initValue: valueAccessor() }, context);
ko.applyBindingsToNode(element, { value: valueAccessor() }, context);
}
};
ko.bindingHandlers.checkedWithInit = {
init: function(element, valueAccessor, allBindings, data, context) {
ko.applyBindingsToNode(element, { initChecked: valueAccessor() }, context);
ko.applyBindingsToNode(element, { checked: valueAccessor() }, context);
}
};
Notice valueWithInit
simply uses initValue
under the hood.
注意valueWithInit
只是initValue
在引擎盖下使用。
See it in action on jsfiddle.
回答by Mark Hobson
If a custom binding is too heavyweight then a simple solution is to initialize the observables from the DOM.
如果自定义绑定太重,那么一个简单的解决方案是从 DOM 初始化 observables。
For example, given the following HTML form:
例如,给定以下 HTML 表单:
<form name="person">
<input type="text" name="firstName" value="Joe" data-bind="value: firstName"/>
</form>
Then Knockout could be initialized as follows:
然后可以按如下方式初始化 Knockout:
ko.applyBindings({
firstName: ko.observable(document.forms['person']['firstName'].value)
});
回答by Slim
You can simply use a custom bindingand assign the values to the existing observables:
您可以简单地使用自定义绑定并将值分配给现有的 observables:
ko.bindingHandlers.yourBindingName = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
viewModel.dataSource($(".dataSource").val());
viewModel.pageSize($(".pageSize").val());
}
};
Then, just add a class, or an Id to the inputs and data-bind="yourBindingName"
to the HTML element containing them.
然后,只需将一个类或一个 Id 添加到输入和data-bind="yourBindingName"
包含它们的 HTML 元素中。