Assume I have a simple directive:
angular.module('app').directive('myDir', myDir);
function myDir() {
var directive = {
bindToController: true,
controller: null,
controllerAs: 'vm',
template: '<button ng-click="vm.myDirVar = 1">',
scope: {
myDirVar: '='
}
};
return directive;
}
If in the following example vm.var is undefined, my directive will not bind it to the isolated scope.
<my-dir my-dir-var="vm.var"></my-dir>
So in order to make it work, I'm using ng-init to set the default value for thevm.var, after that the binding to isolated scope works.
<my-dir my-dir-var="vm.var" ng-init="vm.var = var || 0"></my-dir>
The question is, how can I improve my directive so I can get rid of the ng-init while vm.var would still be binding even if it is undefined initially.
You can do something like:
var directive = {
bindToController: true,
controller: function(){
this.myDirVar = this.myDirVar || 0;
},
controllerAs: 'vm',
template: '<button ng-click="vm.myDirVar = 1">',
scope: {
myDirVar: '='
}
};
If is not a problem having a controller.
Related
I want to pass array in the directive attribute. It doesn't work. I tried to pass an array in the attribute of a directive and nothing happened.
class Directive {
constructor () {
'ngInject';
let directive = {
restrict: 'E',
template: '<div class="btn-group">
<label class="btn btn-primary classButton"
ng-model="radioModel" uib-btn-radio="'First'"
config="{{config[0]}}">{{config[0]}}
</label>
</div>',
controller: Controller,
controllerAs: 'vm',
bindToController: true,
scope: {
config: '='
}
};
return directive;
}
}
class Controller {
constructor() {
'ngInject';
}
}
<directive config="First, Second"></directive>
How should I pass the array to the directive?
Maksim,
You need to pass a scope variable instead of a array constant here,
That is, declare a scope variable and assign this value to that variable like this,
$scope.myArray = ['Firs', 'Second'];
and now you can pass this scope variable to your directive.
<directive config="myArray"></directive>
It should work !!!
I have this simple directive:
angular.module('app')
.directive('ppBubble', function () {
function PpBubble($scope, $element, $attrs) {
var vm = this;
vm.message = $attrs.message;
vm.isSent = $attrs.sent;
}
return {
templateUrl: '../views/directives/ppBubble.html',
restrict: 'E',
controller: PpBubble,
controllerAs: 'vm',
bindToController: true
};
});
And here is how I use it:
<pp-bubble ng-repeat="msg in vm.messages" sent="{{msg.sent}}" message="{{msg.message}}"></pp-bubble>
The problem is that I see this "msg.message" instead of the string this object attribute hold:
Here is the messages array:
vm.messages = [
{
sent: true,
message: "aasdasd dsfsdfd"
}, ...
]
I am probably missing something stupid :(
The problem is that you're doing vm.message = $attrs.message; and you're taking the string literally, which means: {{msg.message}}, since that's the text that's written in that attribute:
message="{{msg.message}}".
You need to work with a scope to make Angular work its binding magic, and take the data from there:
function PpBubble($scope, $element, $attrs) {
var vm = this;
vm.message = $scope.message;
vm.isSent = $scope.sent;
}
return {
templateUrl: '../views/directives/ppBubble.html',
restrict: 'E',
scope: { message: "=", sent: "=" },
controller: PpBubble,
controllerAs: 'vm',
bindToController: true
};
Here is the messages array:
vm.messages = [
{
sent: true,
message: "aasdasd dsfsdfd"
}, ...
]
EDIT: Another thing is that now that you're binding from the directive, you need to bind to the variable itself, and not to its string literal, meaning to remove the {{}} from the directive HTML:
<pp-bubble ng-repeat="msg in messages" sent="msg.sent" message="msg.message"></pp-bubble>
Fiddle
Here is what worked for me:
function PpBubble($scope, $element, $attrs) {
var vm = this;
}
return {
templateUrl: '../views/directives/ppBubble.html',
restrict: 'E',
controller: PpBubble,
controllerAs: 'vm',
bindToController: true
};
});
Also i removed the {{}} from the html:
Thanks to #Omri Aharon
Just wondering what's the best way to communication from controller to directive function, i have got an ng-click on one of the button, but the function sit in the directive, is there a way i can call the function within the controller (which sits in directive). i understand u can apply double binding with scope, is there any better way of doing so?
Cheers
app.controller('leadsListing', ['$scope', function($scope){
$scope.filterresultcount = 0;
$scope.records = [];
$scope.filtertotal = '';
$scope.$watch('filtertotal', function(){
$scope.filterresultcount = parseInt($scope.filtertotal / 20);
});
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.heyJoe(); // It's in diretive
};
}]);
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount'
},
restrict: 'A',
replace: true,
link: function($scope, iElm, iAttrs, controller) {
$scope.heyJoe()
}
}
}
I believe the best way to implement this kind of controller --> directive communication is to use $scope.$broadcast from the controller, and $scope.$on in the directive's controller/ linking function.
Controller:
app.controller('leadsListing', ['$scope', function($scope){
// ...
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.$broadcast('joeCalled');
};
}]);
Directive:
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount'
},
restrict: 'A',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
scope.$on('joeCalled', function(){
// Do something...
});
});
};
}
Edit:
Created a working example of this technique:
http://jsfiddle.net/9p3eyy5h/2/
Calling a function directly in the directive from the controller could be done by placing an empty object on the controller scope, binding it to the directive's scope with '=', and attaching a function to it in the directive's linking function/ controller, which could later be called by the wrapping controller.
Controller:
app.controller('leadsListing', ['$scope', function($scope){
// ...
$scope.directiveFuncs = {};
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.directiveFuncs.heyJoe();
};
}]);
Directive:
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount',
// Binding to the controller's func obj
funcs: '='
},
restrict: 'A',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
scope.funcs.heyJoe = function(){
// Do something...
}
});
};
}
HTML:
<div ng-controller="leadsListing">
<div recordfilter funcs="directiveFuncs"></div>
</div>
I would however advise to use my other approach, as it prevents direct dependency between the controller and the directive, and therefor, more robust, so it won't throw an error if the directive is missing or changes.
Working example:
http://jsfiddle.net/9pm3zg5s/1
I have 2 directives, one for searching and one for pagination. The pagination directive needs to access the search directive to find out what property we're currently searching by. When I load the page though, it throws an error saying Error: [$compile:ctreq] Controller 'search', required by directive 'pagination', can't be found!. However I have a controller setup in my search directive.
Here is my search directive:
angular.module('webappApp')
.directive('search', function ($route) {
return {
templateUrl: 'views/search.html',
restrict: 'E',
scope: {
searchOptions: '=',
action: '=',
currentProperty: '=',
currentValue: '='
},
controller: function($scope) {
$scope.searchBy = $scope.searchOptions[0].text;
$scope.searchByProperty = $scope.searchOptions[0].property;
$scope.setSearchBy = function(event, property, text) {
event.preventDefault();
$scope.searchBy = text;
$scope.searchByProperty = property;
};
$scope.search = function() {
$scope.searching = true;
$scope.currentProperty = $scope.searchByProperty;
$scope.currentValue = angular.element('#searchCriteria').val();
$scope.action($scope.searchByProperty, $scope.currentValue, function() {
$scope.searching = false;
});
};
$scope.reload = function() {
$route.reload();
};
}
};
});
Here is my pagination directive:
angular.module('webappApp')
.directive('pagination', function () {
return {
templateUrl: 'views/pagination.html',
restrict: 'E',
require: '^search',
scope: {
basePath: '#',
page: '=',
sort: '='
},
link: function(scope, element, attrs, searchCtrl) {
console.debug(searchCtrl);
scope.searchByProperty = searchCtrl.searchByProperty;
}
};
});
In order for one directive to use another's controller by use of require, it needs to either share the same element as the controller containing directive, or it has to be a child of it.
You can't use require in the way you have, where the elements are siblings.
Angular docs about directives, including require
If it doesn't make sense to rearrange the DOM in the way I've described, you should inject a service into both directives which contains the data/methods you wish to share between the two.
Note: you could also experiment with the $$nextSibling / $$prevSibling properties of the directives' scopes, but this would present only a very fragile solution
You cannot use require in directive like that, however , since the only thing you need to pass between directives is a string , just bind them to the same property in parent controller (it can be parent directive controller):
...
<div ng-app='app' ng-controller='MyCtrl as ctrl'>
<my-dir-one s1='ctrl.message'></my-dir-one>
<my-dir-two s2='ctrl.message'></my-dir-two>
and first directives:
app.directive('myDirOne', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s1: '=',
second directive
app.directive('myDirTwo', function ($route) {
return {
templateUrl: 'views/my-dir-one.html',
restrict: 'E',
scope: {
s2: '=',
Updating as there is some confusion as to what I am asking. I would like to use a directive to inject a variable into the controller used by that directive. I realize I can use the $scope for that, but I don't find that an intuitive solution.
Essentially I want my controller to have the proposal variable injected into it.
My intended usage:
<blah-directive proposal="proposal"></blah-directive>
The directive (so far):
app.directive('blahDirective', function () {
return {
restrict: 'E'
, transclude: true
, replace: true
, scope: {
proposal: '='
}
, templateUrl: 'blahTemp.html'
, controller: blahController
};
});
blahTemp.html
<form class="form-horizontal" role="form" name="myBidForm">
**{{ proposal }}**
</form>
this is displaying the value proposal variable in the $scope fine, but it is not what I want. Essentially I would like to define my controller like:
var blahController = function($scope, SomeOtherResource, proposal) {
}
If you want to inject locals into a controller use $controller.
Here is an example (plunker):
app.directive('blahDirective', function ($controller) {
return {
restrict: 'E',
scope: {
proposal : "="
},
transclude: true,
replace: true,
templateUrl: 'blahTemp.html',
link : function (scope, elm, attrs){
scope.proposal = {};
var locals = {
$scope: scope ,
proposal: scope.proposal
};
$controller('blahController', locals);
}
};
});