Change attribute from inside Directive

Using AngularJS. I have a directive that I want to have two-way data binding. The directive will have the attribute "activate". Initially, the β€œactivate” value will be β€œ1”.

The directory link function checks if the "activate" value is "1". If so, it will change β€œactivate” to 0, but will do some other things.

Later, if I want the directive to do something again in the controller, I will again change the "activate" to "1". Since the directive has a clock, it will repeat the loop.

Unfortunately, every time I do this, I get the expression "0", used with the directive "testDirective", does not lend itself to purpose! "or" Inappropriate model expression: 1 (directive: testDirective). "

Here is the HTML:

<body ng-app="app"> <test-directive activate="1"></test-directive> </body> 

Here is the JS:

 var app = angular.module('app', []); app.directive('testDirective', function() { return { restrict: 'E', scope: { activate : '=' }, link: function( scope, elem, attrs, controller ) { var el = elem[0]; var updateContent = function() { el.innerText = 'Activate=' + scope.activate; }; updateContent(); attrs.$observe( 'activate', function() { console.log('Activate=' + scope.activate); if( scope.activate == '1') { scope.activate = '0' updateContent(); } }); } } }); 

Here it is on jsFiddle: http://jsfiddle.net/justbn/mgSpY/3/

Why can't I change the value stored in the directive attribute? I use two way binding.

The docs say: "If the property of the parent area does not exist, it throws an exception NON_ASSIGNABLE_MODEL_EXPRESSION."

NOTE. The content for the update correctly displays the value "activate". However, the value "activate" in "" is not updated.

However, this does not make any sense to me, since there is a property of the parent scope.

Any ideas?

+6
source share
2 answers

Although I agree with using $watch instead of attrs.$observe , which is not the main reason for the error message you receive.

The problem is that you are trying to assign a value to an unassignable expression - since the error message tells you: Non-assignable model expression: 1 (directive: testDirective)

The untranslatable expression in this case is the number "1"

 <test-directive activate="1"> 

You will be able to pass the initial value (1) to the directive, but when the directive tries to update this value, the activate attribute cannot be changed - because it is a number.

So, you need to change this to a variable so that you can update the value later.

See the code below where I initialize a variable in $ scope called activate.initialValue through the controller.

And I also used $watch instead of attrs.$observe .

I added $timeout only to simulate an event to change the activation value after 2 seconds.

HTML

 <body ng-app="app" ng-controller="appCtrl"> <test-directive activate="activate.initialValue"></test-directive> </body> 

Js

 var app = angular.module('app', []); app.controller('appCtrl', function($scope){ $scope.activate = { initialValue : 1 } }); var app = angular.module('app', []); app.controller('appCtrl', function($scope){ $scope.activate = { initialValue : 2 } }); app.directive('testDirective', function($timeout) { return { restrict: 'E', scope: { activate : '=' }, link: function(scope, elem, attrs) { scope.$watch('activate', function(newValue, oldValue){ console.log('activate has changed', newValue); }); $timeout(function(){ scope.activate = 0; }, 2000); }, template: "{{activate}}" } }); 

You can also see the work here ( http://jsfiddle.net/mgSpY/63/ ).

And here is the official AngularJS documentation ( http://docs.angularjs.org/error/ngModel:nonassign )

+12
source

To control the scope.$watch property, you should use scope.$watch instead of attrs.$observe :

 link: function(scope, elem, attrs, controller) { var el = elem[0]; var updateContent = function() { el.innerText = 'Activate=' + scope.activate; }; scope.$watch('activate', function(value) { console.log('Activate=', value); if(value === 1) { scope.activate = '0' } updateContent(); }); } 

jsFiddle here .

Note that I removed the updateContent call from the links function because you can safely access the area property inside the $watch callback, unless you can guarantee that the value associated with this property is available before processing the Angular directive.

$observe should only be used to observe / view changes in the value of a DOM attribute that contains interpolation (for example, value="{{ value }}" ). Check out this SO question to better understand it.

+3
source

Source: https://habr.com/ru/post/953363/


All Articles