简体   繁体   中英

ngModel, dot-rule and more than one level of scope hierarchy

Let's say I want to write HTML like:

<div my-directive my-start="topModel.start" my-end="topModel.end">

my-directive has a template that invokes other directives with ngModel , like so:

<div>
  <input ng-model="myStart" />
  <input ng-model="myEnd" />
</div>

I would like the inner inputs to transparently update topModel . It doesn't work this way, because there's no dot in the ng-model attribute and the value is set in local scope.

The only way I found so far is to watch both models in my-directive and translate, but it's a horrible abomination.

restrict: 'A',
scope: {
    myStart: '=',
    myEnd: '='
},
link: function(scope, el, attrs) {
    scope.model = { start: scope.myStart, end: scope.myEnd };
    scope.$watch("model.start", function(n) {
        scope.myStart = n;
    });
    scope.$watch("model.end", function(n) {
        scope.myEnd = n;
    });
    scope.$watch("myStart", function(n) {
        scope.model.start = n;
    });
    scope.$watch("myEnd", function(n) {
        scope.model.end = n;
    });
}

How can I pass the bindings through my-directive to the inner directives without all this manual synchronization?

EDIT : See plunker at http://plnkr.co/edit/ppzVd7?p=preview - this one actually works

EDIT2 : See another at http://plnkr.co/edit/Nccpqn?p=preview - this one shows how "direct access" without dot doesn't work, and with dot and $watches does.

When you define the scope property as you do on your directive, you will automatically get two properties, scope.myStart and scope.myEnd , with a bidirectional binding to topModel . When you map them over to scope.model you break that binding.

Here is a working example:

module.directive('myDirective', function () {
    return {
        scope: {
            myStart: '=',
            myEnd: '='
        },
        template: '<p><label>Start: <input type="text" ng-model="myStart" /></label></p>' +
                  '<p><label>End: <input type="text" ng-model="myEnd" /></label></p>',
        link: function (scope, element, attrs) {
            scope.$watch('myStart', function (val, old) {
                console.log('Start changed from', old, 'to', val);
            });

            scope.$watch('myEnd', function (val, old) {
                console.log('End changed from', old, 'to', val);
            });
        }
    };
});

Plunker

With your 2nd example I finally understood your problem, though I still don't think it's comming from breaking the dot-rule . You're simply mixing scopes / model binding (see $parent.myStart ).

Try:

(function (app, ng) {
  'use strict';

  app.controller('MyCtrl', ['$scope', function($scope) {
    $scope.topModel = {
      start: 'Initial Start',
      end: 'Initial end'
    };
  }]);

  app.directive('myDirective', function(){
    return {
      scope: { myStart: '=', myEnd: '=' },
      template: '<div>\
        <p>Start: <span special-editor-a ng-model="$parent.myStart"></span></p>\
        <p>End: <span special-editor-b ng-model="myEnd"></span></p>\
      </div>'
    }
  });

  // with replace
  app.directive('specialEditorA', function(){
    return {
      scope: true,
      require: 'ngModel',
      replace: true,
      template: '<input type="text" />'
    }
  });

  // without replace
  app.directive('specialEditorB', function(){
    return {
      scope: { m: '=ngModel' },
      require: 'ngModel',
      template: '<input type="text" ng-model="m" />'
    }
  });
}(angular.module('app', []), angular));

demo: http://plnkr.co/edit/SfFBf5NgFhSOQiFf5Emc?p=preview

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