Can't put ng-controller on directive? - angularjs

I have a very simple angular example at http://jsfiddle.net/7eL47/3/. The rendered output of the code shows "Foobar" on the page.
The template for this rendered output is:
<div ng-app="myApp" ng-controller="MenuController">
<unordered-list>
Foo{{foo}}
</unordered-list>
</div>
However, when I change the location of ng-controller to the unordered-list as shown below, "Foobar" no longer appears--it's just "Foo." The value of {{foo}} is never replaced with "bar".
<div ng-app="myApp">
<unordered-list ng-controller="MenuController">
Foo{{foo}}
</unordered-list>
</div>
Why don't I see "Foobar" still when I change the ng-controller directive to be on the unordered-list element?

In your first example, ngController is a parent to unordered-list. So it has visibility to foo.
Your second example:
<unordered-list ng-controller="MenuController">
Results in two sibling scopes each with a parent of ngApp.
Both your directive and the ngController directive use scope: true. scope: true causes a child scope to be created for that directive which inherits from the parent. Therefore you end up with sibling scopes.
Thus, in the second example, $scope.foo is not visible, since it's no longer on the scope unoderedList inherits from (but rather on a sibling scope).

Related

Showing or hiding the parent element of a custom directive

I have a query concerning a custom directive example found within Angular's docs here.
When the select method is called (from the parent controller in order to show / hide a pane), the relevant <div class="tab-pane" ng-show="selected"> element is shown / hidden, which is how it should function as per the example, naturally.
However, I'd like to hide the parent element, which is <my-pane title="..."> so that <my-pane title="..."> isn't left visible (even though all it's child content is hidden.) In other words, move the ng-show directive from <div class="tab-pane" ng-show="selected"> to <my-pane title="..." ng-show="selected">
I assumed that each <my-pane title="..."> had its own isolated scope with a unique $scope.id, so it should be easy to accomplish by targetting the relevant scope and updating a selected value via the tab links, but for the life of me I cannot seem to make it work.
After inspecting both <my-pane title="...">'s in console it appears as they both have the same $scope.id, which shouldn't be the case as they're separate scopes, right?
Something is missing from my understanding of isolated scopes perhaps. Any pointers would be great.

Why must I set "trasnclude: true" in an Angular DDO?

I know what transclude: true does, but I've always wondered: "why must I put transclude: true in my DDO as well as a ng-transclude in my template?".
What's going on internally that forces angular to be redundant? Is it security/XSS protection? Performance?
The ngTransclude documentation explains the separation of the two (emphasis mine):
[ngTransclude is a] directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
This means:
transclude: true indicates that the directive makes its content available for transclusion.
ng-transclude indicates where the content should go.
A directive with transclusion enabled doesn't actually have to handle the transclusion of it's own content. It can let a child element choose where to put the transcluded content.
Here is a (trivial) example that shows how transclusion can be handled by a child directive inside a parent:
<!-- Application template -->
<parent-el>
<h1>Transcluded content</h1>
</parent-el>
<!-- <parent-el> template -->
<p>I am the parent element</p>
<child-el></child-el>
<!-- <child-el> template -->
<p>I am the child element</p>
<div ng-transclude></div>
This is how the content will then be rendered in the page:
<!-- Rendered content -->
<parent-el>
<p>I am the parent element</p>
<child-el>
<p>I am the child element</p>
<div>
<h1>Transcluded content</h1>
</div>
</child-el>
</parent-el>
There are three kinds of transclusion depending upon whether you want to transclude just the contents of the directive's element, the entire element or multiple parts of the element contents:
true - transclude the content (i.e. the child nodes) of the directive's element.
'element' - transclude the whole of the directive's element including any directives on this element that defined at a lower priority than this directive. When used, the template property is ignored.
{...} (an object hash): - map elements of the content onto transclusion "slots" in the template.
-- https://docs.angularjs.org/api/ng/service/$compile#transclusion

AngularJS : Correct usage of ng-model

I was reading Angular js docs when I came across the issues mentioned related to using ng-model with directives like ng-include , ng-switch , and ng-view.The reason mentioned was child scope and parent scope but I was not able to understand it completely.
Also it was mentioned that issue with primitives can be easily avoided by following the "best practice" of always have a '.' in your ng-model.
Here's the link
Can anyone please explain it in less-technical language?
ng-include, ng-switch and ng-if creates child scope.
It means that if you create
<div ng-controller="MyCtrl">
<div id="innerDiv" ng-if="!something">
<input ng-model="model">
</div>
</div>`
the model will be created in scope created by the #innerDiv (because of using ng-if). It might be problematic if you want to use model value in the controller, because it won't be possible (Parent scope has no access to properties of child scope!).
The solution is to use $parent.model in <input ng-model="model">. Then the property will be changed in parent's scope and that is what you usually want to achieve.
However using $parent might look not good for someone, so better solution is to create a named model in the controller. The you can use it and follow the rule "always have a '.' in your ng-model." Child scope created by ng-if has access to parrent scope, so he can use already defined $scope.model and change $scope.model.text property.
Controller:
$scope.model = {};
Html:
<div ng-controller="MyCtrl">
<div id="innerDiv" ng-if="!something">
<input ng-model="model.text">
</div>
</div>`
(But remember that if you not define $scope.model in the controller, it would behave like in first example).
If you are not sure that you are in the same scopes, you can check it by displaying scope's $id. Simply add in html {{$id}} above ng-if (or ng-include, ng-switch) and inside.
<div ng-controller="MyCtrl">
scope id: {{$id}}
<div id="innerDiv" ng-if="!something">
child scope id:{{$id}}
</div>
<div>scope id again {{$id}}</div>
</div>`
Some examples:
https://jsfiddle.net/La90btfh/3/

Angular directive new scope rule

The Angular directive documentation says: "If set to true, then a new scope will be created for this directive. If multiple directives on the same element request a new scope, only one new scope is created. The new scope rule does not apply for the root of the template since the root of the template always gets a new scope."
My question is the last sentence. I assume that "template" refers to the directive's template, but on testing a simple directive whether it has or doesn't have a template, no new scope is created without setting "scope: true". Am I missing something here?
Perhaps the "root of the template" there means the root element which matches ngController directive. In the example below, the first and second myCustomer directives belong to the same "root template", but the third one is different.
<div ng-controller="Controller">
<my-customer></my-customer>
<div>
<my-customer></my-customer>
</div>
</div>
<div ng-controller="Controller">
<my-customer></my-customer>
</div>

Difference between onLoad and ng-init in angular

I am learning angular. I don't understand what is difference between onLoad and ng-init for initialization of a variable. In which scope it creates this variable.
For example
<ng-include onLoad="selectedReq=reqSelected" src="'partials/abc.html'"></ng-include>
OR
<ng-include ng-init="selectedReq=reqSelected" src="partials/abc.html"></ng-include>
Please also give me some idea about isolated scope.
ng-init is a directive that can be placed inside div's, span's, whatever, whereas onload is an attribute specific to the ng-include directive that functions as an ng-init. To see what I mean try something like:
<span onload="a = 1">{{ a }}</span>
<span ng-init="b = 2">{{ b }}</span>
You'll see that only the second one shows up.
An isolated scope is a scope which does not prototypically inherit from its parent scope. In laymen's terms if you have a widget that doesn't need to read and write to the parent scope arbitrarily then you use an isolate scope on the widget so that the widget and widget container can freely use their scopes without overriding each other's properties.
From angular's documentation,
ng-init SHOULD NOT be used for any initialization. It should be used only for aliasing.
https://docs.angularjs.org/api/ng/directive/ngInit
onload should be used if any expression needs to be evaluated after a partial view is loaded (by ng-include).
https://docs.angularjs.org/api/ng/directive/ngInclude
The major difference between them is when used with ng-include.
<div ng-include="partialViewUrl" onload="myFunction()"></div>
In this case, myFunction is called everytime the partial view is loaded.
<div ng-include="partialViewUrl" ng-init="myFunction()"></div>
Whereas, in this case, myFunction is called only once when the parent view is loaded.
Works for me.
<div ng-show="$scope.showme === true">Hello World</div>
<div ng-repeat="a in $scope.bigdata" ng-init="$scope.showme = true">{{ a.title }}</div>

Resources