Im having a hard time accessing the attributes passed in to my directive from the template of that directive. I want to be able to access 'companyId' from album.tmpl.html but no matter what i try i can't get it. The strangest part is i can see it has made its way in to the controller, but somehow it's not getting from the controller to the template. I know the template is correctly calling the controller as it can succesfully print out the value of 'testVar' which is initialised inside the controller. Any advice would be appreciated.
directive + directive controller
(function () {
'use strict';
angular.module('erCommon')
.directive('erAlbum', albumDirective)
.controller('AlbumController', AlbumController);
function AlbumController() {
var vm = this;
vm.testVar = "test var initiated";
}
function albumDirective($log) {
function albumLink(scope, element, attrs, AlbumController) {
//watch vars in here
}
return {
restrict: 'E',
scope: {
companyId: '=companyId'
},
bindToController: true,
templateUrl: 'components/temp/album.tmpl.html',
controller: 'AlbumController',
controllerAs: 'albumCtrl',
link: albumLink
};
}
})();
template ( album.tmpl.html
<div ng-controller="AlbumController as albumCtrl">
testVar: {{albumCtrl.testVar}}<BR>
companyId:{{albumCtrl.companyId}}<BR>
</div>
usage
<er-album company-id="2"></er-album>
output
test var: test var initiated
companyId:
You need to remove ng-controller from your template:
<div>
testVar: {{albumCtrl.testVar}}<BR>
companyId:{{albumCtrl.companyId}}<BR>
</div>
To achieve the result you wanted i had to modify the structure of your code slightly. Hope this helps you to understand the issue. Look for materials about isolated scopes which Angular uses with directives.
HTML:
<div ng-app="erCommon" ng-controller="AlbumController as albumCtrl">
<er-album company-id="2" test = "albumCtrl.testVar"></er-album>
</div>
Controller:
angular.module('erCommon', [])
.directive('erAlbum', albumDirective)
.controller('AlbumController', AlbumController);
function AlbumController() {
var vm = this;
vm.testVar = "test var initiated";
}
function albumDirective() {
return {
restrict: 'E',
scope: {
test: '=test',
companyId: '#companyId'
},
template: '<div> testVar: {{test}}<BR> companyId:{{companyId}}<BR> </div>', // it will work fine with templateUrl as well, just didn't want to cr8 another file...
link: function(scope, element, attrs){
//do whatever else you might need;
}
};
}
Related
Below is the only way i could figure out how to get a directive to pull out an attribute from its origin element, get a new value by hitting a service, and then adding that new service method return as a class in the directive template. i'm wondering if there is an alternative pattern that might be cleaner then this pattern that might use ng-class or possibly ng-transclude:
html:
<my-directive data-condition="{{hour.condition}}"></my-directive>
js:
angular.module('myApp')
.directive('myDirective', function (myService) {
return {
transclude: true,
replace: true,
scope: true,
template: '<i class="{{wiIconClass}}"></i>',
restrict: 'E',
link: function($scope, $elm, attrs){
$scope.wiIconClass=myService.getValue(attrs.condition);
}
}
});
If your function myService.getValue is synchronous, you could simply do:
<div ng-class="getClass(hour.condition)">
And in your controller:
$scope.getClass = function(condition) {
return myService.getValue(condition);
}
Alternatively, you can directly put your service within your scope:
$scope.myService = myService;
So the HTML becomes
<div ng-class="myService.getValue(hour.condition)">
In both cases, you will need to inject your service into your controller:
myModule.controller('myController', function($scope, myService) {
// this controller has access to myService
})
I would use the Directives scope parameter instead of using the Directives Attribute values. This is because when using the attributes you will need to setup a $watch to see when that value updates, with using $scope you get the benefit of the binding aspect.
As far as to respond to the best way, its hard to say without knowing your actual task. You can have Angular update the elements css class value in several different ways.
Here's a working Plunker with some small updates to your existing code.
http://plnkr.co/edit/W0SOiBEDE03MgostqemT?p=preview
angular.module('myApp', [])
.controller('myController', function($scope) {
$scope.hour = {
condition: 'good'
};
})
.factory('myService', function() {
var condValues = {
good: 'good-class',
bad: 'bad-class'
};
return {
getValue: function(key) {
return condValues[key];
}
};
})
.directive('myDirective', function(myService) {
return {
transclude: true,
replace: true,
scope: {
condition: '='
},
template: '<i class="{{myService.getValue(condition)}}"></i>',
restrict: 'E',
link: function(scope, elm, attrs) {
scope.myService = myService;
}
};
});
html :
<div ng-app="appMod">
<div task-info>{ { data.name } }</div>
</div>
script :
var appmod = angular.module('appMod', []);
appmod.directive("taskInfo", function () {
return {
restrict: 'A',
scope: {},
link: function ($scope, $element, attr) {
$scope.taskdat = '{"name":"Task name","status":"Completed"}';
$scope.data = JSON.parse($scope.taskdat);
scope = $scope; //scope data
},
};
});
is it possible to bind directive scope without having controller scope in Angular Js? If yes, please give me some solution examples.
You don't need a controller scope for writing a directive , see this fiddle.
Here, there is no controller scope, and the value hero is bound within the directive as:
myApp.directive('myDirective', function() {
return {
restrict: 'EAC',
link: function($scope, element, attrs, controller) {
var controllerOptions, options;
$scope.hero='superhero'
}
};
});
Works fine :)
Also the example you provided is similar, but you just need to remove scope from returned JSON object(from directive), as it is being defined as $scope inside the link fucntion.
see : http://jsfiddle.net/bg0L80Lx/
controller option ?
.directive('mydirective', function() {
return {
restrict: 'A', // always required
//controller: 'SomeController'
template:'<b>{{status}}</b>',
controller:'YourCtrl'
}
})
I have a directive defined as
Application.Directives.directive('listview', function() {
return {
restrict: 'EAC',
templateUrl: 'directives/listview/view.html'
};
});
And then want to include it from the main view like this
<div class="{{directiveName}}">
</div>
where directiveName equals "listview". However, it does not work. It generates the below code, but the listview directive does not get loaded
<div class="listview">
</div>
Yet, when I type the above generated code directly into the main template, it does load the directive. How come? How can I make it work?
So I found a way. What you'd want is something like this
<div {{directiveNameInScope}}></div>
But again, that doesn't work. So I created a directive to do it for you. It works like
<div loaddirective="directiveNameInScope"></div>
where the loaddirective directive looks like
Application.Directives.directive('loaddirective', function($compile) {
return {
restrict: 'A',
scope: { loaddirective : "=loaddirective" },
link: function($scope, $element, $attr) {
var value = $scope.loaddirective;
if (value) {
// Load the directive and make it reactive
$element.html("<div "+value+"></div>");
$compile($element.contents())($scope);
}
},
replace: true
};
});
I put it up on github here: https://github.com/willemmulder/loaddirective
I have a example angularJS
<div ng-controller="testCtrl">
<test color1="color1" updateFn="updateFn()"></test>
</div>
<script>
angular.module('dr', [])
.controller("testCtrl", function($scope) {
$scope.color1 = "color";
$scope.updateFn = function() {
alert('123');
}
})
.directive('test', function() {
return {
restrict: 'E',
scope: {color1: '=',
updateFn: '&'},
template: "<button ng-click='updateFn()'>Click</button>",
replace: true,
link: function(scope, elm, attrs) {
}
}
});
</script>
</body>
</html>
I want when I click button, the alert box will appear, but nothing show.
Can anyone help me?
To call a controller function in parent scope from inside an isolate scope directive, use dash-separated attribute names in the HTML like the OP said.
Also if you want to send a parameter to your function, call the function by passing an object:
<test color1="color1" update-fn="updateFn(msg)"></test>
JS
var app = angular.module('dr', []);
app.controller("testCtrl", function($scope) {
$scope.color1 = "color";
$scope.updateFn = function(msg) {
alert(msg);
}
});
app.directive('test', function() {
return {
restrict: 'E',
scope: {
color1: '=',
updateFn: '&'
},
// object is passed while making the call
template: "<button ng-click='updateFn({msg : \"Hello World!\"})'>
Click</button>",
replace: true,
link: function(scope, elm, attrs) {
}
}
});
Fiddle
Perhaps I am missing something, but although the other solutions do call the parent scope function there is no ability to pass arguments from directive code, this is because the update-fn is calling updateFn() with fixed parameters, in for example {msg: "Hello World"}. A slight change allows the directive to pass arguments, which I would think is far more useful.
<test color1="color1" update-fn="updateFn"></test>
Note the HTML is passing a function reference, i.e., without () brackets.
JS
var app = angular.module('dr', []);
app.controller("testCtrl", function($scope) {
$scope.color1 = "color";
$scope.updateFn = function(msg) {
alert(msg);
}
});
app.directive('test', function() {
return {
restrict: 'E',
scope: {
color1: '=',
updateFn: '&'
},
// object is passed while making the call
template: "<button ng-click='callUpdate()'>
Click</button>",
replace: true,
link: function(scope, elm, attrs) {
scope.callUpdate = function() {
scope.updateFn()("Directive Args");
}
}
}
});
So in the above, the HTML is calling local scope callUpdate function, which then 'fetches' the updateFn from the parent scope and calls the returned function with parameters that the directive can generate.
http://jsfiddle.net/mygknek2/
In your 'test' directive Html tag, the attribute name of the function should not be camelCased, but dash-based.
so - instead of :
<test color1="color1" updateFn="updateFn()"></test>
write:
<test color1="color1" update-fn="updateFn()"></test>
This is angular's way to tell the difference between directive attributes (such as update-fn function) and functions.
How about passing the controller function with bidirectional binding? Then you can use it in the directive exactly the same way as in a regular template (I stripped irrelevant parts for simplicity):
<div ng-controller="testCtrl">
<!-- pass the function with no arguments -->
<test color1="color1" update-fn="updateFn"></test>
</div>
<script>
angular.module('dr', [])
.controller("testCtrl", function($scope) {
$scope.updateFn = function(msg) {
alert(msg);
}
})
.directive('test', function() {
return {
scope: {
updateFn: '=' // '=' bidirectional binding
},
template: "<button ng-click='updateFn(1337)'>Click</button>"
}
});
</script>
I landed at this question, because I tried the method above befire, but somehow it didn't work. Now it works perfectly.
use dash and lower case for attribute name ( like other answers said ) :
<test color1="color1" update-fn="updateFn()"></test>
And use "=" instead of "&" in directive scope:
scope: { updateFn: '='}
Then you can use updateFn like any other function:
<button ng-click='updateFn()'>Click</button>
There you go!
I had to use the "=" binding instead of "&" because that was not working.
Strange behavior.
#JorgeGRC Thanks for your answer. One thing though, the "maybe" part is very important. If you do have parameter(s), you must include it/them on your template as well and be sure to specify your locals e.g. updateFn({msg: "Directive Args"}.
I'm trying to acheive databinding to a value returned from a service inside a directive.
I have it working, but I'm jumping through hoops, and I suspect there's a better way.
For example:
<img my-avatar>
Which is a directive synonymous to:
<img src="{{user.avatarUrl}}" class="avatar">
Where user is:
$scope.user = CurrentUserService.getCurrentUser();
Here's the directive I'm using to get this to work:
.directive('myAvatar', function(CurrentUser) {
return {
link: function(scope, elm, attrs) {
scope.user = CurrentUser.getCurrentUser();
// Use a function to watch if the username changes,
// since it appears that $scope.$watch('user.username') isn't working
var watchUserName = function(scope) {
return scope.user.username;
};
scope.$watch(watchUserName, function (newUserName,oldUserName, scope) {
elm.attr('src',CurrentUser.getCurrentUser().avatarUrl);
}, true);
elm.attr('class','avatar');
}
};
Is there a more succinct, 'angular' way to achieve the same outcome?
How about this ? plunker
The main idea of your directive is like
.directive('myAvatar', function (CurrentUserService) {
"use strict";
return {
restrict: 'A',
replace: true,
template: '<img class="avatar" ng-src="{{url}}" alt="{{url}}" title="{{url}}"> ',
controller: function ($scope, CurrentUserService) {
$scope.url = CurrentUserService.getCurrentUser().avatarUrl;
}
};
});