KnockoutJS subscribe to multiple observables with

2019-01-19 10:21发布

I've got a model class in KnockoutJS which has multiple values that I'd like to subscribe to. Each subscription will perform the same task, like so:

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; });
}

Is there a way to combine these two calls to subscribe, so that I can use one subscription to 'watch' both values?

5条回答
走好不送
2楼-- · 2019-01-19 11:00

You can create some kind of an extension for this purpose. Simple example:

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

Usage:

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')
查看更多
虎瘦雄心在
3楼-- · 2019-01-19 11:05

Improving upon refactoring the function body into a variable, by turning the list of dependencies to track into a loop:

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楼-- · 2019-01-19 11:06

You can use a computed observable for this purpose. You just need to make sure that you access the value of each observable in the read function. Would be something like:

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

So, you get dependencies on the two observables and set your flag.

Also, if you are looking for something like a "dirty" flag, then you might consider something like: http://www.knockmeout.net/2011/05/creating-smart-dirty-flag-in-knockoutjs.html. The idea is that you use a computed observable that calls ko.toJS() on an object to unwrap all of its observables.

查看更多
劫难
5楼-- · 2019-01-19 11:13

Do you not want to duplicate handler function's body? Extract it to a variable.

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);
}
查看更多
倾城 Initia
6楼-- · 2019-01-19 11:14

Typescript version made for executing the same call back on any Observable in a list of Observables.

This solution works for the types:

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

Benefits of this approach:

  1. If an Observable gets added to your KnockoutObservableArray then the change will be detected and the subscribe function will be added to that Observable as well.
  2. The same solution can be used for many different types and the types are handled for you.

    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 {
                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;
    }
    
查看更多
登录 后发表回答