Angular: regarding passing scope data to directives - angularjs

I am fairly new to Angular and still learning. I posted a question regarding how to pass scope data to directives. Two nice people answered my question but the explanations were very short, so I do not understand some of the code in two of the answers. I will post the two answers and accompanying code here, and if possible someone with be able to answer my questions.
1st set of Code
<li my-directive price="item.price" ng-repeat="item in products">{{item.name}} — {{item.price}}</li>
myApp.directive('myDirective', function(){
return {
scope: { price: '=' },
require: 'ngModel',
link : function(scope){
console.log(scope.price)
},
controller: function(scope, element, attrs, ngModel){
console.log(ngModel.price);
console.log(scope.price);
}
}
});
point 1
Why is scope declared in the directive ? Regarding 'isolated scope' property name has to be price, is there a reason for the variable name here? Can we give it a different name ?
point 2
I am new to ng. This is the first time I've noticed a 'controller declared in directive'. When and why do people declare a controller in a directive? Mostly we declare a controller outside of a directive. Are a controller function and a controller inside a directive both the same, or different?
point 3
What is the meaning of require: 'ngModel', ? If we do not write require: `ngModel what will fail to work?
2nd set of Code
angular.module('myApp')
.directive('myDirective', function () {
return {
transclude: true,
restrict: 'AEC',
scope: {
name: '=',
price: '='
},
templateUrl: 'my-directive.html',
link: function (scope, element, attr) {
}
}
}
});
<li my-directive ng-repeat="item in products" price="item.price" name = "item.name"></li>
points 1
What is transclude: true ? What is the meaning of transclude = true or false? In what kind of situation do people work using transclude: true or false?
points 2
Where I have to put file my-directive.html in folder, what would be the location of the file?
Will Angular load this template file automatically?

1st set
Point 1: why scope declared in directive ? isolated scope property name has to be price ? here can we give different name ?
Ans: scope is declared in directive so that each instance of directive will have their own scope independent of each other. No it need not be price, it can be any variable name as per your choice. It's a general practice to use variable names in context of directive's usage.
Point 2:
i am new in ng. this is first time i notice controller declared in directive. why and when people declare controller in directive ? mostly we declare controller out side of directive. controller function and controller inside in directive both are same or different?
Ans: This controller is the controller of your directive and is different than what you normally see for a module.
Point 3:
what is meaning of require: 'ngModel', ? if we do not write require: `ngModel then what will not work?
Ans: When using your directive, it forces it to be used along with the attribute/controller ng-model. You can refer this answer for more details.
2nd set
Point1: what is transclude: true ? what is the meaning of transclude = true or false? in what kind of situation people work with transclude: true or false?
Ans:It's setting transclude as true. As per documentation:
Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
transclude: true allows us to wrap a users template with our template. I've used it as true in a directive I wrote where if a table is empty, I show a error message showing No Data Available. In my case, table will not by displayed at all, instead this directive's template is displayed.
Points 2:
where i have to put the file my-directive.html in folder. what would be the location of the file?
angular will load this template file automatically?
Ans: You can place this template anywhere in the project folder. Like, in a folder named views or templates. As long as you provide correct path in templateUrl, angular will find the file and use it in the directive.

Related

AngularJS Directives - why do I need to call $compile in link() for expression defined in compile()?

I have this code:
app.directive('foo', function($compile) {
return {
restrict: 'E',
scope: {},
template: '<span>{{bar}}</span>',
compile: function(element, attrs) {
element.attr('title', '{{bar}}');
return function(scope, element, attrs) {
scope.bar = 'hello';
$compile(element)(scope);
}
}
}
});
Plunkr:
http://plnkr.co/edit/nFTgvYqoiFAthmjoizWS?p=preview
If I remove the $compile bit in the link function then the title attribute remains with the expression text ({{bar}}) and not the value ('hello');
Anyone can explain why?
I thought (from what I read in the docs) that this is what the compile phase is for - manipulating the template and preparing it for the link with scope and data binding. Why do I need to manually call $compile again? Isn't the template already compiled?
Maybe the phase names should be changed from compile, preLink, and postLink to postCompile, preLink, and postLink. The postCompile phase is availble to manipulate DOM before linking to a scope, at this point the linking function has been created but no scopes have been created. DOM can be added that requires no compilation. If additional elements are added that include directives or require interpolation, those additional elements need to be compiled and linked in order for the directives and interpolation to work.
To manupulate the template before compile, furnish a function to the template property: template: function(tElement, tAttrs) {}. For more information, see AngularJS Comprehensive Directive API Reference -- Template.
can you share a reference to "DOM can be added that requires no compilation, etc." or explain how did you found out about this?
Some sources of information:
AngularJS Developers Guide -- HTML Compiler
AngularJS Developers Guide -- Creating a Directive that Manipulates the DOM

