从HTML标记中为observable赋予初始值

Giving initial value to observable from the HTML markup

本文关键字:observable HTML      更新时间:2023-09-26

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

以下是我基本上在做的事情:

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

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被设置为一个空字符串,我认为这是因为声明可观察性的方式。但是,有没有一种方法可以推翻这种行为或其他什么?

这里有点晚了。事实上,我对RP的回答并不满意,因为它打破了Knockout的声明性。特别是,如果使用valueWithInit定义属性,则不能在早期绑定中使用。这是他的一把叉子。

您以相同的方式使用它,但它仍然是文档范围内的声明性文件:

<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

在jsfiddle上看到它的作用。

您可以使用一个自定义绑定来保持代码的整洁,该绑定通过使用元素的当前值初始化值绑定来封装值绑定。

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

绑定可能看起来像:

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

如果自定义绑定太重,那么一个简单的解决方案是从DOM初始化可观察对象。

例如,给定以下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)
});

您可以简单地使用自定义绑定并将值分配给现有的可观测值:

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

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