Angular directive not applied on change of the value - javascript

I have a directive like
testApp.directive('changeValue', function(){
alert("coming");
return {
restrict: 'E',
link: function (scope, element, attrs) {
element.text(attrs.val);
},
replace: true
};
});
and I have my views as follows
<change-value val="{{test.val}}"/>
When I refresh my page, I could see the values and I get the alerts. But when the value is changed I don't get it applied to the directive but it binds to the view as I could see it being changed outside when simply called like
<span>{{test.val}}</span>
I am not sure how this works. Thanks in Advance!

In your code, the link function is executed only once so the value is updated only when the directive instance is created. Since you want to have a watch of its value and keep updating the text you can use $observe()
var app = angular.module('my-app', [], function() {})
app.controller('AppController', function($scope) {
$scope.test = {
val: "Welcome"
};
});
app.directive('changeValue', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
attrs.$observe('val', function() {
element.text(attrs.val);
})
},
replace: true
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app" ng-controller="AppController">
<change-value val="{{test.val}}"></change-value>
<input ng-model="test.val" />
</div>

Related

Directives in object created during app lifetime doesn't work

I've created some basic directive. It works well if I use it with some objects in html file
<some-directive some-data="123"></some-directive>
But if I dynamically load this object to my webpage:
//getting html source as a string, then appending it to DOM:
elem.html("<some-directive some-data='123'></some-directive>");
The directive doesn't work (object is being added properly to DOM)
app.directive('someDirective', function (notes, parts) {
return {
restrict: 'AE',
scope: {
someData: '='
},
link: function (scope, elem, attrs) {
console.log("directive fired");
}
};
});
What can I do to make it work properly?
For dynamic directives, you have to use $compile service that compiles scope into template. Look at sample below, <some-directive-wrapper /> will add <some-directive /> element into itself and compile scope value
var app = angular.module('app', []);
app.directive('someDirective', function () {
return {
restrict: 'AE',
scope: {
someData: '='
},
template: '<h2>someDirective compiled, someData is {{someData}}</h2>',
link: function (scope, elem, attrs) {
console.log("directive fired");
}
};
});
app.directive('someDirectiveWrapper', function ($compile) {
return {
restrict: 'AE',
link: function (scope, elem, attrs) {
//get value from ajax maybe
//and set to scope
scope.data = 123;
//replace directive with another directive
elem.html('<h1>someDirectiveWrapper compiled </h1>\n<some-directive some-data="data"></some-directive>');
//compile scope value into template
$compile(elem.contents())(scope);
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<some-directive-wrapper></some-directive-wrapper>
</div>

Angular pass dynamic scope to directive

I have a directive that takes an attribute value from a an http call like so:
Controller
app.controller("HomeController", function($scope){
$http.get("/api/source").then(function(res){
$scope.info = res.data
});
});
HTML
<div ng-controller="MainController">
<ng-mydirective data-info="{{info}}"></ng-mydirective>
</div>
Directive:
app.directive("ngMydirective", function(){
return {
restrict: "E",
templateUrl: "/partials/info.html",
link: function(scope, element, attrs){
scope.info = attrs.info;
}
}
});
I'm trying to pass that info variable from the controller, into the directive, through an attribute. It doesn't work because the variable value is initially created from an asynchronous http call, so at the time the directive is created, there is no value.
Is there a way to do this?
You can observe the attribute's value:
link: function(scope, element, attrs){
attrs.$observe('info', function(val) {
scope.info = val;
})
}
Also, if you can change the directive scope to be isolated, you can do it by binding the values:
app.directive("ngMydirective", function(){
return {
restrict: "E",
scope: {info: '='},
templateUrl: "/partials/info.html",
link: function(scope, element, attrs){
}
}
});
<div ng-controller="MainController">
<ng-mydirective info="info"></ng-mydirective>
</div>

Why does this angularjs directive not show the bound document?

I have a directive with a bound doc model from controller. But the directive does not show the document?
HTML:
<div ng-app="myApp">TestApp:
<div ng-controller="TestController as Test">Testdoc controller: {{Test.doc}}
<br>
<test-me ng-init="abc=123" my-model="Test.doc">Testdoc directive: {{myDoc}} | {{abc}}</test-me>
</div>
</div>
JS:
var app = angular.module("myApp", []);
app.controller("TestController", function () {
this.doc = {
name: 'testname',
id: null
};
});
app.directive("testMe", function () {
return {
scope: {
myDoc: '=myModel'
},
link: function(scope, elem, attrs){
}
}
});
output HTML is still:
TestApp:
Testdoc controller: {"name":"testname","id":null}
Testdoc directive: <missing output of doc here>| 123
jsfiddle: http://jsfiddle.net/kx97y93k/14/
What i am doing wrong?
Solution was not to call {{myDoc}} in html directly but inside a template.
app.directive("testMe", function () {
return {
restrict: 'E',
template: '<div>Testdirective: {{myDoc}}<div>',
scope: {
myDoc: '=myModel'
},
link: function(scope, elem, attrs){
}
}
});
See updated jsfiddle: http://jsfiddle.net/kx97y93k/18/
Many thanks to #Mr_Green for that hint
add restrict : 'E' to the DDO, see below. Angular 1.2 uses restrict : 'A' if not explicitly set otherwise. For Angular 1.3 that became 'EA' and your directive would have worked. To make it work in your fiddle, change it as follows:
app.directive("testMe", function () {
return {
restrict : 'E',
scope: {
myDoc: '=myModel'
},
link: function(scope, elem, attrs){
}
}
});

Using & bindings in nested AngularJS directives

I'm trying to bind to ng-change on an element created by a directive up through two other directives that wrap it to a method on the controller using & bindings in an isolate scope, but I can't figure out how to get arguments to pass all the way through. Here's a plunk that demonstrates the problem.
In short, I have an HTML structure like this:
<body ng-app="ExampleApp">
<div ng-controller="Controller">
<button ng-click="doSomething('Called directly')">Call Function Directly</button>
<br />
<outer on-outer-model-changed="doSomething('Called from Outer in HTML')"></outer>
</div>
</body>
The controller:
var app = angular.module('ExampleApp', []);
app.controller('Controller', ['$scope',
function($scope) {
$scope.doSomething = function(one, two, three) {
console.log(arguments);
};
}
]);
The outer directive:
app.directive('outer', function($compile) {
return {
restrict: 'E',
scope: {
outerModelChanged: '&onOuterModelChanged'
},
link: function(scope, element, attrs) {
var innerElement = angular.element('<inner></inner>');
innerElement.attr('on-inner-model-changed', 'outerModelChanged(\'Called from Outer\')');
element.after(innerElement);
$compile(innerElement)(scope);
console.log(arguments);
}
}
});
And the inner directive that the outer directive creates:
app.directive('inner', function() {
return {
scope: {
innerModelChanged: '&onInnerModelChanged'
},
restrict: 'E',
template: '<button ng-click="innerModelChanged(\'Called from Inner\')">Call from Inner</button>'
}
});
I understand that I'm getting the output ["Called from Outer in HTML"] because this is hardcoded into the <outer> tag. What I don't understand is how to pass arguments all the way up from the inner directive.
I'm not sure I 100% get what you want to accomplish but this is how you would make the ["Called from Inner"] message appear.
Change the html so the on-outer-model-changed expression does not use a hardcoded string.
<body ng-app="ExampleApp">
<div ng-controller="Controller">
<button ng-click="doSomething('Called directly')">Call Function Directly</button>
<br />
<outer on-outer-model-changed="doSomething(outerParam)"></outer>
</div>
</body>
Then change the outer directive to call outerModelChanged with a parameter. And set the outerParam to the innerParam.
app.directive('outer', function($compile) {
return {
restrict: 'E',
scope: {
outerModelChanged: '&onOuterModelChanged'
},
link: function(scope, element, attrs, controller) {
var innerElement = angular.element('<inner></inner>');
innerElement.attr('on-inner-model-changed', 'outerModelChanged({outerParam:innerParam})');
element.after(innerElement);
$compile(innerElement)(scope);
console.log(arguments);
}
}
});
Finally call the innerModelChanged from the inner directive with the innerParam set to your message.
app.directive('inner', function() {
return {
scope: {
innerModelChanged: '&onInnerModelChanged'
},
restrict: 'E',
template: '<button ng-click="innerModelChanged({innerParam:\'Called from Inner\'})">Call from Inner</button>'
}
});
Here is a plunk to the above code.

Dynamic NG-Controller Name

I want to dynamically specify a controller based on a config that we load. Something like this:
<div ng-controller="{{config.controllerNameString}}>
...
</div>
How do I do this in angular? I thought this would be very easy, but I can seem to find a way of doing this.
What you want to do is have another directive run before anything else is called, get the controller name from some model remove the new directive and add the ng-controller directive, then re-compile the element.
That looks like this:
global.directive('dynamicCtrl', ['$compile', '$parse',function($compile, $parse) {
return {
restrict: 'A',
terminal: true,
priority: 100000,
link: function(scope, elem) {
var name = $parse(elem.attr('dynamic-ctrl'))(scope);
elem.removeAttr('dynamic-ctrl');
elem.attr('ng-controller', name);
$compile(elem)(scope);
}
};
}]);
Then you could use it in your template, like so:
<div dynamic-ctrl="'blankCtrl'">{{tyler}}</div>
with a controller like this:
global.controller('blankCtrl',['$scope',function(tyler){
tyler.tyler = 'tyler';
tyler.tyler = 'chameleon';
}]);
There's probably a way of interpolating the value ($interpolate) of the dynamic-ctrl instead of parsing it ($parse), but I couldn't get it to work for some reason.
I'm using it in ng-repeat, so this is improved code for loops and sub objects:
Template:
<div class="col-xs6 col-sm-5 col-md-4 col-lg-3" ng-repeat="box in boxes">
<div ng-include src="'/assets/js/view/box_campaign.html'" ng-dynamic-controller="box.type"></div>
</div>
Directive:
mainApp.directive('ngDynamicController', ['$compile', '$parse',function($compile, $parse) {
return {
scope: {
name: '=ngDynamicController'
},
restrict: 'A',
terminal: true,
priority: 100000,
link: function(scope, elem, attrs) {
elem.attr('ng-controller', scope.name);
elem.removeAttr('ng-dynamic-controller');
$compile(elem)(scope);
}
};
}]);
Personally the 2 current solutions here didn't work for me, as the name of the controller would not be known when first compiling the element but later on during another digest cycle. Therefore I ended up using:
myapp.directive('dynamicController', ['$controller', function($controller) {
return {
restrict: 'A',
scope: true,
link: function(scope, elem, attrs) {
attrs.$observe('dynamicController', function(name) {
if (name) {
elem.data('$Controller', $controller(name, {
$scope: scope,
$element: elem,
$attrs: attrs
}));
}
});
}
};
}]);

Categories