3

UI で「読み込み中」インジケーターを表示または非表示にするには、その可視性を、waiting次のように定義されている名前付きのオブザーバブルにバインドします。

// Viewmodel
var outstandingRequests = ko.observable(0);

// true if any requests are outstanding
var waiting = ko.computed(function() {
    return outstandingRequests() > 0;
}.extend({ throttle: 500 });

// These are called when AJAX requests begin or end
function ajaxBegin() {
    outstandingRequests(++outstandingRequests());
}
function ajaxEnd() {
    outstandingRequests(--outstandingRequests());
}

<!-- View -->
<div data-bind="visible: waiting">Please wait, loading...</div>

waitingアプリケーションの知覚速度を上げるために、リクエストに時間がかかる場合 (この場合は 500 ミリ秒以上) でない限り、読み込みメッセージを表示したくないため、オブザーバブルを調整しています。問題は、実行時間の長いリクエストが終了すると、さらに 500 ミリ秒が経過するまで読み込みインジケーターが消えないことです。代わりに、最後の未処理のリクエストが終了したら、すぐwaitingに false に切り替えたいと思います。

を使用して最初に修正を試みましたvalueHasMutated()が、更新はまだ遅れています。

function ajaxEnd() {
    outstandingRequests(--outstandingRequests());
    // If that was the last request, we want the loading widget to disappear NOW.
    outstandingRequests.valueHasMutated(); // Nope, 'waiting' still 500ms to update :(
}

waitingスロットル拡張機能をバイパスして、強制的にすぐに更新するにはどうすればよいですか?

4

2 に答える 2

4

オブザーバブルを拡張すると、エクステンダーは通常、オブザーバブルを目的の動作で別のオブザーバブルにラップします。元のオブザーバブルへの参照を保持できます。これにより、オブザーバブルの調整されたバージョンを通常どおり公開しながら、すべてに直接書き込みを行うことができます。

例えば、

var myObservable = ko.observable('foo');
var myThrottledObservable = myObservable.extend({ throttle: 500 });
myThrottledObservable('bar'); // delayed
myObservable('baz'); // immediate

waiting特定のユース ケースでは、オブザーバブルを調整するのではなく、オブザーバブルを調整し、調整されたoutstandingRequests値を で使用しますwaiting

var outstandingRequests = ko.observable(0);

// throttled requests for the waiting observable
var throttledOutstandingRequests = outstandingRequests.extend({ throttle: 500 });

// true if any requests are outstanding
var waiting = ko.computed(function() {
    return throttledOutstandingRequests() > 0;
};

// These are called when AJAX requests begin or end
function ajaxBegin() {
    outstandingRequests(++outstandingRequests());
}
function ajaxEnd() {
    outstandingRequests(--outstandingRequests());
}

オブザーバブルへの書き込みはoutstandingRequestsすぐに行われますが、waitingオブザーバブルは効果的に抑制されます。


または、私の意見では、よりクリーンな解決策は、throttledエクステンダーを再実装して、すぐに更新する機能を追加することです。

ko.extenders['throttleEx'] = function(target, timeout) {
    // Throttling means two things:

    // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
    //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
    target['throttleEvaluation'] = timeout;

    // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
    //     so the target cannot change value synchronously or faster than a certain rate
    var writeTimeoutInstance = null;
    var throttled = ko.dependentObservable({
        'read': target,
        'write': function(value) {
            clearTimeout(writeTimeoutInstance);
            writeTimeoutInstance = setTimeout(function() {
                target(value);
            }, timeout);
        }
    });

    // add function to set the value directly
    throttled['immediate'] = function(value) {
        target(value);
    };

    return throttled;
};

それを使用するには:

var waiting = ko.computed(function() {
    return outstandingRequests() > 0;
}.extend({ throttleEx: 500 });

// These are called when AJAX requests begin or end
function ajaxBegin() {
    outstandingRequests.immediate(++outstandingRequests());
}
function ajaxEnd() {
    outstandingRequests.immediate(--outstandingRequests());
}
于 2013-10-31T16:46:52.150 に答える
2

本当に必要なのは、waitingオブザーバブルが になったときにオブザーバブルからの通知を遅らせることtrueです。notifySubscribersこれは、オブザーバブルの関数をインターセプトすることで実行できます。

var originalNotifySubscribers = this.isWaiting.notifySubscribers,
    timeoutInstance;
this.isWaiting.notifySubscribers = function(value, event) {
    clearTimeout(timeoutInstance);
    if ((event === 'change' || event === undefined) && value) {
        timeoutInstance = setTimeout(function() {
            originalNotifySubscribers.call(this, value, event);
        }.bind(this), 500);
    } else {
        originalNotifySubscribers.call(this, value, event);
    }
};

jsFiddle: http://jsfiddle.net/mbest/Pk6mH/

編集:あなたの特定のケースに対する別の、おそらくより良い解決策を考えました。waitingオブザーバブルは他の 1 つのオブザーバブルにのみ依存するため、オブザーバブルを更新する手動サブスクリプションを作成できますwaiting

var timeoutInstance;
this.isLoading.subscribe(function(value) {
    clearTimeout(timeoutInstance);
    if (value) {
        timeoutInstance = setTimeout(function() {
            this.isWaiting(true);
        }.bind(this), 500);
    } else {
        this.isWaiting(false);
    }
}, this);

jsFiddle: http://jsfiddle.net/mbest/wCJHT/

于 2013-10-31T20:55:26.883 に答える