knockout.js - 在轮询更新时帮助处理UI状态更改

时间:2014-06-19 20:17:52

标签: forms data-binding knockout.js

我的观察结果发生变化之后,我遇到了丢失UI状态变化的问题,并希望得到一些建议。

首先,我轮询我的服务器以获取更新。这些消息在我的视图模型中,并且< ul>完美呈现:

demonstration of multiple items rendering

当我的用户点击"回复"或者"分配给"按钮,我显示一个小表格来执行这些操作:

form displayed on button click

我现在的问题是,当我的下一个轮询调用返回时,列表会重新绑定,并且我将丢失表单应该打开的状态。我为" currentQuestionID"添加了视图模型属性。所以我可以使用visible:binding并在绑定后重新显示表单。

完成后,表格会正确显示在"当前项目"重新绑定后,但表单值丢失。也就是说,它重新绑定,重建表单元素,显示它们,但任何用户输入都会消失(这当然是有意义的,因为HTML刚刚重新生成)。

我试图遵循相同的模式(使用值:绑定来设置值和事件:{change:responseChanged}绑定以使用值更新observable)。 HTML片段如下所示:

<form action="#" class="tb-reply-form" data-bind="visible: $root.showMenu($data, 'reply')">
    <textarea id="tb-response" data-bind="value: $root.currentResponse, event: {keyup: $root.responseChanged}"></textarea>
    <input type="button" id="tb-submitResponse" data-bind="click: $root.submitResponse, clickBubble: false" value="Send" />
</form>
<form action="#" class="tb-assign-form" data-bind="visible: $root.showMenu($data, 'assign')">
    <select id="tb-assign" class="tb-assign" data-bind="value: $root.currentAssignee, options: $root.mediators, optionsText: 'full_name', optionsValue: 'access_token', optionsCaption: 'Select one...', event: {change: $root.assigneeChanged}">
    </select>
    <input type="button" id="tb-submitAssignment" data-bind="click: $root.submitAssignment, clickBubble: false" value="Assign"/>
</form>

现在,我最终看起来像一个无限循环,其中设置值会导致更改发生,从而导致值......等等。

我想&#34;搞砸了#34;只需将其移出foreach ...通过移动每个&lt; li&gt;之外的表单。在foreach中:绑定并进行一些DOM操作以将表单移动到&#34;当前项目&#34;,我认为我不会丢失用户输入。

replyForm.appendTo(theContainer).show();

直到第一次轮回返回&amp;重新绑定。由于HTML是为&lt; ul&gt;重新生成的,因此DOM不再具有我的表单,我尝试抓取它并执行.appendTo(容器)不会做任何事情。我想在这里,我可以将元素复制到活动项而不是移动它?

所以,这一切似乎都让我错过了一些基本的东西,因为有人必须在淘汰赛中将表格放入foreach循环中!

是否有人有策略在敲除中的绑定项目内维护表单状态?

或者,可能有一种方法可以让淘汰赛没有绑定任何已经绑定的东西,只会产生新的&#34;元件。

最后,我应该为此废弃淘汰赛并手动生成&#34;新物品&#34;每次轮询电话回来时我自己。

最后一点信息;如果我将我的轮询间隔设置为30秒,所有位&#34;工作&#34;因为它提交,保存,重新绑定等等。我只需要表格及其内容通过重新绑定来实现。

非常感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

好吧,我自己想出来了。而且令人尴尬。

以下是我的VM代码的一部分:

function TalkbackViewModel( id ) {
   var self = this;

   talkback.state.currentTalkbackId = "";
   talkback.state.currentAction = "";
   talkback.state.currentResponse = "";
   talkback.state.currentAssignee = "";

   self.talkbackQueue = ko.observableArray([]);
   self.completeQueue = ko.observableArray([]);
   self.mediators = ko.observableArray([]);

   self.currentTalkbackId = ko.observable(talkback.state.currentTalkbackId);
   self.currentAction = ko.observable(talkback.state.currentAction);
   self.currentResponse = ko.observable(talkback.state.currentResponse);
   self.currentAssignee = ko.observable(talkback.state.currentAssignee);

self.showActionForm = function(data, action) {
    return ko.computed(function() {
        var sameAction = (self.currentAction() == action);
        var sameItem = (self.currentTalkbackId() == data.talkback_id());

        return (sameAction && sameItem);
    }, this);
};

    self.replyToggle = function(model, event) {
    // we're switching from one item to another. clear input values.
    if (self.currentTalkbackId() != model.talkback_id() || self.currentAction() != "reply") {
        self.currentResponse("");
        self.currentAssignee("");
        self.currentTalkbackId(model.talkback_id());
    }

我的第一个错误是试图对待textarea&amp;下拉一样。我注意到下拉列表是节省价值&amp;重新加载,但愚蠢地试图保持代码与textarea相同,并引起我自己的问题。

因此...

首先,我回到使用currentAssignee和currentResponse的$ root视图模型属性来关闭值并使用value:bindings重新绑定这些控件。

接下来,我需要删除事件处理程序:

event: { change: xxxChanged }

因为它们没有意义(双向绑定!!!!)。下拉值使用值:binding。

自动更改和更新

textarea只更新了模糊,导致我认为我需要onkeyup,onkeydown等。我摆脱了那些处理程序,因为它们1)错误,2)搞砸了值:绑定创建一个无限循环。

我只在textarea上需要这个以获取我的viewmodel属性的最新值更新:

valueUpdate: 'input'

在这一点上,一切都得以挽回重新绑定,我没有失去我的价值观,但我的插入位置在textarea中是不正确的。我添加了一些代码来处理:

var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);

如果您只是执行item.focus()。某些浏览器的行为正常.val(item.val());但我需要实际上使价值变为&#34;改变&#34;在我的情况下,最后得到插入符号,所以我保存了值,清除它,然后恢复它。我在事件处理程序中执行了此操作,以便将事件数据返回到浏览器:

    $(window).on("talkback.retrieved", function(event, talkback_queue, complete_queue) {
        var open_mappings = ko.mapping.fromJS(talkback_queue);
        self.talkbackQueue(open_mappings);
        if (talkback_queue) self.queueLength(talkback_queue.length);

        var completed_mappings = ko.mapping.fromJS(complete_queue);
        self.completeQueue(completed_mappings);

        if (self.currentTalkbackId()) {
            var element = $("li[talkbackId='" + self.currentTalkbackId() + "']");
            if (talkback.state.currentAction == "assign") {
                var item = element.find(".tb-assign");
                var oldValue = item.val();
                item.val('');
                item.focus().val(oldValue);
            } else {
                var item = element.find(".tb-response");
                var oldValue = item.val();
                item.val('');
                item.focus().val(oldValue);
            }
        }
    }
);

所以,我的最后一个问题是,如果我在我的方法中使用了我的observable&#34;清除&#34;当一个新的&#34;当前项目&#34;被选中(replyToggle&amp; assignToggle),它们似乎不起作用。

self.currentResponse("");
self.currentAssignee("");

我无法清除值。我不得不做一些hack-fu并在下面添加一行来解决它现在:

$(".tb-assign").val("");