从动态创建的输入元素更新observable

时间:2013-08-30 15:04:41

标签: knockout.js

Example jsFiddle


以下是JSON格式的模型结构概述

var json = JSON.stringify([{
    text: 'Enter your name',
    controlType: 'TextBox',
    answer: null
}, {
    text: 'Choose some of these',
    controlType: 'Label',
    answer: null
}, {
    text: 'Item one',
    controlType: 'CheckBox',
    answer: null
}, {
    text: 'Item two',
    controlType: 'CheckBox',
    answer: null
}, {
    text: 'Item three',
    controlType: 'CheckBox',
    answer: null
}, {
    text: 'Choose from multiple elements',
    controlType: 'Multiple',
    answer: null
}, ]);

我有一个问题列表(详见上文),每个问题都有各自的textcontrolType属性。我已经创建了一个自定义绑定处理程序来绘制表单上的输入,另一个处理程序根据创建的控件的DOM结构处理answer

例如,对于一种特定的控件类型,我将 Multiple 作为类型,这将创建以下标记:

<select data-bind="options: ['Jan','Feb','Mar'...], optionsCaption: ' - '"></select>
<input type="text" size="5" />

这实际上允许用户提交单个问题的答案 2013年1月,但我很难让绑定处理程序工作。

当我第一次开始这样做的时候,我用计算的observable扩展了每个问题 - 所以在这种情况下我会使用以下计算扩展question

question.answer.month = ko.computed({
    read: function () {
        if (this.answer()) {
            return this.answer().split(' ')[0]; // returns the month part
        }
        return null;
    },
    write: function (value) {
        if (value) {
            this.answer(value);
        }
    },
    owner: question
});

但是我开始进入各种循环引用地狱 - 所以我想我会把它提升到一个级别,然后从DOM元素中设置我的answer observable。

有没有人有任何想法/提示让这个工作?

更多信息

似乎我之前没有解释得太好,所以我会稍微努力一点。

我的每个question对象都有一个名为controlType的属性。这表示应在页面上绘制DOM元素。因此,对于多个(作为示例),我应该具有以下DOM元素:

<div class="question">
    <label class="question-label" data-bind="text: $data.text"></label>
    <select data-bind="value: $data.answer.month, options: [month names]"></select>
    <input type="text" data-bind="value: $data.answer.year" />
</div>

这会让用户回答一个问题 - 假设问题文本可能就像你什么时候出生?那么上面的DOM结构允许他们提交月份和年份的答案,即 1990年1月。现在,在后台我需要从这两个DOM元素中获取值并将它们合并为一个单独的答案来设置question.answer属性。我尝试了两个计算的observable question.answer.monthquestion.answer.year但是当我的控件类型变得更复杂时,我进入循环引用地狱,即我的答案取决于它的对应部分,每个对应部分依赖于一个解析的答案。

简而言之 - 我正在尝试根据未指定数量的DOM元素设置一个可观察值,在这个(最简单的)情况下,它是SELECTINPUT元素。

1 个答案:

答案 0 :(得分:1)

这个答案提出了另一种解决方案。

首先,使用template binding替换自定义绑定处理程序的用法。您的模板将如下所示:

<script type="text/html" id="Label">
    <h4 data-bind="value: answer"></h4>
</script>

<script type="text/html" id="TextBox">
    <input type="text" data-bind="value: answer" />
</script>

<script type="text/html" id="CheckBox">
    <input type="checkbox" data-bind="checked: answer, processControl: $data" />
</script>

<script type="text/html" id="Multiple">
    <select data-bind="value: answer, options: opts, optionsCaption: '-'"></select>
    <input type="text" data-bind="value: answerAddition" />
</script>

这可能是主要问题和主要问题。以下是关于你所拥有的“多重”问题类型的一些想法,但我强烈建议你更多地研究这个问题,如果你遇到麻烦问一个孤立的问题,因为如果你有一个问题,那么SO往往效果最好。时间。

PS。在this fiddle中查看所有这些内容。请注意,jQuery几乎不再使用,这通常是一个好兆头(至少是你使用KO的标志)。

我认为您可能需要更多地处理您所拥有的“复杂”/多重问题类型。以上是IMO正确方向的一个步骤,还需要对数据和视图模型进行一些更改:

  • 数据将包含选项,例如
    opts: ['Jan', 'Feb', 'Mar' /*etc*/]

  • 视图模型将包含类似的内容 self.opts = ko.observableArray(data.opts || []);

  • 视图模型还包含额外的可观察值:
    self.answerAddition = ko.observable('');

  • 以及可能的只读计算了returs:
    self.answer() + ' ' + self.answerAddition()

但是,我认为你可以从更进一步的步骤中受益,也许创建一些基于原型的继承并创建一个专门的问题构造函数来表示“月+年”。