How to have a global property changed event handler with KnockoutJS

438 views Asked by At

I come from a C# environment, there we have INotifyPropertyChanged interface. When subscribed to this property changed event, one receives the sender, and the property name. The sender is the ViewModel in this example. I want to have something similar with KnockoutJS. I tried to subscribe and store the instance of the function to a hashtable which contains an object with ViewModel and PropertyName parameter. Because the new value in the observable isn't sufficient for what I want to use the event for.

How can I create code with KO that works in a similar fashion as C#'s INotifyPropertyChanged?

This is some crap I wrote to show you I've put in some effort. But I am failing here miserably.

var propertyChangedHashTable = new Hashtable();

function PropertyChanged(newValue) {
    console.log(this);
    var changedEventParams = propertyChangedHashTable[this];
    console.log(changedEventParams);
    //gateway.propertyChanged(changedEventParams.viewModel, changedEventParams.propertyName, newValue);
};

function subscribePropertyChanged(viewModel, objectPath) {
    if (typeof objectPath === "undefined" || objectPath == null) objectPath = "";
    if (objectPath.length !== 0) objectPath += '.';
    var observable = ko.observable("").toString();

    for (var propertyName in viewModel) {
        var viewModelName = viewModel.__proto__.constructor.name;
        var localObjectPath = objectPath + viewModelName;
        var property = viewModel[propertyName];
        if (propertyName.indexOf("ViewModel") !== -1) {
            subscribePropertyChanged(property, localObjectPath);
            continue;
        }
        var isObservable = property.toString() === observable.toString();
        if (!isObservable) continue;

        var propertyChangedFunc = PropertyChanged;

        propertyChangedHashTable.put(propertyChangedFunc, 'test');
        property.subscribe(propertyChangedFunc);
    }
}

function MainViewModel() {
    var self = this;
    self.isRecording = ko.observable(false);
    self.dataDirectory = ko.observable("C:\\Temp\\Recordings");
    self.toggleIsRecording = function() {
         self.isRecording(!self.isRecording());
    };
}

var viewModel = new MainViewModel();
subscribePropertyChanged(viewModel);
1

There are 1 answers

0
nyrocron On BEST ANSWER

From the knockout docs:

The subscribe function accepts three parameters: callback is the function that is called whenever the notification happens, target (optional) defines the value of this in the callback function, and event (optional; default is "change") is the name of the event to receive notification for.

So if you supply the ViewModel as second argument "target" to subscribe() you can access it in the handler as this. For example:

<p data-bind="text: counter"></p>
<button data-bind="click: buttonClicked">Increment</button>

<script type="text/javascript">
var ViewModel = function() {
    this.counter = ko.observable(0);
    this.buttonClicked = function() {
        this.counter(this.counter() + 1);
    };

    this.counter.subscribe(function(newValue) {
        console.log(newValue);
        console.log(this);
    }, this);
};

ko.applyBindings(new ViewModel());
</script>