-
Notifications
You must be signed in to change notification settings - Fork 27.5k
Feature Request: Programmatically add directives to another directive's elements before they get compiled. #6950
Comments
You can define multiple directives for a given element name/attribute/class/comment and all of them will be invoked. On top of this, a directive can be intercepted and modified. These two mechanisms should solve a big chunk of these issues. For more complex cases this is harder to fully handle them in a generic way, #6781 is an attempt to doing so but this may or may not be part of 1.3. (BTW, 2.0 will have something like this but not 100% compatible) |
Hi @trusktr
If module A defines a directive on
You can modify a directive by defining a decorator on the original directive. The API is defined here http://docs.angularjs.org/api/auto/object/$provide #6781 and the design changes for 2.0 are something else, and they aim at other things that cannot be done using the previous two mechanism |
I'm not quite sure what you mean. Can you link to the docs for those two points? Or can you provide two quick examples to see if we mean the same thing? EDIT: after reading the design doc, I think I see what you mean about "defining multiple directives for a given element name/attribute/class/comment". I think you mean something along the lines of how the directive annotations like @TemplateDirective use selectors to define which elements they can apply to. Is that what you mean? If so, that is something independent of what I'm proposing. I'm also still not clear on what directive collectors and extractors are, but they don't seem like part of the public API. I also looked at PR #6781 and I don't know what those are yet. I've been learning Angular for the first time based on the public API documentation. What I'm proposing here is simply an easy to use public API, which I haven't seen in the docs, and which can perhaps be done with private, undocumented API that already exists. The API I'm proposing here is really simple to use, and won't change current usage of Angular, only add to it. :D I looked through the AngularJS 2.0 design document, and I didn't see anything that specifically addressed this need. The only thing that came close was this:
This implies that 3rd party events already exist. But 3rd party events are not always guaranteed. Being to attach directives like I've proposed would be extremely useful for events that don't exist, as well as for other use cases that can take advantage of the improvements described in the design doc (e.g. the new decorative, template, and component directive design). |
@lgalfaso Hi!
So you mean if I have <div data-awesome-directive="awesome.data"></div> that I can do the following? <div data-awesome-directive="awesome.data" data-my-own-directive="my.data"></div> If so, I knew that already, but does it let me add directives to elements of the 3rd party directive? Suppose that the 3rd party <div data-awesome-directive="awesome.data"></div> generates <div data-awesome-directive="awesome.data">
<table>
<tr>
<td>one</td>
<td>two</td>
<td>three</td>
</tr>
</table>
</div> And suppose that I want to do something whenever a user clicks on a Are you saying that if I attach my own directive to the parent element where I've attached the 3rd party directive <div data-awesome-directive="awesome.data" data-my-own-directive="my.data"></div> that my custom directive can have access to the elements generated by the 3rd party directive in order to apply What I'm aiming for is the effect of having <div data-awesome-directive="awesome.data">
<table>
<tr>
<td ng-click="function()">one</td>
<td ng-click="function()">two</td>
<td ng-click="function()">three</td>
</tr>
</table>
</div> but I obviously don't want to edit the 3rd party library to add those Does such a mechanism exist so that I can make the 3rd party's generated elements have the Thanks! |
Hi, I mean that if you have <div data-awesome-directive="awesome.data"></div> you can add another directive named For the example you just post, you can also create a directive on the |
@lgalfaso Thank you for linking to this document. I was looking for a more in depth guide on Directives in the Angular documentation but I couldn't find it because I was looking for something with the keyword "directive" in the title, not "compile". I read through the whole thing, and I see what the
Can you explain how I would put a The API that I've proposed above would be simple to use and straight forward. It would be the next easiest way to add directives to elements (of a 3rd party directive for example), the easiest way being to actually write the |
@trusktr I do not want to put any false hope here, but the modifications you are proposing have many side-effects. On top, I think that all this can be implemented using an external library without any change to the core.
You can add a controller with a decorator or adding another directive
You can add a directive to the child element, this new directive will check if it can find a parent controller and if that is the case do the needed, if not, do nothing This is turning into support and not a real issue with Angular, so I am going to close this |
@lgalfaso Thanks for the useful hint on I went and did extensive research on
Those limitations are good reason for anyone (I in this case) to suggest (potentially new) API changes in order to make AngularJS more powerful. That being said, my suggestion doesn't need to be specifically accepted, but some API change should be made in lieu of the fact that what I want to do is limited within the scope of AngularJS.
Doesn't everything one does in programming have side-effects? Side-effects are not always bad. Can you elaborate on the bad side-effects that my proposed API would create? The API I've proposed can have the following few rules associated with it (as does every API in Angular). I will refer to directives being attached to elements of another directive as "attached-directives" and the directive who's elements are having attached-directives attached the "other-directive".
Cases where The need exists, and an API that does like what I'm proposing would be powerful.
To conclude, one thing is clear about AngularJS: the use of already-made directives in HTML makes it so simple to do powerful things (e.g. like using Do you (anyone reading this) have any other suggestions on how to simply apply directives onto HTML elements when access to 3rd party source code containing the HTML is not available? Also, do you have any suggestions on further improving and/or modifying and/or why not implementing my proposed API? Thanks! |
Hi, Let me comment inline
A lot can be abstracted into a third party library, and I think that would be a good place for it. Not in the core.
Documentation can be improved, but for sure that is not a reason to increase the API
This works one way or the other. In both cases you need to know something about the third party directive, if this is a few or a lot, thats another issue
Not true, you can decorate anything the
Adding a way to reference DOM elements without putting super strict rules is a bad idea. Angular is trying to solve this by forcing you to have to be really explicitly on what you want to modify. When you say that you want to make a reference an element, then there must be a way to reference this element. Once that door is open, then there is no way to close it and sooner rather than later Now that it looks like you understand the many issues involved and that you read quite a few on the problems, please re-read #6950 (comment) |
@lgalfaso I've been thinking about this again, being on another Angular project. I think this would be very powerful, especially when using 3rd party libraries who's code bases you don't want to touch. When you attach a directive onto child elements, it'd behave exactly as if those directives existed on the element before that element was compiled, as if the directive was in the markup of that library. For example, if you know you're using some library that provides // add a directive to children that match a certain directive using the D: prefix
app.attachDirective('D:awesomeDirective', 'myDirective');
// add a directive to children that match a certain selector using the S: prefix
app.attachDirective('S:#header.dropdown', 'myDirective'); This would come in so much handy. I know that as owners of our code bases we can avoid this, but when using third party libraries we don't decide on the architecture for those libraries, so a feature like this would be powerful. |
@trusktr I think that most of what you are asking for, can be done with a third party library without any modification to angular core. The only reason I bring this up is that I doubt that something like this would ever be part of the core. Once you think that an external module works the way the author intended it to work, then allowing into the core a way to do general monkey patching is a big no |
@lgalfaso If this feature can be added through a 3rd-party addon that's be nice. If I get some time for it I'll try and see what I can do. This feature isn't necessarily for monkey patching. Suppose you install some awesome 3rd-party calendar module. The calendar might generate a grid of Maybe I just don't have enough experience with Angular yet. What other way would you imagine doing something like this (i.e. directions to take on making a 3rd party plugin to achieve this functionality)? |
I'll be interested in knowing the proper path to take for such implementation. |
@mustaphakd Angular 2.x will be out soon. Instead of directives, there's components. Each component gets attached to a DOM element based on it's CSS selector. It might then be easy to attach components to the inner workings of a 3rd party component simply by making a new component instance in JavaScript who's selector simply matches that of something inside the 3rd party component. I got to see @davideast's presentation on Angular 2.0. David, do you think what I just described will be possible, attaching components to things inside a 3rd party component via selectors? This is basically what this whole issue was about, but seemingly difficult to implement compared to the Angular 2.0 components if selectors work as I'm imagining here. If this will be possible in 2.0, I imagine that there could be 3rd party components that cause problems with each other if they use generic or similar selectors. Is this true? For example, suppose I have |
Or will shadow dom (or something) prevent this sort of thing, and in that case, will there not be a way to apply a component to elements of another (3rd party) component (who's code we wish not to modify)? A practical use case would be adding a special functionality to all images in a page, even if they are in a 3rd party component, without having to modify the 3rd party component. |
I really don't get why there is this long discussion around this. As @lgalfaso already mentioned, it is very possible and easy to implement it with Angular 1.x (e.g. implementing another directive with the same name, higher priority and manipulate its content in a postLink function). (Bonus-points for naming your helper/module "attachDirective" and being super-happy 😛) |
@gkalpak can U imagine efficient memory utilization? angularJs already does an excellent job clearing views out of memory when we tell it to do so, but currently all controller are preloaded into memory when the script is run and once initialized, their instance stays. currently, we have to plumb some infrastructural codes to properly remove instances from memory or reload them on demand. |
@mustaphakd: This sounds like a totally different issue. If you have found a memory leak in Angular core, you should definitely submit a new issue (providing all the necessary details and (if possible) a live reproduction (e.g. using JSFiddle, Plnkr etc)), so the Angular team can look into it. |
@gkalpak U misunderstood me. I have not found any memory leak in Angular core. although the title is specifically about directive, I was also mirroring the same concept to views being loaded on demand with their controller cleared from memory once the view is disposed off. may be U right both ideas may not even be that parallel at the moment. |
@mustaphakd: Sorry, I lost you. Is it a question ? If so, you should direct it to the appropriate channels to get help ;) |
@gkalpak: no, it was not a question. it is just another current issue we find ourselves confronted with: loading and appending new directive to element being tied to an existing directive; loading and appending new controller to an existing element (not so much of an issue since there are easy work around thanks to the api) but clearing the component from memory once done using it demands great care unless we instantiate the component on a property that is globally accessible. angularJs seems to do a great job clearing views from memory but not their controllers. So a request here would, loading and removing components from memory (including their js scripting logic). |
@mustaphakd: It's still not clear to me what "removing components from memory" means. |
@gkalpak : well, can we think of directive and controller as such? |
Components/directives are "loaded into memory" as soon as the page loads the script file. An instance of a directive is typically tied to its DOM element and/or Scope. When the element is destroyed, Angular tries to remove all references to the Scope. When all references to the former element and its scope have been cleaned up, the garbage-collector can remove it from memory. Apart from that, there's not a whole lot of memory management that goes on, given that JavaScript apps have no control over the GC. Moving back to the original issue: Interrupting the directive compilation cycle can be computationally-expensive, and have unpredictable side-effects. If you absolutely need to do it, you can add a new attribute to your element, and re-$compile it. |
@schmod: There is no need to "interrupt the directive compilation cycle"... |
Hey all, back to @trusktr's proposal, I think this would be extremely useful and quite easy to implement. I think the only constraint would be that a programmatically-added directive would need a lower priority than the directive that added it. For anyone else who stumbles across this issue and just needs the functionality today, there's a clever post on SO that will get you up and running right now. |
+1 Definitely would be really helpful if adding directives programatically is supported. |
+1. In my case I have a dashboard that can have say about a dozen "component" directives on it at any given time. They are in a gridster like grid where the components can be resized, moved around, added from a list and removed. However the list of components that can be added is in the hundreds. Don't wanna have to set up a ng-switch for that :) |
In my case. I have dashboard and list of component (directives), User can select the components from list of component for Data Visualization. I need a way to dynamically add and remove the directives. It will be a great help. |
I ran into an issue. So far as I know, Angular doesn't have a mechanism by which to address it easily.
http://ngmodules.org has some awesome modules. They are really easy to use, but if they don't expose any events to you, then you're out of luck if you don't want to actually modify the 3rd party module's source code.
What if Angular had an easy way to attach directives to the element of another directive
programmatically
(before it and any sub-templates get compiled) so that one could use third party directives and not worry if they've exposed any useful features like like event handling mechanism (e.g. they don't use ngClick on any elements)?For example, here's an example:
Suppose we use someone's
awesomeDirective
.This nice directive generates some type of awesome HTML widget with HTML that is defined in the 3rd party source code, perhaps a twitter feed, a profile badge, or something else awesome, but the author of the directive did not include any way to handle click events, etc!
We could step away from Angular, and use jQuery to make a click handler like
but we don't want to leave the world of Angular awesomeness!
Perhaps, a new addition to the
Module.directive
API can be added to Angular so one can add a directive to any other directive's elements, like so:This would be a nice way to extend directives that you are using in your app... But what if you want to attach directives to only specific instances of another directive? Then:
The first argument in this case is an array. The first element in the array tells Angular that we wish to attach our directive to elements matching the
awesomeDirective
only if those elements are.rootElements
. All.rootElements
that match theawesomeDirective
will then have thengClick
directive applied to sub elements of the classelements
using jQuery's.find('.elements')
.Angular could benefit from something like this! And it would not only be useful for event handling that 3rd party modules haven't exposed, but for augmenting 3rd party directives with all sorts of other functionality.
The text was updated successfully, but these errors were encountered: