13

The problem is shown here: http://jsfiddle.net/ews7S/

<input type="text" ng-model="testModel" dir="123">

When an element is bound to a model in a controller scope and you also add a directive to the element that has it's own local scope, then changes to the model only change in the directives scope.

Atomix
  • 2,440
  • 8
  • 36
  • 48

2 Answers2

18

An alternative solution is to use an object for the model, rather than a primitive. Then the new directive scope will inherit (prototypically) a reference to the object, rather than a copy of the primitive's value:

$scope.model = { testProp: "444" };

<input type="text" ng-model="model.testProp" dir="123">
<input type="text" ng-model="model.testProp">

document.getElementById("out").innerHTML = scope.model.testProp;

http://jsfiddle.net/mrajcok/awfU5/

With a primitive, such as $scope.testModel, the directive scope's testModel property gets a copy of the parent scope's testModel's value. Changes to one do not affect the other.

With an object, such as $scope.model, both the parent scope and the directive scope have a reference to the same (one) object. Changes in either affect the same object.

Another (fragile) solution is to use the undocumented $parent property (make these changes to the question fiddle):

<input type="text" ng-model="$parent.testModel" dir="123">

document.getElementById("out").innerHTML = scope.$parent.testModel;

Note that using $parent is a fragile solution because use of $parent depends on the DOM structure. E.g., If another controller was added (explicitly by you, or implicitly by another Angular directive) between the parent and the child (now grandchild), we would then need to use $parent.$parent.testModel.

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • This is a great explanation of why is important to bind to an object instead of a primitive in certain situations. +1 – theMothaShip May 08 '14 at 13:54
  • I had this problem with a textarea. The 1st mentioned solution (inherit a reference to the object) worked, but the 2nd did not! More accurately: it worked for a while then for no obvious reason simply stopped. The textarea stopped updating the parent scope property. I recommend to avoid the $parent solution, it seems very flaky and unstable! – tivoni Aug 12 '15 at 17:42
  • @tivoni, it probably stopped working for you because use of `$parent` depends on the DOM structure. If another controller was added (explicitly by you, or implicitly by another Angular directive) between the parent and the child (now grandchild), you may need to use `$parent.$parent.testModel`. I'll update the post to reflect this. – Mark Rajcok Aug 12 '15 at 23:02
  • @Mark I am certain no other controller was added in between the two controllers – tivoni Aug 13 '15 at 09:37
  • You are awesome Mark... what an important concept, though basic, that was. – Kings Feb 17 '16 at 19:47
6

The solution is to add this to the directive:

scope: {testModel: '=ngModel'},

See here: http://jsfiddle.net/ews7S/1/

Why this works is because the '=' sets up bi-directional binding between a local scope property and the parent scope property (see docs: http://docs.angularjs.org/guide/directive under Directive Definition Object).

Atomix
  • 2,440
  • 8
  • 36
  • 48
  • 1
    This solution also creates an "isolate scope", which is different from what "scope: true," does in the original question. In the question fiddle, "scope: true", creates a new scope that prototypically inherits from the parent scope. In the solution fiddle here, a new scope is created that does not prototypically inherit from the parent scope. If you want a solution where the new scope prototypically inherits, see my alternative answer. If you don't want the inheritance (and often you don't), then this is the solution you want. – Mark Rajcok Nov 29 '12 at 23:26
  • 2
    [ngModel](http://docs.angularjs.org/api/ng.directive:ngModel) has more responsibilities than other simpler attributes. Thus you will also have issues with [NgModelController](http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController). When directive will be linked ngModel.$viewValue/$modelValue will have not what you need. – Artem Andreev Nov 30 '12 at 05:50