Skip to content
e-ryu edited this page Jul 1, 2015 · 1 revision

Overview

As the internal mechanism, Asta4js implements the 2-way binding by a built-in observing mechanism which can be used directly by developers too.

Basically, all the javascript model/variable can be observed, array can be observed as well.

variable observing

  • _on_change

    As mentioned in the Basic Usage, we performed rendering/binding by a snippet which is created by scope's snippet method, for simple observing, the snippet is not necessary, then it becomes simpler:

    $scope.data = {};
    $scope.observeData = {};
    
    $scope.observe($scope.data, {
      value: {
        _on_change: function(newValue, oldValue){
          $scope.observeData.value = newValue + "-observed";
        }
      }
    }

    The given meta can be a function which will be treated as "_on_change" by default:

    $scope.observe($scope.data, {
      value: function(newValue, oldValue){
        $scope.observeData.value = newValue + "-observed";
      }
    }
  • _register_assign

    It would be rarely to be used but as the way how the Asta4js bind the DOM's value change to model/variable, the "_register_assign" can be directly used by developers too.

    $scope.observe($scope.data, {
      value: {
        _register_assign: function(bindContext, changeHandler){
    
          // register some event monitor the handle the external changes, and the given changeHandler can be called to do the real assignment.
          var target = $("#someinput");
          target.change(function(){
            changeHandler(target.val(), bindContext);
          });
    
          // we have to return a function which can be use to perform force assignment
          return function(){
            changeHandler(target.val(), bindContext);
          };
        }
      }
    }

    As the matter of fact, the above example is the default inner implementation of framework for monitoring input changes.

array observing

Things become a little bit complicated for observing an array. Basically, we will want to observe 3 types of change event on an array reference: array reference change, array splice, array item change.

To achieve the various observing tasks on array, the following meta format can be used:

$scope.observe($scope.data, {
  list:
  {
    _splice: {
      _on_change: function(splices){
        splice.index; // index position that the change occurred.
        splice.removed; // an array of values representing the sequence of elements which were removed
        splice.addedCount; // the number of elements which were inserted.
      }
    },
    _value : {
      _on_change: function(newValue, oldValue, bindContext){
        var index = bindContext._getArrayIndex(); // the index of current item's last dimension
        var indexes = bindContext._getArrayIndexes(); // the all indexes of the current item's all dimensions
      }
    },
    _item  : {
      _value: {
        _on_change: function(newValue, oldValue, bindContext){
          // common value observing
        }
      }
    }
  }
});

(See the real usage age of array observing at todoApp)

The above example the raw format of array observing, which is obviously difficult to use for common array observing tasks(usually we will want to map the observing target array to a new array), thus we can use the following format to make things easier:

$scope.observe($scope.data, {
  list: {
    _array_map: function(newValue, oldValue, bindContext){
      var list = $scope.observeData.list;
      var hopeLength = Array.isArray(newValue) ? newValue.length : 0;
      Aj.util.arrayLengthAdjust(list, hopeLength, function(){//initialize new item
        return "";
      }, function(item, index){//discard mapped items
        //if necessary, this is a chance to clear the resource of mapped item before they are removed
        //item.discard()
      });
      return list;
    },
    _array_discard: function(bindContext){
      delete $scope.observeData.list;
    },
    _item: {
      _value: function(newValue, oldValue, bindContext){
        // via index
        var targetIndex = bindContext._getArrayIndex();
        $scope.observeData.list[targetIndex] = $scope.data.list[targetIndex] + "-observed-changed";
      }
    }
  }
});

The "_array_map" will be called on array reference change and splice both, so the developer have the chance to return a mapped array for the given changed target array. "Aj.util.arrayLengthAdjust" is a help functioni to helper developers adjust the length of mapped array simply, of course, you can do your own mapping logic without this function.

"_array_discard" will be called when current bind context is dicarded, bascially just release the mapped array's memory is enough.

"_item" is the as the same as what has been mentioned in rendering/binding, to handle the item change. the "_value" means we want to handle the change of item itself rather it's proerties.

If we do not assign the value to the item itself but assign to the item's properies, "_mappedItem" of bind context can be used to make things simpler:

$scope.observe($scope.data, {
  list: {
    ...
    _item: {
      _value: function(newValue, oldValue, bindContext){
        //the magic age should be 10 years younger than the real age
        bindContext._mappedItem.magicAge = newValue - 10;
      }
    }
  }
});