AngularJS isolated directive with ng-repeat breaks transclusion scope - angularjs

I'd really appreciate some understanding of this scoping issue I encountered while developing a transcluded isolated directive, one where the transclusion is repeated inside the template.
I promise that I do already understand how both isolated and transclusion scopes work, and I've searched the web for an answer already. :)
What I'm finding here is that, when not using a repeat, my transcluded html can access to the parent scope (as I would expected, as the transcluded scope is a sibling of the parent scope).
However the moment I change my template to repeat the transclusion, the parent scope is no longer accessible to the transcluded html.
I'm sure there's a good reason for this, but for the life of me I can't put my finger on it.
Please see an example plunker here - it will explain what I'm referring to better than words can.

What happens with ng-repeat is that the transcluded scope (00A below) becomes a child of the ng-repeat scope (009), rather than a child of the controller scope (003).
(Right-click on the image and open it in a new tab to see it better.)
Since there is no dashed line from isolate scope 006 to controller scope 003, transcluded scope 00A can't find outer using prototypal inheritance chaining/lookup.
(Scopes 004 and 005 are created by the working directive. Transcluded scope 005 can find outer using prototypal inheritance.)

Related

angular directive automatically re-instantiated when scope change

Many of my directive (soon to become components) takes their scope from variables set by other directives. Currently in each directive I have to watch my scope to know if it has changed which seems to complicate the code unnecessarily. So I started using ng-if="vm.ready" on my tag to reinstantiate the directive when I need it to. But then the management of that state is left outside of the directive which is harder to maintain.
I am wondering if angular provide such a mechanism when if the scope of your directive change then it will at least reinstantiate your directive controller.
Thanks
You can probably use $onInit()
After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller properties. You can access these bindings once they have been initialized by providing a controller method called $onInit, which is called after all the controllers on an element have been constructed and had their bindings initialized.
https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L250

Why are AngularJS isolate scopes not attached to the directive root element (when using replace)?

When using the replace flag on a directive, normal scopes get attached to the root element of the template and the element gets appended with a "ng-scope" class.
When a directive uses an isolate scope the root element of the template still gets appended with a scope class: "ng-isolate-scope", but the isolate scope itself is attached NOT attached to the node. Why this behavior?
It is of interest to me because it also appears to mess with the expected behavior of a template that uses ng-if in the root element of a template. It causes the template transclude from the external scope instead of transcluding from the local isolate scope of the directive.
here is an example of a simple directive with an isolate scope and replace set to true. If you note the console log you will see that the root element of the directive is not bound to the directive's scope, only the child element is.

ui-bootstrap modal scope bug

I am noticing some weirdness with the ui-bootstrap modal scope. It seems that when using ng-model in it, you have to reference $parent to get to the scope of the modal controller. Notice in my plunker that the other properties such an ng-options doesn't require $parent:
http://plnkr.co/edit/xGSHz4EkZvGr2D6CUeBz?p=preview
Any idea why? I found a similar issue here:
Scope issues with Angular UI modal
That led me to try the $parent change but I am unable to comment on that thread because I don't have enough reputation.
Any idea why the scope seems to change?
Thanks!
The modal has its own scope (I've never used Angular UI, but it's the only thing that can be happening) and when you're setting "selectedLocation" the property is getting set on the modal's scope and not your controller's scope. The $parent is forcing it to got your controller's scope, but that's not a good solution because you'll be locking your self into a certain structure always assuming the parent of the modal has the "model".
Here's a modified Plunker using a model object on your controller scope (using model.selectedLocation)
http://plnkr.co/edit/B5kZaIA5xi2RediUTBK7?p=preview
Anyways, if you put your property on something like "$scope.model.selectedLocation" that changes the behavior. Now, when I reference "model.selectedLocation" on the modal, the modal's scope doesn't have a model object so Angular goes up the scope chain to your controller's scope (which does have a model object).
Watch this video from John Lindquist, I think it can explain it much better than I can. :-)
http://egghead.io/lessons/angularjs-the-dot

Accessing parent scope in isolated directive

I'm building click to edit directives, but have issues understanding how to access parent scope in an isolated directive.
Example: http://jsfiddle.net/ADukg/3591/
scope: {},
It works if I "unisolate" the scope removing scope: {}; but need the isolated scope.
UPDATE:
Done it adding
controller: 'FormCtrl',
To the directive. See http://jsfiddle.net/ADukg/3601/
You could use the $parent property on the isolate scope to get direct access to the parent scope, but normally you'll want to use attributes to specify which parent scope properties the directive needs to do its work.
If you need to change the parent scope properties in the directive, bind with = (two-way objects). If you only need the string values of the parent scope properties in the directive, bind with # (one-way strings).
The given solution won't work if you pass an attribute with primitive type like 'string', 'long' .... etc
The two-way binging works only with object.
Every scope object contains a special property called $parent which refers to its parent scope. The isolated scope also has a $parent property. But it refers to the enclosing controller/directive's scope.
To make it work with primitive attributes: you could bind your directive template to a controller. This will expose your directive to its parent and you can access by $parent.
Second solution is to not make an isolate scope (but i don't think it's your goal).

AngularJS - nested directives without transclude?

Accompanying plunker.
I have an attribute-level custom directive in a div. The directive has an isolated scope. Inside my div I have other directives that expect to be in the scope of the parent.
The issue is that the directives inside my div have access only to the isolated scope, not to the parent scope. I understand why, but I'm not clear on how to solve it cleanly.
I know that I can use transclude to solve this (see plunker) but this feels very sloppy. I have no need for a template, but I'd have to create one just for transclude to work, and transclude seems to be the only way to ensure that my nested directives have access to the correct scope.
Is there an alternative, cleaner way to do this?
To head off some possible questions:
I'm using an attribute-level directive instead of an element-level to make things easier for IE
I'm using an isolated scope because it's a best practice - I don't want to hose my parent scope by accident, and I want the directive to be portable.
I'm really not sure what you're trying to do.
But what you're actually doing is leveraging bidirectional binds on an isolated scope for ill-effect. It almost seems unrelated to your question.
Anyhow... here is an update to your plunker
Basically what was happening is inside of your isolated directive you need to use whatever name you've assigned in your scope declaration, in this case toggleOn().
however if you want to you can do this. Basically just call $parent.colorToggle().
You can decide if this is "less sloppy" than transcluding:
<button ng-click="$parent.colorToggle()">Inside</button>
Isolate scopes have access to the parent scope via a $parent property. Although an isolate scope does not prototypically inherit from its parent scope, Angular still maintains a hierarchy via $parent and $$childHead and $$childTail.

Resources