KnockoutJS подписывается на несколько наблюдаемых с одним и тем же действием обратного вызова

У меня есть модельный класс в KnockoutJS, который имеет несколько значений, на которые я хочу подписаться. Каждая подписка будет выполнять одну и ту же задачу:

function CaseAssignmentZipCode(zipCode, userId, isNew) {
  var self = this;
  self.zipCode = ko.observable(zipCode);
  self.userId = ko.observable(userId);
  self.isNew = isNew;
  self.isUpdated = false;

  self.zipCode.subscribe(function () { self.isUpdated = true; });
  self.userId.subscribe(function () { self.isUpdated = true; });
}

Есть ли способ объединить эти два вызова для подписки, чтобы я мог использовать одну подписку для "просмотра" обоих значений?

Ответы

Ответ 1

Вы можете использовать для этой цели вычисленный наблюдаемый. Вам просто нужно убедиться, что вы получаете доступ к значению каждого наблюдаемого в функции чтения. Будет что-то вроде:

ko.computed(function() {
   self.zipCode();
   self.userId();
   self.isUpdated = true;
});

Итак, вы получаете зависимости от двух наблюдаемых и устанавливаете свой флаг.

Кроме того, если вы ищете что-то вроде "грязного" флага, вы можете рассмотреть что-то вроде: http://www.knockmeout.net/2011/05/creating-smart-dirty-flag-in-knockoutjs.html. Идея состоит в том, что вы используете вычисленное наблюдаемое, которое вызывает ko.toJS() для объекта, чтобы развернуть все его наблюдаемые.

Ответ 2

Вы не хотите дублировать тело функции обработчика? Извлеките его в переменную.

function CaseAssignmentZipCode(zipCode, userId, isNew) {
  var self = this;
  self.zipCode = ko.observable(zipCode);
  self.userId = ko.observable(userId);
  self.isNew = isNew;
  self.isUpdated = false;

  var handler = function () { self.isUpdated = true; };

  self.zipCode.subscribe(handler);
  self.userId.subscribe(handler);
}

Ответ 3

Улучшение при реорганизации тела функции в переменную путем поворота списка зависимостей для отслеживания в цикле:

function CaseAssignmentZipCode(zipCode, userId, isNew) {
  var self = this;
  self.zipCode = ko.observable(zipCode);
  self.userId = ko.observable(userId);
  self.isNew = isNew;
  self.isUpdated = false;

  var handler = function () { self.isUpdated = true; };

  ko.utils.arrayForEach([self.zipCode, self.userId], function(obs) {
    obs.subscribe(handler);
  });
 } 

Ответ 4

Для этой цели вы можете создать какое-то расширение. Простой пример:

function subscribeMany(callback, observables) {    
    for (var i = 0; i < observables.length; i++) {
        observables[i].subscribe(callback);
    }
}

Использование:

var name = ko.observable();
var email = ko.observable();

var callback = function(value) {
    console.log(value);
};

subscribeMany(callback, [name, email]);

name('test 1')
email('test 2')

Ответ 5

Версия Typescript, предназначенная для выполнения одного и того же обратного вызова для любого Observable в списке Observables.

Это решение работает для типов:

  1. KnockoutObservableArray<KnockoutObservable<T>>
  2. KnockoutObservable<KnockoutObservable<T>[]>
  3. KnockoutObservable<T>[]

Преимущества этого подхода:

  1. Если Observable будет добавлен в ваш KnockoutObservableArray то изменение будет обнаружено, и функция подписки будет добавлена и в этот Observable.
  2. Одно и то же решение может использоваться для множества различных типов, а типы обрабатываются для вас.

    function subscribeMany<T>(
        observables: KnockoutObservableArray<KnockoutObservable<T>> | KnockoutObservable<KnockoutObservable<T>[]> | KnockoutObservable<T>[],
        callback: (v: T) => void
        ): KnockoutObservableArray<KnockoutObservable<T>> | KnockoutObservable<KnockoutObservable<T>[]> | KnockoutObservable<T>[] {
    
        function _subscribeMany<T>(
            observables: KnockoutObservableArray<KnockoutObservable<T>> | KnockoutObservable<KnockoutObservable<T>[]> | KnockoutObservable<T>[],
            callback: (v: T) => void): void {
    
            if (_isObservableArray<T>(observables)) {
                _subcribeAndRun(observables, (array) => {
                    array.forEach((observable) => {
                        observable.subscribe(callback);
                    });
                });
            }
            else {
                ko.unwrap(observables).forEach((observable) => {
                    observable.subscribe(callback);
                });
            }
        }
    
        function _isObservableArray<T>(observables: KnockoutObservableArray<KnockoutObservable<T>> | KnockoutObservable<KnockoutObservable<T>[]> | KnockoutObservable<T>[]): observables is KnockoutObservableArray<KnockoutObservable<T>> | KnockoutObservable<KnockoutObservable<T>[]> {
            return "subscribe" in observables;
        }
    
        function _subcribeAndRun<T>(o: KnockoutObservable<T>, callback: (v: T) => void): KnockoutObservable<T> {
            o.subscribe(callback);
            callback(o());
    
            return o;
        }
    
        _subscribeMany<T>(observables, callback);
        return observables;
    }