从HTML标记中为observable提供初始值

时间:2012-08-25 19:49:56

标签: javascript data-binding knockout.js observable

我正在尝试创建一个HtmlHelper扩展名,将一些HTML输出到视图中。在这个HTML中,我正在连接一些KnockoutJS绑定。我是KO的新手,所以我仍然在努力完成一些事情。无论如何,我要做的是生成绑定到客户端代码上的observable的输入字段(在服务器端代码中),然后通过隐藏字段的值设置observable的初始值。不幸的是,这对我不起作用。所以我想知道我是否有办法完成这项任务(即使我必须完全不同)。

这是我基本上在做的事情:

在我的客户端视图模型中,我有以下内容:

self.dataSource = ko.observable();
self.pageSize = ko.observable();

我的扩展方法输出以下内容:

<input type="hidden" value="/Employee/Get" data-bind="value: dataSource" />
<input type="hidden" value="30" data-bind="value: pageSize" />

但是当页面渲染时,当我检查元素时,我注意到输入字段的value被设置为空字符串,我认为这是因为可以观察到observable的方式。但有没有办法覆盖这种行为或其他什么?

4 个答案:

答案 0 :(得分:23)

这里有点晚了。我对RP的回答并不满意,因为它打破了Knockout的声明性质。具体来说,如果使用valueWithInit定义属性,则不能在早期绑定中使用它。这是一个fork of his jsfiddle to demonstrate

你以同样的方式使用它,但它仍然是文档范围内的声明:

<input data-bind="valueWithInit: firstName" value="Joe" />

扩展这个想法,你也可以使用它来分离初始化和绑定:

<input data-bind="initValue: lastName, value: lastName" value="Smith" />

这有点多余,但在使用插件而不是内置绑定时会变得有用:

<input data-bind="initValue: lastName, myPlugin: lastName" value="Smith" />

扩展一点,我还需要一种初始化复选框的方法:

<input type="checkbox" data-bind="checkedWithInit: isEmployed" checked />

以下是处理程序:

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);
    }
};

注意valueWithInit只是简单地使用initValue

in action on jsfiddle

答案 1 :(得分:21)

可以用来保持代码更清晰的一种替代方法是使用自定义绑定来包装值绑定,方法是使用元素的当前值初始化它。

如果视图模型不存在,您甚至可以在视图模型上创建可观察对象。

绑定可能类似于:

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] });
    }
};

您可以使用它:

<input value="someValue" data-bind="valueWithInit: 'firstName'" />

请注意,属性名称是引号,如果它不存在而不是从未定义的值中输出错误,则允许绑定创建它。

以下是一个示例:http://jsfiddle.net/rniemeyer/BnDh6

答案 2 :(得分:2)

如果自定义绑定太重,那么一个简单的解决方案就是从DOM初始化observable。

例如,给定以下HTML表单:

<form name="person">
  <input type="text" name="firstName" value="Joe" data-bind="value: firstName"/>
</form>

然后可以按如下方式初始化Knockout:

ko.applyBindings({
  firstName: ko.observable(document.forms['person']['firstName'].value)
});

答案 3 :(得分:1)

您只需使用custom binding并将值分配给现有的observables:

ko.bindingHandlers.yourBindingName = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        viewModel.dataSource($(".dataSource").val());
        viewModel.pageSize($(".pageSize").val());
    }
};

然后,只需在输入中添加一个类或Id,并在包含它们的HTML元素中添加data-bind="yourBindingName"