0

Knockout マッピング プラグインを使用して、3 秒ごとにサーバーから取得した JSON で UI を更新しています。UI は、いくつかのネストされたforeachバインディングで構成されています。ただし、何も変更されていない場合でも、すべてのforeachバインディングのすべてが完全に削除され、更新のたびに再レンダリングされているようです。

var testData = {
    Answers: [],
    Inspectable: {
        Categories: [{
            Id: 1,
            Name: "Test Category",
            Questions: [{
                Id: 1,
                Text: "Test Question",
                Active: true,
                Answers: [{
                    Text: "Test Answer",
                    Id: 1
                }]
            }]
        }]
    }
};

function ViewModel() {

    var self = this;

    this.refreshUrl = $("[data-view=edit]").data("url");

    this.refresh = function(callback) {
        $.get(self.refreshUrl, function(data) {
            //Ignoring actual JSON data for testing
            ko.mapping.fromJS(testData, {}, self);
            if (typeof callback == "function") {
                callback.call(self);
            }
        });
    }

    this.addedQuestion = function() {
        // Gets called for every question every refresh by afterRender
        // Never gets called at all by afterAdd
    }

};

var refreshing = false, handler;
window.viewModel = new ViewModel();

//Initialize the UI after initial AJAX is completed
viewModel.refresh(function() {

    ko.applyBindings(this);

        $(document).on("click", ".add-question", function() {
        if (!refreshing) {
            handler = setInterval(viewModel.refresh, 3000);
            refreshing = true;
        }
    });
});

誰かがこれに明らかに間違っていると思いますか?

編集

静的 JavaScript オブジェクトを使用するようにスクリプトを編集しました。それでも、すべての更新で再レンダリングされます。また、Knockout 2.3.0 に更新されました。ビューは次のとおりです。

    <!-- ko foreach: Inspectable.Categories -->
        <div class="row row-fluid space-above">
            <h4 class="orange" data-bind="text: Name"></h4>
            <!-- ko foreach: { data: Questions, afterRender: $root.addedQuestion } -->
                <!-- ko if: Active() || ~$.map($root.Answers(), function(a) { return a.Id() == Id() }) -->
                    <div class="question space-above">
                        <p><strong data-bind="text: Text"></strong></p>
                        <div class="answers" data-bind="foreach: Answers">
                            <!-- ko if: $parent.AllowMultiple --><label class="checkbox"><input type="checkbox" data-url="<%= Url.Action("AddOrRemoveAnswer") %>" data-bind="attr: { value: Id, name: 'question-' + $parent.Id() }"/><!-- ko text: Text --><!-- /ko --></label><!-- /ko -->
                            <!-- ko ifnot: $parent.AllowMultiple --><label class="radio"><input type="radio" data-url="<%= Url.Action("AddOrRemoveAnswer") %>" data-bind="attr: { value: Id, name: 'question-' + $parent.Id() }"/><!-- ko text: Text --><!-- /ko --></label><!-- /ko -->
                        </div>
                    </div>
                <!-- /ko -->
            <!-- /ko -->
            <!-- ko if: Questions().length == 0 -->
                <div class="question space-above">
                    <p><strong>No questions in this category.</strong> <a class="add-question" data-bind="attr: { href: '<%= Url.Action("Create", "Questions") %>?categoryId=' + Id() + '&inProgress=true' }" target="_blank">Add some.</a> </p>
                </div>
            <!-- /ko -->
            <!-- ko if: Questions().length > 0 -->
                <div class="question space-above">
                    <a class="add-question" data-bind="text: 'New question for ' + Name(), attr: { href: '<%= Url.Action("Create", "Questions") %>?categoryId=' + Id() + '&inProgress=true' }" target="_blank"></a>
                </div>
            <!-- /ko -->
        </div>
    <!-- /ko -->
4

1 に答える 1

3

何も変更されていない場合でも、バインディングが完全に削除され、更新のたびに再レンダリングされる理由は、ko.applyBindingsすべての更新を呼び出しているためです。

ko.applyBindingsリフレッシュ機能の中に入れたくありません。ko.applyBindings を 1 回呼び出すだけで済みます。その後、KO はデータの更新時に DOM の更新を処理します (またはその逆)。あなたはただ欲しい:

var testDate = { ... };
function ViewModel () { ... }
var viewModel = new ViewModel();
ko.applyBindings(viewModel);
viewModel.refresh(function () {
  // ko.applyBindings(this); <- Get rid of this here, no bueno
  $(document).on("click", ....);
});

以上です。refresh を呼び出すたびに、viewModel を更新するサーバーからデータを取得します。値が更新されると、KO は必要に応じて DOM を更新します。その後 applyBindings を呼び出すと、KO はブロードソードで DOM を通過し、必要かどうかにかかわらずすべてのバインディングを更新します。


jQuery の簡単なヒント:

$.get(...)promise を返します。promise が返す 3 つの重要な関数があり.done(), .fail(), and .always().ます。関数については、this.refresh()次のように $.get の結果を返します。

this.refresh = function () {
  return $.get(...);
};

次に、これを呼び出すと、次のようになります。

viewModel.refresh().done(function(data){
  // Callback function
});

これで、コールバック関数を渡したり、コールバックが関数のタイプであるかどうかを確認したり、失敗した場合のコールバック関数の処理について心配したりする必要がなくなりました。また、普遍的であり、関数のチェーンにプロミスを返し続けることができます。関数は、関数を実行する前に $.get リクエストが解決されるまですべて待機します。

于 2013-09-07T02:48:53.440 に答える