从多个DOM元素设置ko.observable值

时间:2013-08-01 13:25:13

标签: knockout.js

Example jsFiddle


鉴于我有一个问题列表,在一个模型中使用控件类型(即文本框,选择列表,复选框等),该模型是通过asmx web方法检索的JSON中使用ko.mapping构建的,如何根据多个DOM元素设置问题的答案?

说我有以下问题:

{
    text:"This is the question text",
    answer:null,
    controlType:"picker"
}

然后我创建了一个picker模板,如下所示:

<select data-bind="options: ['Large','Medium','Small'], optionsCaption: 'Select size...'" />
<select data-bind="options: ['Black','Red','Green'], optionsCaption: 'Select colour...'" />

当两个SELECT元素都选择了值时,我需要将answer设置为这个问题。

我还需要保留答案,所以如果question从服务器返回并填充了答案(保存表单提交等),则在SELECT元素中选择相应的值,即:

{
    text:"This is the question text",
    answer:"Large Red",
    controlType:"picker"
}

这将为我选择元素中的[Large]和[Red]。

这是我到目前为止所尝试的内容。

在我的observable

上设置ko.computed

编辑:这是一个working jsFiddle for this想法。

我已经在我的视图模型中回答问题,并在每个问题上设置ko.computed,通过ID访问DOM,因此我的模板变为:

<select data-bind="attr: { 'id':'leftOption' }, options: ['Large','Medium','Small'], optionsCaption: 'Select size...'" />
<select data-bind="attr: { 'id':'rightOption' }, options: ['Black','Red','Green'], optionsCaption: 'Select colour...'" />

我的计算值变为:

question.computedAnswer = ko.computed({
    read: function () {
        return question.answer();
    },
    write: function (value) {
        var left = $('#leftOption').val();
        var right = $('#rightOption').val(); 
        question.answer(left + ' ' + right);
    }
});

这只是尖叫哦,上帝,拜托,不!不过!对我来说,因为我正在以非常混乱的方式访问DOM(并且最近学会了一个很好的做法,就是不要像这样访问DOM ,感觉各种错误!!)。

创建自定义绑定处理程序

警告:我仍然要掌握自定义处理程序

为了通过绑定处理程序限制对DOM的访问,我为此创建了一个...但似乎无法让它工作。这个想法出现在provided jsFiddle中,但它并没有起作用,因为它没有做任何事情!

基本上我正在尝试在处理程序中执行此操作:

ko.bindingHandlers.computedAnswer = {
    update: function (element, value, all, model, context) {
        switch (model.controlType()) {
            case 'picker': {
                var $parent = $(element).parent();
                var first = $parent.find('select:first').val();
                var second = $parent.find('select:last').val();
                if (first && second) {
                    model.answer(first + ' ' + second);
                } else {
                    model.answer(null);
                }
            }
        }
    }
};

尝试在我的模板上激活它,如下所示:

<select data-bind="computedAnswer: $data, options: ['Large','Medium','Small'], optionsCaption: 'Select size...'" />
<select data-bind="computedAnswer: $data, options: ['Black','Red','Green'], optionsCaption: 'Select colour...'" />    

有没有人知道如何让这个工作?

1 个答案:

答案 0 :(得分:1)

视图是存储数据的错误位置。它也不是很灵活。如果您的picker个问题存储了自己的选项怎么办?这不仅会让每个问题都有不同的选项,而且会有不同的数字选项。

它还保存视图的数据,并简化您的HTML和viewmodel代码,并保持DOM访问不受视图模型的影响,而无需自定义绑定(不应该避免自定义绑定所有的费用,我只是觉得这不是他们的好例子。)

A picker 问题

}, {
    text: "dependent on master question",
    answer: null,
    controlType: "picker",
    pickers: [
        { name: "Size", options: ['Large', 'Medium', 'Small'], value: '' },
        { name: "Color", options: ['Red', 'Blue', 'Green'], value: '' }
    ]
}

查看

<script type="text/html" id="picker">
    <!-- ko foreach: pickers -->
    <label data-bind="text: name"></label>
    <select data-bind="options: options, value: value, optionsCaption: 'Choose...'"></select>
    <!-- /ko -->
</script>

计算出的答案

viewModel.questions().forEach(function (question) {
    if (question.controlType() === 'picker') {
        question.answer = ko.computed(function() {
            var answers = [];
            question.pickers().forEach(function(i) {
                if (i.value())
                    answers.push(i.value());
            });
            return answers.length === question.pickers().length ?
                answers.join(' ') : '';
        });
    }
});

The Fiddle