Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

KnockoutJS subscribe to property changes with Mapping Plugin

Tags:

Is there anyway I can tell the knockout mapping plugin to subscribe to all property changes call a certain function?

I realize I can manually subscribe to the property change event in this manner:

var viewModel = {     name: ko.observable('foo'), }  // subscribe manually here viewModel.name.subscribe(function(newValue){    // do work }) 

I would like to be able to generically subscribe though, since my view models may vary, I don't want to hardcode the property names. I created a function that does this, but it may not be the best approach. It works over all browsers except IE7 and below.

Here I take a viewmodel as an argument and try to reflect on it subscribing to the properties:

function subscribeToKO(data) {          $.each(data, function (property, value) {             if (getType(value) == "Object")                 data[property] = subscribeToKO(value);             else if (getType(value) == "Array") {                 $.each(value, function (index, item) {                     item = subscribeToKO(item);                 });             }             else {                 if (value.subscribe) {                     value.subscribe(function (newValue) {                         // do work                                                              });                 }             }         });         return data;     } 

Like I said this works, but since I am using the mapping pluging I was hoping there was a hook I could use to provide it with a function that will generically subscribe to property changes.

Something like:

mapping = {    create: function(options){        options.data.subscribe(function(newValue){             // do work ???        });    } }  ko.mapping.fromJS(viewModel, mapping); 

Any ideas?

like image 917
Gabe Avatar asked Apr 13 '12 15:04

Gabe


2 Answers

Here's a generic approach based on Ryan Niemeyer's dirty flag.
Click here for the JsFiddle.

Html:

<ol> <li>     Telephone : <input data-bind="value: telephone"/> </li> <li>     Address : <input data-bind="value: address"/> </li> </ol>​ 

Javascript:

var model = {     telephone: ko.observable('0294658963'),     address: ko.observable('167 New Crest Rd')  }; // knockout extension for creating a changed flag (similar to Ryan's dirty flag except it resets itself after every change) ko.changedFlag = function(root) {     var result = function() {};     var initialState = ko.observable(ko.toJSON(root));      result.isChanged = ko.dependentObservable(function() {         var changed = initialState() !== ko.toJSON(root);         if (changed) result.reset();         return changed;     });      result.reset = function() {         initialState(ko.toJSON(root));     };      return result; }; // add changed flag property to the model model.changedFlag = new ko.changedFlag(model); // subscribe to changes model.changedFlag.isChanged.subscribe(function(isChanged) {     if (isChanged)  alert("model changed"); }); ko.applyBindings(model);​ 
like image 137
Matthew Kelly Avatar answered Oct 04 '22 00:10

Matthew Kelly


This handy little plugin is pretty close to what you did but it comes with several options and can work over a much broader set of requirements without requiring the Mapping plugin:

https://github.com/ZiadJ/knockoutjs-reactor

Basically it allows you to write this kind of code:

ko.watch(viewModel, function(target, trigger) {      // do work });   
like image 36
2 revs Avatar answered Oct 04 '22 01:10

2 revs