3

示例 jsFiddle


鉴于我有一个问题列表,模型中的控件类型(即文本框、选择列表、复选框等)是ko.mapping从通过 asmx Web 方法检索的 JSON 构建的,我如何将答案设置为基于多个 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...'" />

answer当(且仅当)两个 SELECT 元素都选择了一个值时,我需要设置这个问题。

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

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

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

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

ko.computed在我的 observable 上设置 a

编辑:这是一个适用于这个想法的jsFiddle 。

我在我的视图模型中循环了问题,并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 的访问,我为此创建了一个......但似乎无法让它工作。这个想法在提供的 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...'" />    

有谁知道如何让它工作?

4

1 回答 1

1

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

它还保留视图的数据,并简化您的 HTML 和 viewmodel 代码,并在不需要自定义绑定的情况下将 DOM 访问保留在 viewmodel 之外(并不是说您应该不惜一切代价避免自定义绑定,我只是不这么认为对他们来说是一个很好的例子)。

一个 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(' ') : '';
        });
    }
});

小提琴

于 2013-08-01T19:40:06.577 回答