0

For most JS frameworks and libraries, the value they bring are often in the form of a new structure as to how to build an application (Backbone, React), or new ideas that effectively power-up the language (Angular), or simply the methods they offer are well tested, fast, and really convenient (jQuery).

Usually the ideas and methods they provide are pretty straightforward usage of JavaScript, but with a very clever team behind it that find interesting ways to do things which you can think through and get a solid guess as to how the guts work.

However, I've been unable to think through the ability to two-way bind JS models to view components. What is the secret sauce at the heart of this feature that makes this work? Changing an internal variable from a user input is simple, but what about the reverse? How would you be able to "know" when a JS variable has changed in order to update the display instantly? Surely it can't be polling, so what then?

stupidkid
  • 410
  • 1
  • 6
  • 17
  • Usually whenever data changes it is because of... an event! So many frameworks, like Angular for example, use event-driven data binding. – sdgluck Jul 15 '15 at 19:15
  • Good read for Angularjs: http://stackoverflow.com/questions/9682092/databinding-in-angularjs/9693933#9693933 – Luc Morin Jul 15 '15 at 19:19
  • @sdgluck Read paragraph 3 sentence 3! – stupidkid Jul 16 '15 at 20:44
  • @stupidkid It is the same mechanism, and the reason why you must use methods like `$apply` if a change occurs outside of an Angular-aware event. – sdgluck Jul 18 '15 at 10:48

3 Answers3

2

Whenever a block of your JS runs that angular triggered it will run a digest cycle when the block finishes executing. This basically checks all the values that might of changed and would require updates to the view.

If angular didn't trigger the code then it won't know that something might of changed so your bindings can get out of sync. For example if you run something like this

setTimeout(function() {$scope.myValue = '123'});

Angular won't know that myValue changed and it actually won't update the view. That's why Angular has it's own services for doing everything. e.g. $timeout or $http.

If you have some callback function that Angular doesn't know about then you can manually tell it to check for changes by calling $scope.$apply()

rob
  • 17,995
  • 12
  • 69
  • 94
1

there are several ways to do it. Object.observe is great, but lacks good support. You can poll for values as well, keeping a 2nd copy of the object around to compare. You can also write your own explicit set/get methods to update the model like backbone does.

One neat method i use a lot is using getters/setters to keep the model synced to the dom:

//a demo "model" of data:
model = {
    name: "Fred"
};

function change(k,v){alert([k,v]);} // a stand-in change monitor for demo

// iterate model and replace values with getter/setter combos: 
Object.keys(model).forEach(function(key) {
    var val = model[key];
    delete model[key];
    Object.defineProperty(model, key, {
        get: function() {
            return val;
        },
        set: function(v) {
            val = v;
            change(key, val);
        } //call change upon setting
    });
    change(key, val); //update view "onload"
}); // alerts "Fred";

//update model (fires change() with "name" and "sally" arguments:
model.name="sally"; // alerts  "sally";

the change function is quite simple and for your case should just find elements bound to keys. the advantage here is that you don't need special custom CRUD methods, you can just modify the object properties via assignment like it's 1999. It also doesn't poll, and works correctly all the way back to IE9 and any other ES5 environments. It's the simplest way to bind JS>DOM (afaik) without custom methods.

It does have some limits: nested objects are tricky to get/set upon, you can't do the whole object at once, you can only "watch" primitives. Arrays are a problem too: you can't really replace expando properties with getters/setters without side-effects. But, upon a relatively flat collection of JSON-safe data, get/set works a charm and needs no complex libs to get operational.

checkout a complete example using this method: http://pagedemos.com/xg3szbguqnwu/4

dandavis
  • 16,370
  • 5
  • 40
  • 36
0

I can speak to how it's done in Backbone, which has a relatively low-level perspective on data-binding.

It's a combination of 1. the library having control over attribute setter methods 2. invoking callback functions when attributes change (e.g. by dispatching events) in order to update the UI.

The essential pseudocode is this:

class Model:

  method set(name, value):
    if value != this.attributes[name]
      this.triggerEvent('change', name, value)
      this.attributes[name] = value

m = new Model()
someInputWidget.onEvent('userChangedInput', function(value) {
  m.set(someInputWidget.name, value)
})
m.onEvent('change', function(name, value) {
  getInputWidgetByName(name).setValue(value)
})

Backbone does not do any data binding to the UI, but you can refer to Backbone's annotated source for the actual event-dispatching implementation.

mooiamaduck
  • 2,066
  • 12
  • 13