What is the correct way to return a value from a directive?

I have the following set up as a directive in my angular project:
{
restrict: "AE",
replace: true,
template: template,
require: "ngModel",
scope: {
chosen: "=ngModel",
choices: "=choices",
placeholder: "#placeholder"
}
}
I have everything working internally for my directive, the missing piece right now is that when I select a value inside of it, the parent scope containing my directive isn't receiving any kind of update. Despite me making assigments to chosen from anywhere inside of my directive.
As the title states, what's the simplest way for me to assign a value chosen inside of my directive, to it's parent's scope?
Ideally I'd like the solution to:
Not require me to use the link function - I feel like this can be done declaratively
Not require my directive to guess anything about it's parent scope
As a followup question, is there any reason to use ngModel in this circumstance? Is it or could it be beneficial? Or could I just as easily get away with recycling a name attribute which contains the parent scope's desired return value?

ng-select is not working along with a custom directive

I have created a plunker in order to emphasize the problem:
http://plnkr.co/edit/QHUpCv?p=preview
If I remove the custom attribute, or move the ng-select out of it, the companies are listed as the should, in case I use ng select with or within the custom attribute directive that I have created it breaks.
I suspect that some kind of $watch is required there inside of the scope for menuCtrl, but I have no idea whatsoever how to implement it.
As far as i can tell, the problem is that you are generating a new scope for your directive, so a quick fix would be to forbid that via:
// [...]
restrict: "A",
scope: false,
link: //..
I made a plunkr here to illustrate.
If you do want it this way and prefer an own scope for this directive, you can pass in the values for the select, i.e.
<div restrict companies="companies" access="admin">
and read it in in the scopeof the directive:
restrict: 'A',
prioriry: 100000,
scope: {
companies: '='
},
of course, you would then use the companies directly with the select:
<select ng-model="data.selectedCompany" ng-options="company for (id, company) in companies">

using ng-show in directive template

I am trying to define a directive to show a checkmark when a question has been answered.
questModule.directive('showCheckmarkOnDone', function () {
return {
transclude: true,
template: "<div ng-transclude></div><img src='img/checkmark.svg' " +
"ng-show='scope.questions[type][number-1].done' " +
"class='checkmark' style='height: {{height}}px; width: {{height}}px;'>",
link: function (scope, element, attrs) {
scope.height = $(".app").height()/12;
scope.number = attrs.number;
scope.type = attrs.type;
}
};
});
The scope.questions[type][number-1].done exists in my controller for the page, and I can see that it is being updated correctly when I press the done button. However, the directive does not register the change. I tried putting a $watch in the link function - that didn't help either. I think I'm a bit confused about how to get my directive scope to play nicely with my controller scope - any thoughts on how I can give this directive access to an object that exists in an outside controller? (scope.questions)
This is not a valid way to define directive scope:
scope: '#'
You can either (a) not define it, (b) set it to true, or (c) set it to {} (an object). See the docs for more info: http://docs.angularjs.org/guide/directive (find the header: "Directive Definition Object")
In this case, I imagine if you remove it, you may be OK, because it will allow scope.questions to be visible from your directive. If you reproduce the issue in jsfiddle.net or plnkr.co, it would be much easier to assist you.
Edit:
Your directive generally should have 1 parent element
You should not use scope in your directive's HTML, it's implied
I think you said as much in your comment, but you should strive to make your directive more generic by passing in scope.questions[0][0].done instead of looking it up in your directive's HTML using attributes.
Here's a working example: http://jsfiddle.net/EZy2F/1/

Directive scope attributes break depending on attribute name

I have a very strange phenomenon with a directive and an isolated scope, where the attributes in the scope work or do not work depending on the naming of the attribute. If I use
{check:'#check'}
it works just fine and as expected. However,if I use:
{checkN:'#checkN'}
the defined function never gets assigned. An example would look like:
HTML:
<item ng-repeat="list_item in model.list" model="list_item" checkN="checkName()" check="checkName()" position="$index"></item>'
Javascript
app.directive('item', function(){
return {
restrict: 'E',
replace : false,
scope:{
$index: '=position',
check: '&check',
checkN: '&checkN',
model:'='
},
template: '',
link: function(scope, element, attrs){
console.log(scope.check())
console.log(scope.checkN())
}
}
});
The console will then give me the following:
The checkName function has been called [which is the return string of the function]
undefined
It is really possible that it depends on the usage of capital letters? This would be very "unexpected" behaviour.
Thanks for your help
schacki
Html is case insensitive, therefore myAttribute and myattribute would be indistinguishable from each other depending on the browser. Angularjs' authors made a design decision about passing from html to javascript and vice-versa in terms of directives.
ngRepeat directive would be used as ng-repeat in the view(html).
Likewise, your directive checkN should be used as check-n for angular to recognise that as directive.

Resources