简体   繁体   中英

Angularjs Manual Two way Binding

The best way to describe my problem is with a demonstration. This is how I want my directive to be used:

<my-directive my-attr="{'property': obj}"></my-directive>

Where 'obj' is an object on the scope that I want two-way binding on. The catch is I also need to get the name of the object 'obj'.

A partial solution I came up with was to pass in the name of the object ('obj') as a string and use $parse to get the object. But when this does not effect the $parent scope in the same way using scope '=' would. How can I manually write two-way binding so that changes to the object on my scope will change the object on parent scopes?

After quite some research, and with reference to the following issue , here's an extension to Scope that allows two-way binding (it is, essentially, the Angular code, only modified to work outside nodeLinkFn ):

angular.module('scopeBindExtention', [])

.run( [ '$rootScope', '$parse', function( $rootScope, $parse ) {

    var extended = angular.extend( $rootScope, {} );

    extended.$bindTwoWay = function( scopeName, parentName ) {
        var scope       = this,
            parentScope = scope.$parent;
            lastValue,
            parentGet,
            parentSet,
            compare;

        parentGet = $parse(parentName);
        if (parentGet.literal) {
          compare = angular.equals;
        } else {
          compare = function(a,b) { return a === b; };
        }
        parentSet = parentGet.assign || function() {
          // reset the change, or we will throw this exception on every $digest
          lastValue = scope[scopeName] = parentGet(parentScope);
          throw new Error( "Expression '" + parentName + "' is non-assignable!" );
          /*
          throw $compileMinErr('nonassign',
              "Expression '{0}' is non-assignable!",
              parentName);
          */
        };
        lastValue = scope[scopeName] = parentGet(parentScope);
        var unwatch = parentScope.$watch($parse(parentName, function parentValueWatch(parentValue) {
          if (!compare(parentValue, scope[scopeName])) {
            // we are out of sync and need to copy
            if (!compare(parentValue, lastValue)) {
              // parent changed and it has precedence
              scope[scopeName] = parentValue;
            } else {
              // if the parent can be assigned then do so
              parentSet(parentScope, parentValue = scope[scopeName]);
            }
          }
          return lastValue = parentValue;
        }), null, parentGet.literal);
        scope.$on('$destroy', unwatch);    
    }

}]);

Clients should just call:

scope.$bindTwoWay( 'childModelName', 'parentModelName' );

Can use this object syntax:

scope[ attrs.myAttr] 

to access parent scope property since you aren't using an isolated scope in directive

I managed to solve my problem. My solution (in coffeescript):

$scope.$watch name, (val) ->
  scope = $scope.$parent
  # look up the scopes until the real object is found
  while scope isnt null
    if not scope.hasOwnProperty(name)
      scope = scope.$parent
    else
      expressions[name].assign(scope, val)
      scope = null

This will check up the chain of scopes and find the one that the object is defined on and modify it there. I believe this is near identical behavior to using scope: '=' in the directive definition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM