Angular js directive and restrict option - angularjs

i was reading a write up on directive from this url https://docs.angularjs.org/guide/directive
The restrict option is typically set to:
'A' - only matches attribute name
'E' - only matches element name
'C' - only matches class name
'M' - only matches comment
<div ng-controller="Controller">
<my-customer></my-customer>
</div>
angular.module('docsRestrictDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
restrict: 'E',
templateUrl: 'my-customer.html'
};
});
template html file
Name: {{customer.name}} Address: {{customer.address}}
please help me to understand what is the meaning of restrict: 'E', ?
i am looking for a example where restrict will be A or C
please show me the usage of restrict: 'A' and C
also tell me how could i pass multiple argument to directives ?
thanks

let say you have a directive 'myDirective'
if in restrict you have only C you can only use it as classes like this :
<div class="my-directive"></div>
If it's A it's as attribute :
<div my-directive></div>
If it's E it's as element
<my-directive></my-directive>
TO pass argument you generaly se attributes :
<div my-directive my-first-argument="toto" my-second-argument="titi"></div>
To get the value you have to way :
use the attr provided in link function
use the scope with one way or two way binding.
Personnaly i prefer the attribute approach when it comme to directive, element after and class in last. I have already bootstrap based on classes i don't want to clash with it.

please help me to understand what is the meaning of restrict: 'E', ?
The 'E' restriction will match by element name:
<my-customer></my-customer>
i am looking for a example where restrict will be A or C
please show me the usage of restrict: 'A' and C
The 'A' restriction will match by an element attribute:
<div my-customer=""></div>
The 'C' restriction will match by a class:
<div class="my-customer"></div>
also tell me how could i pass multiple argument to directives ?
It depends on your requirements, but one simple way is using an isolated scope:
.directive('myCustomer', function() {
return {
restrict: 'E',
scope: {
arg1: "#",
arg2: "#"
},
templateUrl: 'my-customer.html'
};
});
<my-customer arg1="first" arg2="second"></my-customer>
This SO answer gives a pretty good explanation of this process.

restrict is key to be used in DDO (Directive Definition Object) to inform what it Would be in view
for Example in DDO (Directive Definition Object)
Directive with restrict:E (Element)
Directive
app.directive('my-directive',function(){
return {
restrict: 'E', //means HTML Element
...
};
});
so directive in View for restrict:'E' as Element in HTML element as below
view
<myDirective></myDirective>
Directive with restrict:A (Attributes)
Directive
app.directive('my-directive',function(){
return {
restrict: 'A', //means HTML attribute
...
};
})
so directive in View for restrict:'A' as attribute in HTML element as below
view
<div myDirective></div>
Directive with restrict:'C' (class)
Directive
app.directive('my-directive',function(){
return {
restrict: 'C', //means HTML attribute
...
};
})
so directive in View for restrict:'C' as class in HTML element as below
view
<div class="myDirective"></div>
you can use Isolate scope (doesn't inherit from its parents scope) or you can pass variables to attributes which depends on your implementation both can used in directive's link and controller
for isolate scope DDO will be as
<my-directive variable1="hello" variable2="world" variable3="call()"></my-directive>
app.directive('myDirective',function(){
restrict:E,
scope:{
variable1:'#variable1',
variable2:'=varialbe2',
variable3:'&variable3'
}
})
Or you can also pass data through attributes and access them using attrs in Directive's link Function and $attrs DI in Directives Controller

Related

Is there a way to know the name of the model used in a directive?

Suppose I have the following directive:
.directive("counter", function () {
return {
restrict: "E",
scope:{
bindModel:'=ngModel'
},
templateUrl: "/directives/countertemplate.html"
and template:
<div id="counter" ng-class="{true: 'twitter-counter'}[bindModel.length > 140]">
{{140 - bindModel.length}}
</div>
is there a way I could know the name of the model passed into the directive:
<counter ng-model="twitterPost"></counter>
for example I would like to know from the context of the template that "twitterPost" was sent to the template, so I could perform a different set of logic than "facebookPost"
In link function you can retrieve all attributes from the element. DEMO
template: "<div ng-show='isTwitterPost'>Twitter</div><div ng-show='!isTwitterPost'>Facebook</div>",
link: function ($scope, element, attrs) {
$scope.isTwitterPost = attrs.ngModel === 'twitterPost';
}
You should add extra attributes.
<counter ng-model="twitterPost" is-twitter='true'></counter>
You should do it in angular way, passing object which carries all the info you need and process the logic in directive, for example
in this fiddle:
//jsfiddle.net/HB7LU/6052/

AngularJS directive not properly receiving link passed in attribute

I've got an AngularJS directive that is not placing a string (intended to be a relative path to an image) inside one of the attributes in an HTML element and I'm at a loss as to why.
My item looks like the following:
item : {
name: 'Test Name',
link: 'Assets/logo.png'
}
If I step through the javascript, I'm correctly receiving the link from the webservice, so that's not the problem as my Angular controller properly shows the link in the $scope.
The following is what I have in the template for that controller that I'm having the problem with:
<my-directive name="{{item.name}}" link="{{item.link}}"></my-directive>
Here's the javascript for my directive:
angular.module('myModule').directive('myDirective', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '/RelativePathToTemplateFile.html',
scope: {},
link: function($scope, element, attr, model) {
$scope.name = attr.name;
$scope.link = attr.link;
}
}
})
When I look at the rendered HTML, I have the following:
<div name="Test Name" link></div>
What's going on? How can I pass this link in properly?
Directive scope binding technique can resolve this issue. Try to use "#" to bind the directive property to the evaluated DOM attribute.
HTML
<div ng-controller="myCtrl">
<my-directive my-name="{{item.name}}" my-link="{{item.link}}"></my-directive>
</div>
Javascript
angular.module("myApp",[])
.controller("myCtrl",function($scope){
$scope.item = {
name:"Test Name",
link:"Assets/logo.png"
};
})
.directive("myDirective",function(){
return {
restrict: "E",
template: '<div name="{{myName}}" link="{{myLink}}">{{myName}}</div>',
replace: true,
scope:{
myName:"#",
myLink:"#"
}
};
});
Here is a jsFiddle DEMO, you could refer to it.
From the documentation:
function link(scope, element, attrs) { ... } where:
* scope is an Angular scope object.
* element is the jqLite-wrapped element that this directive matches.
* attrs is a hash object with key-value pairs of normalized attribute names and their corresponding attribute values.
so it's "attrs", not "attr"
try:
<myDirective name="item.name" link="item.link"></myDirective>
It will be better :)

ng-include on load with an object

This works
<div ng-include="template.html" onload="person='Jane'"></div>
^ This sets the local scope variable person in the include to 'Jane' (string)
But I want to pass a person object that is called user: {name: 'Jane' }
<div ng-include="template.html" onload="person=user"></div>
^ This sets the local scope variable person in the include to be 'undefined'
How is it possible to pass an object as a local variable to ng-include?
Maybe what you actually want is a custom directive:
<div person-directive="{name:'Jane'}"></div>
JS:
angular.module('myApp',[])
.directive('personDirective',function(){
return {
restrict: 'A',
scope: {
person: '=personDirective'
},
templateUrl: 'template.html'
};
});
With this, you bind the passed-in value to person in the loaded template.
Working fiddle
ng-include is not that reusable because it doesn't offer a way to set local variables. Using onload is bad because it litters the global scope. If the example gets a little more complicated, it'll fail.
Making a new directive instead of using ng-include is a cleaner solution.
The ideal usage looks like:
<div ng-include-template="template.html" ng-include-variables="{ person: 'Jane' }"></div>
<div ng-include-template="template.html" ng-include-variables="{ person: user }"></div>
The directive is:
.directive(
'ngIncludeTemplate'
() ->
{
templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
restrict: 'A'
scope: {
'ngIncludeVariables': '&'
}
link: (scope, elem, attrs) ->
vars = scope.ngIncludeVariables()
for key, value of vars
scope[key] = value
}
)
You can see that the directive doesn't use the global scope. Instead, the directive reads the object from ng-include-variables and add those members to its own local scope.
This is clean and generic.

Pass form to directive

I want to encapsulate my form fields in a directive so I can simply do this:
<div ng-form='myForm'>
<my-input name='Email' type='email' label='Email Address' placeholder="Enter email" ng-model='model.email' required='false'></my-input>
</div>
How do I access the myForm in my directive so I can do validation checks, e.g. myForm.Email.$valid?
To access the FormController in a directive:
require: '^form',
Then it will be available as the 4th argument to your link function:
link: function(scope, element, attrs, formCtrl) {
console.log(formCtrl);
}
fiddle
You may only need access to the NgModelController though:
require: 'ngModel',
link: function(scope, element, attrs, ngModelCtrl) {
console.log(ngModelCtrl);
}
fiddle
If you need access to both:
require: ['^form','ngModel'],
link: function(scope, element, attrs, ctrls) {
console.log(ctrls);
}
fiddle
Here a complete example (styled using Bootstrap 3.1)
It contains a form with several inputs (name, email, age, and country).
Name, email and age are directives. Country is a "regular" input.
For each input is displayed an help message when the user does not enter a correct value.
The form contains a save button which is disabled if the form contains at least one error.
<!-- index.html -->
<body ng-controller="AppCtrl">
<script>
var app = angular.module('app', []);
app.controller('AppCtrl', function($scope) {
$scope.person = {};
});
</script>
<script src="inputName.js"></script>
<script src="InputNameCtrl.js"></script>
<!-- ... -->
<form name="myForm" class="form-horizontal" novalidate>
<div class="form-group">
<input-name ng-model='person.name' required></input-name>
</div>
<!-- ... -->
<div class="form-group">
<div class="col-sm-offset-2 col-sm-4">
<button class="btn btn-primary" ng-disabled="myForm.$invalid">
<span class="glyphicon glyphicon-cloud-upload"></span> Save
</button>
</div>
</div>
</form>
Person: <pre>{{person | json}}</pre>
Form $error: <pre>{{myForm.$error | json}}</pre>
<p>Is the form valid?: {{myForm.$valid}}</p>
<p>Is name valid?: {{myForm.name.$valid}}</p>
</body>
// inputName.js
app.directive('inputName', function() {
return {
restrict: 'E',
templateUrl: 'input-name.html',
replace: false,
controller: 'InputNameCtrl',
require: ['^form', 'ngModel'],
// See Isolating the Scope of a Directive http://docs.angularjs.org/guide/directive#isolating-the-scope-of-a-directive
scope: {},
link: function(scope, element, attrs, ctrls) {
scope.form = ctrls[0];
var ngModel = ctrls[1];
if (attrs.required !== undefined) {
// If attribute required exists
// ng-required takes a boolean
scope.required = true;
}
scope.$watch('name', function() {
ngModel.$setViewValue(scope.name);
});
}
};
});
// inputNameCtrl
app.controller('InputNameCtrl', ['$scope', function($scope) {
}]);
Edit 2: I'll leave my answer, as it might be helpful for other reasons, but the other answer from Mark Rajcok is what I originally wanted to do, but failed to get to work. Apparently the parent controller here would be form, not ngForm.
You can pass it in using an attribute on your directive, although that will get rather verbose.
Example
Here's a working, simplified jsFiddle.
Code
HTML:
<div ng-form="myForm">
<my-input form="myForm"></my-input>
</div>
Essential parts of the directive:
app.directive('myInput', function() {
return {
scope: {
form: '='
},
link: function(scope, element, attrs) {
console.log(scope.form);
}
};
});
What's happening
We've asked Angular to bind the scope value named in the form attribute to our isolated scope, by using an '='.
Doing it this way decouples the actual form from the input directive.
Note: I tried using require: "^ngForm", but the ngForm directive does not define a controller, and cannot be used in that manner (which is too bad).
All that being said, I think this is a very verbose and messy way to handle this. You might be better off adding a new directive to the form element, and use require to access that item. I'll see if I can put something together.
Edit: Using a parent directive
OK, here's the best I could figure out using a parent directive, I'll explain more in a second:
Working jsFiddle using parent directive
HTML:
<div ng-app="myApp">
<div ng-form="theForm">
<my-form form="theForm">
<my-input></my-input>
</my-form>
</div>
</div>
JS (partial):
app.directive('myForm', function() {
return {
restrict: 'E',
scope: {
form: '='
},
controller: ['$scope', function($scope) {
this.getForm = function() {
return $scope.form;
}
}]
}
});
app.directive('myInput', function() {
return {
require: '^myForm',
link: function(scope, element, attrs, myForm) {
console.log(myForm.getForm());
}
};
});
This stores the form in the parent directive scope (myForm), and allows child directives to access it by requiring the parent form (require: '^myForm'), and accessing the directive's controller in the linking function (myForm.getForm()).
Benefits:
You only need to identify the form in one place
You can use your parent controller to house common code
Negatives:
You need an extra node
You need to put the form name in twice
What I'd prefer
I was trying to get it to work using an attribute on the form element. If this worked, you'd only have to add the directive to the same element as ngForm.
However, I was getting some weird behavior with the scope, where the myFormName variable would be visible within $scope, but would be undefined when I tried to access it. That one has me confused.
Starting with AngularJS 1.5.0, there is much cleaner solution for this (as opposed to using the link function directly). If you want to access a form's FormController in your subcomponent's directive controller, you can simply slap the require attribute on the directive, like so:
return {
restrict : 'EA',
require : {
form : '^'
},
controller : MyDirectiveController,
controllerAs : 'vm',
bindToController : true,
...
};
Next, you'll be able to access it in your template or directive controller like you would any other scope variable, e.g.:
function MyDirectiveController() {
var vm = this;
console.log('Is the form valid? - %s', vm.form.$valid);
}
Note that for this to work, you also need to have the bindToController: true attribute set on your directive. See the documentation for $compile and this question for more information.
Relevant parts from the documentation:
require
Require another directive and inject its controller as the fourth argument to the linking function. The require property can be a string, an array or an object:
If the require property is an object and bindToController is truthy, then the required controllers are bound to the controller using the keys of the require property. If the name of the required controller is the same as the local name (the key), the name can be omitted. For example, {parentDir: '^parentDir'} is equivalent to {parentDir: '^'}.
Made your 'What I'd prefer' fiddle work!
For some reason you could see the "$scope.ngForm" string in a console.log, but logging it directly didn't work, resulting in undefined.
However, you can get it if you pass attributes to the controller function.
app.directive('myForm', function() {
return {
restrict: 'A',
controller: ['$scope','$element','$attrs', function($scope,$element,$attrs) {
this.getForm = function() {
return $scope[$attrs['ngForm']];
}
}]
}
});
http://jsfiddle.net/vZ6MD/20/

exposing an object in angularjs directive scope, can't access properties

So, I have the following relatively simple Angularjs directive
app.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
site: '#',
index: '#'
},
template: '<div>{{site}}</div>',
replace: true,
}
});
And here is where I call the directive in HTML
<div id="eventGraphic" class="span12">
<my-directive ng-repeat="site in IEvent.sites" site="{{site}}" index="{{$index}}"></my-directive>
</div>
Which, given that each site is an object, produces this output (copied from browser)
{"name":"Hurlburt","_id":"5148bb6b79353be406000005","enclaves":[]}
{"name":"Walker Center","_id":"5148cca5436905781a000005","enclaves":[]}
{"name":"test1","_id":"5148ce94436905781a000006","enclaves":[]}
{"name":"JDIF","_id":"5148cf37436905781a000007","enclaves":[]}
However, if I change the template in the directive to
template: '<div>{{site.name}}</div>',
it does not produce any output. This seems like a fairly straightforward use case, any ideas what I could be doing wrong? The desired output would be just the name field in each object.
You need to use '=' to map the object. '#' implies you're just passing a string value to the new scope.
app.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
site: '=', //two-way binding
index: '#' //just passing an attribute as a string.
},
template: '<div>{{site}}</div>',
replace: true,
}
});
Then in your markup, don't use a binding in the attribute, just pass the expression:
<div id="eventGraphic" class="span12">
<!-- below, site="site" is passing the expression (site) to
the two way binding for your directive's scope,
whereas index="{{$index}}" is actually evaluating the expression
($index) and passing it as a string to the index attribute,
which is being put directly into the directive's scope as a string -->
<my-directive ng-repeat="site in IEvent.sites"
site="site"
index="{{$index}}"></my-directive>
</div>

Resources