AngularJS - nested directives without transclude? - angularjs

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.

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

Creating reusable UI components

In jQuery, you can create reusable UI components. In AngularJS, I understand that using Directives is the equivalent way of doing this. The Directive syntax includes a Link property where you can manipulate the DOM. But this is where I get confused. AngularJS is meant to avoid manipulating the DOM because AngularJS manipulates the DOM internally, so why would you do this? Is there a better way of doing this without actually having to manipulate the DOM?
Where did you get the idea that all DOM manipulation is a bad thing? How would anything ever get rendered if the DOM wasn't being manipulated? It's a bad idea to mess with the DOM in controllers or outside of Angular's context but in directives you're plugging into Angular and can safely make DOM changes without breaking anything.
You're basically asking how to manipulate the DOM without manipulating the DOM which makes no sense.
Go ahead and do your stuff in the link-function, it's perfectly fine.
At some point you will have to engage in manipulating the DOM in some way or another, but for 95% of all cases you can stick to angular's built in directives, such as ng-if, ng-hide/show, ng-switch, ng-repeat etc...
But these are all directives, so angular isn't manipulating the DOM "internally", it does it through directives, just as it's meant to do...
And keep in mind that in your directive templates, you can use those other directives, so your directive can focus on it's own task.
Use the $new(isolate) method from the $rootScope to create reuseable UI components. Set isolate to true. Don't manipulate anything outside of your UI component:
If true, then the scope does not prototypically inherit from the
parent scope. The scope is isolated, as it can not see parent scope
properties. When creating widgets, it is useful for the widget to not
accidentally read parent state.
https://docs.angularjs.org/api/ng/type/$rootScope.Scope

Sharing isolate scope with nested directives in Angular

I want to have a custom directive that is reusable and creates an isolate scope so it can be used anywhere (as long as the consumer uses the API defined by the directive). Then, I want the consumer to easily be able to mix and match different reusable pieces that fit within the main reusable directive.
The situation I'm working with is a drop down menu. The main directive would isolate the scope and define the API for the dropdown as a whole. The inner directives would allow the consumer to choose whether they want a button that opens the menu, a search box/input field that opens the menu, etc. Then they could also choose what menu style is used:
<dropdown items="..." selected-item="...">
<dropdown-button>(Transcluded button text here)</dropdown-button>
<dropdown-icon-list></dropdown-icon-list>
</dropdown>
The parent directive/controller would handle state/communication for the inner pieces (ie. the button might trigger the "open" state, and the list would respond by opening). In other words, the parent directive would provide a single place for the consumer to define behavior and isolate scope from the rest of the page, while the nested directives would change shared state/respond to changes in shared state based on their role.
I actually had this working by using an isolate scope on the main "dropdown" directive and then inheriting scope with the nested directives (didn't specify "scope: ..." on the nested directives). But, with Angular 1.2, things have changed such that the isolate scope of the parent is truly isolated--the children inherit the scope that exists outside the parent directive, rather than sharing its isolated scope.
What is the Angular way to accomplish such a thing?
I've started retrofitting my existing code to share the controller from the parent directive with the nested children, but I feel that's the wrong way to go once I get into the situation where the children need to listen for changes on the shared scope... The only way I can see to do that would be to pass a callback function from the nested directives into the shared controller which it would bind to a $scope.$on method. Seems like the wrong path to head down.
There’re 3 types of prefixes AngularJS provides.
"#" ( Text binding / one-way binding )
"=" ( Direct model binding / two-way binding )
"&" ( Behaviour binding / Method binding )
All these prefixes receives data from the attributes of the directive element and provide communication between directives. please visit below link for similar question.
Visit https://stackoverflow.com/a/33024209/4348824

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

AngularJS isolated directive with ng-repeat breaks transclusion scope

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.)

Resources