Angular directive to match multiple attributes - javascript

Can I define an angular directive so that it would match multiple similar terms
i.e.
angular.module('search').directive('platformPreload', function() {
return {
link: function(scope, element, attrs) {
}
}
}
Would match both the following:
<div platform-preload-terms="[]"></div>
<div platform-preload-suggestions="[]"></div>

There are no wildcard directive declaration.
But you can isolate the function and repeat the definition:
angular.module('search')
.directive('platformPreload', PlatFunction)
.directive('platformPreloadSuggestions', PlatFunction)
PlatFunction() {
return {
link: function(scope, element, attrs) { }
}
}

You could create isolated scopes for the directive which will allow you to use these attributes in the isolated scope on the directive. Like this:
angular.module('myApp', [])
.controller('appController', function($scope) {
})
.directive('platformPreload', function() {
return {
restrict: 'A',
scope: {
platformTerms: '#',
platformSuggestions: '#'
},
link: function($scope, element, attrs) {
console.log('DIRECTIVE');
if ($scope.platformTerms) {
console.log($scope.platformTerms);
}
if ($scope.platformSuggestions) {
console.log($scope.platformSuggestions);
}
}
};
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body ng-app="myApp" ng-controller="appController">
<div platform-preload platform-terms="These are the terms"></div>
<div platform-preload platform-suggestions="These are the suggestions"></div>
</body>
</html>

Related

Difference between passing attribute to directive in {{}} curly bracket and without curly brackets in angular?

I am trying to set watcher on an attribute in angular directive like this
angular.module("myApp",[]);
angular.module("myApp").directive('myDirective', function () {
return {
restrict: 'A',
scope: true,
link: function ($scope, element, attrs) {
$scope.$watch(function () {
return [attrs.attrOne];
}, function (newVal) {
console.log(newVal);
}, true);
}
};
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"> </script>
</head>
<body ng-app="myApp">
<div my-directive attr-one="{{x}}">{{x}}</div>
<input ng-model="x" />
</body>
</html>
In the above example I passed the ng-model in {{}} to the directive attribute and watcher works like charm
but when I try to pass ng-model directly to directive attribute the watcher doesn't work anymore, check the code below
angular.module("myApp",[]);
angular.module("myApp").directive('myDirective', function () {
return {
restrict: 'A',
scope: true,
link: function ($scope, element, attrs) {
$scope.$watch(function () {
return [attrs.attrOne];
}, function (newVal) {
console.log(newVal);
}, true);
}
};
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>
</head>
<body ng-app="myApp">
<div my-directive attr-one="x">{{x}}</div>
<input ng-model="x" />
</body>
</html>
I am not getting what magic is {{}} brackets doing here. Any explanation for some wise man?
EDIT: I don't want to create an isolated scope because the element on which it applies uses the parent scope inside it

pass dynamic controller to angular directive

i am familiar with the syntax of controller & name but i'm trying to create a generic directive that will get a list of items and for each item i need to specify a controller.
This is my main directive:
function controlPanel() {
var directive = {
restrict: 'E',
replace: true,
scope: {
controlPanelItems: "=sbControlPanelItems"
},
templateUrl: 'control-panel.html',
link: link
};
return directive;
function link(scope, element) {
}
}
Here is the directive template:
<sb-control-panel-item ng-repeat="controlPanelItem in controlPanelItems"
sb-title="controlPanelItem.title"
sb-template-url="controlPanelItem.templateUrl"
sb-control-panel-item-controller="controlPanelItem.controller"></sb-control-panel-item>
My issue is with the sb-control-panel-item-controller attribute.
Angular throws exception when i'm passing variable, it work's great when i'm passing simple string (the name of the controller).
Here is the code of the control-panel-item directive:
function controlPanelItem() {
var directive = {
restrict: 'E',
replace: true,
scope: {
title: '=sbTitle',
templateUrl: '=sbTemplateUrl'
},
templateUrl: 'control_panel_item.html',
controller: '#',
name: 'sbControlPanelItemController',
link: link
};
return directive;
function link(scope, iElement, iAttributes, controller) {
}
}
Maybe there is a way to inject the controller through the link function and then i'll just pass it through the scope?
You can use the $controller service to instantiate whatever controller dynamically inside the directive, check this plunkr.
Just bear in mind that if you wanted to specify a controller statically now, you would need to enclose it in single quotes.
Basically the code would be like:
function MainCtrl() {
this.firstCtrl = 'FirstCtrl';
this.secondCtrl = 'SecondCtrl';
}
function FirstCtrl() {
this.name = 'First Controller';
}
function SecondCtrl() {
this.name = 'Second Controller';
}
function fooDirective() {
return {
scope: {
ctrl: '='
},
template: '<div>{{foo.name}}</div>',
controller: ['$controller', '$scope', function($controller, $scope) {
var foo = $controller($scope.ctrl, {$scope: $scope});
return foo;
}],
controllerAs: 'foo',
link: function ($scope, $element, $attrs, $ctrl) {
console.log($scope.ctrl);
}
};
}
angular
.module('app', [])
.directive('fooDirective', fooDirective)
.controller('MainCtrl', MainCtrl)
.controller('FirstCtrl', FirstCtrl)
.controller('SecondCtrl', SecondCtrl);
and this would be the HTML
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#1.5.8" data-semver="1.5.8" src="https://code.angularjs.org/1.5.8/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller="MainCtrl as main">
<h1>
Test
</h1>
<foo-directive ctrl="main.firstCtrl">
"name: " {{foo.name}}
</foo-directive>
<foo-directive ctrl="main.secondCtrl">
{{foo.name}}
</foo-directive>
</body>
</html>
========================================================================
WRONG OLD ANSWER
From this blog entry seems to be an undocumented property that allows you to do exactly what you need.
function FirstCtrl() {
this.name = 'First Controller';
}
function SecondCtrl() {
this.name = 'Second Controller';
}
function fooDirective() {
return {
scope: {},
name: 'ctrl',
controller: '#',
controllerAs: 'foo',
template: '<div></div>',
link: function ($scope, $element, $attrs, $ctrl) {
}
};
}
angular
.module('app', [])
.directive('fooDirective', fooDirective)
.controller('FirstCtrl', FirstCtrl)
.controller('SecondCtrl', SecondCtrl);
So all you need to do in your directive is add a property name linked to the attribute you will use with the name of your controller.
<foo-directive ctrl="FirstCtrl"></foo-directive>
<foo-directive ctrl="SecondCtrl"></foo-directive>
If your directive, as per your question, needs to be from a property rather than a string, use {{}} notation:
<sb-control-panel-item ng-repeat="controlPanelItem in controlPanelItems"
sb-title="controlPanelItem.title"
sb-template-url="controlPanelItem.templateUrl"
sb-control-panel-item-controller="{{controlPanelItem.controller}}"></sb-control-panel-item>

Functions in compile isolated directive - AngularJS

I'm trying to understand how to call an external function from a built-in-compile directive.
Here is an example: http://plnkr.co/edit/bPDaxn3xleR8SmnEIrEf?p=preview
html:
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-controller="myController as vm">
<my-directive callback="vm.saveAction()" label="click me!"></my-directive>
</body>
</html>
js:
app = angular.module('app', []);
app.controller('myController', function() {
var vm = this;
vm.saveAction = function() {
alert("foo!");
}
});
app.directive('myDirective', function() {
var directive = {
restrict: 'E',
scope: {
callback: '&'
},
compile: compile,
link: link
};
return directive;
function compile(element, attrs) {
var template = '<button ng-click="action()">'+attrs.label+'</button>';
element.replaceWith(template);
}
function link(scope, element) {
scope.action = function() {
// ...something usefull to do...
scope.callback();
}
}
});
I know that I could easly do it from the link function (and it works from there), but I really need to do it from the compile method (this is just a simplified version to better point out the problem).
Could someone help me?
Thank you!
Use template directive to do this
app.directive('myDirective', function() {
var directive = {
restrict: 'E',
scope: {
callback: '&',
label: '#'
},
template: '<button ng-click="action()">{{label}}</button>',
link: link
};
return directive;
function link(scope, element, attrs) {
console.log(scope.label);
scope.action = function() {
// ...something usefull to do...
scope.callback();
}
}
});
Or if you want to use compile method, use pre or post method and compile yourself:
function compile(element, attrs) {
return {
pre: function(scope, elem, attrs) {
var template = $compile('<button ng-click="action()">'+attrs.label+'</button>')(scope);
element.replaceWith(template);
},
post: function (scope, elem, attrs) {
// or here
}
}
}

Call function in controller from a nested directive

I have two nested directives for building a treeview in Angular:
Parent directive:
myApp.directive('nodes', function() {
return {
restrict: "E",
replace: true,
scope: {
nodes: '='
},
template: "<ul><node ng-repeat='node in nodes' node='node'></node></ul>"
}
});
Child directive:
myApp.directive('node', function($compile) {
return {
restrict: "E",
replace: true,
scope: {
node: '='
},
template: "<li>{{node.ObjectName}}</li>",
link: function(scope, element, attrs) {
if (angular.isArray(scope.node.Children)) {
element.append("<nodes nodes='node.Children'></nodes>");
$compile('<nodes nodes="node.Children"></nodes>')(scope, function(cloned, scope) {
element.append(cloned);
});
}
}
}
});
The controller:
function myController($scope, DataService) {
$scope.init = function() {
DataService.getData(0, 0).then(function(data) {
$scope.treeNodes = $.parseJSON(data.d);
});
}
$scope.focusNode = function(prmNode) {
console.log(prmNode);
}
}
HTML:
<div ng-app="testTree" ng-controller="myController">
<div ng-init="init()">
<nodes nodes='treeNodes'></nodes>
</div>
</div>
My question is how can I implement a click on the <li> which will call the "focusNode" function in the controller?
You could pass in the function through an attribute.
Javascript
var myApp = angular.module('myApp', []);
myApp.directive('nodes', function() {
return {
restrict: "E",
replace: true,
scope: {
nodes: '=',
clickFn: '&'
},
template: "<ul><node ng-repeat='node in nodes' node='node' click-fn='clickFn()'></node></ul>"
}
});
myApp.directive('node', function($compile) {
return {
restrict: "E",
replace: true,
scope: {
node: '=',
clickFn: '&'
},
template: "<li><span ng-click='clickFn()(node)'>{{node.ObjectName}}</span></li>",
link: function(scope, element, attrs) {
if (angular.isArray(scope.node.Children)) {
element.append("<nodes nodes='node.Children' click-fn='clickFn()'></nodes>");
$compile('<nodes nodes="node.Children" click-fn="clickFn()"></nodes>')(scope, function(cloned, scope) {
element.append(cloned);
});
}
}
}
});
function myController($scope) {
$scope.focusNode = function(prmNode) {
console.log(prmNode);
}
$scope.root = {
ObjectName: 'Root',
Children:[{
ObjectName: 'A',
Children: [{
ObjectName: 'B'
}, {
ObjectName: 'C'
}]
}]
};
}
HTML
<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js#*" data-semver="1.2.8" src="http://code.angularjs.org/1.2.8/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app='myApp' ng-controller="myController">
<node node="root" click-fn="focusNode"></node>
</body>
</html>
Since I can't comment until I get 50 rep cred :(...
I just wanted to add that I also got an error when trying the plunkr that W.L.Jared shared above. To fix the error, I changed the controller from a global function to:
angular.module('myApp').controller('myController', function($scope){...})
The error went away.
Good answer though :) Exactly what I was looking for.

Linking and controller function of directive which is represented as an attribute of another directive don't work

Have the following problem. I want to make two directives. One of them will be an attribute for another.
Something like this.
<html>
<title>Directives</title>
<head lang="en">
<meta charset="utf-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js" type="text/javascript"></script>
<script src="main.js"></script>
</head>
<body ng-app="app">
<outer inner></outer>
</body>
</html>
The directive source code is here:
var app = angular.module('app', []);
app.directive('inner', function() {
return {
require: "^ngModel",
restrict: "AC",
transclude: true,
replace: false,
templateUrl: /* here is a path to template it's not interesting*/,
controller: function($scope) {
console.log('controller...');
},
link: function(scope, element, attrs) {
console.log('link...');
}
};
});
app.directive('outer', function($q, $rootScope) {
return {
require: "^ngModel",
restrict: "E",
replace: true,
scope: { /* isolated scope */ },
controller: function($scope) {},
templateUrl: /* path to template */,
link: function (scope, elem, attrs, ctrl) {}
}
});
The problem is that controller of outer works, but inner doesn't... Neither link nor controller function works... Can't understand what is wrong...
Any ideas?
The reason its not working is because both directives have been asked to render a template on the same element, and its ambiguous as to which one should be given priority.
You can fix this by giving the inner directive priority over the outer directive (higher numbers indicate higher priority).
Inner:
app.directive('inner', function() {
return {
priority:2,
restrict: "AC",
transclude: true,
replace: false,
template: "<div>{{say()}}<span ng-transclude/></div>",
controller: function($scope) {
$scope.message = "";
$scope.say = function() {
return "this is message";
};
// $scope.say(); // this doesn't work as well
console.log('controller...');
},
link: function(scope, element, attrs) {
// alert('hey');
// console.log('link...');
}
};
});
Also, both directives cannot transclude their contents. One must be 'transclude:false' and the other must be transclude:true.
app.directive('outer', function($q, $rootScope) {
return {
priority:1,
restrict: "E",
transclude:false,
scope: { /* isolated scope */ },
controller: function($scope) {
$scope.message = "";
$scope.sayAgain = function() {
return "one more message";
};
$scope.sayAgain(); // this doesn't work as well
},
template: "<div>{{sayAgain()}}</div>",
link: function (scope, elem, attrs, ctrl) {}
}
});
Here is a working fiddle:
JSFiddle

Categories

Resources