A simple example to explain my case:
I have a directive for labels
<input label="{{obj.label}}"/>
But for some other directives I want to use an attribute with name "label"
<other-directive label="My label"></other-directive>
just as an attribute, not processing the label-directive.
I could just rename the attribute to e.g. "my-label":
<other-directive my-label="My label"></other-directive>
but it would be nice to use "label" as an attribute name.
As #ExpertSystem points out in the comments to the question, angular really has no way of knowing out of the box whether your directive should be applied in one case versus another. The only way I can think of to get around this is to include logic in your directive's compile function that knows how to determine whether it should be applied or not. This plunker demonstrates how I would accomplish this. You basically need to return two different link functions from the compilation phase; one if your directive should be applied (in this case adding a label before the input), and a different one if it should be skipped. You are then free to use that as an argument to a separate directive. This may not work if your directive needs to do things like transclusion or isolated scopes (things that angular doesn't like two directives on the same element to do).
I'd be very sparing with how you use this, however, as it will create an inconsistent API for other developers who may be working with this code. They may not know when the directive will apply and when it won't.
Related
I totally hate to add tag names based on component names in my apps. I hate to do this:
<hero-detail hero="ctrl.hero"></hero-detail>
Isn’t it possible to use normal div tags instead? Something like this?
<div id="hero-detail" data-hero="ctrl.hero"></div>
why isn’t that working?
You can technically restrict a component to an attribute as you do with directives since components are a special kind of directive. (I modified a random fiddle found on google)
EDIT: this does work on older 1.5 versions but not on newer ones. So read on...
But officially you cannot and you shouldn't do it anyway. If you choose a component architecture, you have to stick to the rules or things will get out of hands quickly.
Having components as html elements is clean and good practice. You should learn to like it.
why not, that is also possible.
basically in three ways we can specify the directives.
i.e Element, Attribute, Classname
but while create the directive you can specify
restriction : AE
or whaterver you need,
then you can use like this
<div id="hero-detail" hero-detail data-hero="ctrl.hero"></div>
for more have a deep look at the docs. directives
your answer is here https://docs.angularjs.org/guide/directive
They says:
Directive types $compile can match directives based on element names,
attributes, class names, as well as comments.
All of the Angular-provided directives match attribute name, tag name,
comments, or class name. The following demonstrates the various ways a
directive (myDir in this case) can be referenced from within a
template:
<my-dir></my-dir>
<span my-dir="exp"></span>
<!-- directive: my-dir exp -->
<span class="my-dir: exp;"></span>
Best Practice: Prefer using directives via tag name and attributes
over comment and class names. Doing so generally makes it easier to
determine what directives a given element matches.
Best Practice: Comment directives were commonly used in places where
the DOM API limits the ability to create directives that spanned
multiple elements (e.g. inside elements). AngularJS 1.2
introduces ng-repeat-start and ng-repeat-end as a better solution to
this problem. Developers are encouraged to use this over custom
comment directives when possible.
I have a directive which creates a rich text editor in its LinkingFunction. The small directive I'm using for my rich text editor can be found at https://github.com/angular-ui/ui-tinymce/blob/master/src/tinymce.js.
I need to extend this directive with another directive which will allow me to configure the default options and access the element created by the previous directive.
If possible, I would like to do this without forking the original ui-tinymce directive (linked to above). In this directive there are two properties:
uiTinymceConfig which I need to be able to access and configure before this directive's LinkingFunction is run (before the options are passed to TinyMCE)
tinyInstance which I need to manipulate after it has been created by this directive
I've done plenty of research into extending directives, as well as the different attributes available to the "Directive Definition Object", such as link, pre-link, post-link, compile, and controller. I have experimented with sharing properties between two directives using some of these methods, but I have not come up with a solution that fits my needs (above).
I am happy to fork this original directive code if it is not possible to achieve what is needed without doing so.
So I investigated this a little for you, and came up with this Plnkr.
This will let you override the value provided for injection - note that you can do this in a module that depends on the submodule, so you can provide different configurations for different modules that depend on the submodule, which would be of use for the ui-tinymce directive.
Using a similar principle, you should be able to edit the config value for uiTinymceConfig by just simply overriding it. You can even do this and override it right in the base module if you'd like.
If you want to edit the instance itself after instantiation, you can simply access it by using the ID attribute and calling tinymce.get('#IDattribute') directly anywhere in your code.
Egghead.io has a great explanation of directive restrictions in AngularJS. A custom directive can be defined as follows:
angular.module("app", []).directive("blah", function () {
return {
restrict: "A",
template: "blah directive. nothing to see here."
};
});
This creates a what I will call an attribute directive (due to restrict: "A") for the purposes of asking this question. This is actually Angular's default restriction on a custom directive, and this directive can be used like:
<div blah>
<!-- content of directive -->
</div>
When I want to create a custom directive, however, I normally go for an element directive, like:
<blah>
<!-- content of directive -->
</blah>
How is the former attribute directive any better than the latter element directive and why was it chosen as the default?
This is my opinion, only:
There are three possible ways to define a directive in the HTML - attributes, elements and classes. Classes are too lax and confusing, and a best practice would be to separate logic from style. Element directives are too strict - you can only have one "element" directive in a DOM element. That makes them special right from the start.
An attribute seems to be the best middle ground between these two extremes - it is clear, allows for multiple directives in an element (If you are following Egghead's videos, the superhero example on "directive to directive communication" shows a kind of "directive hierarchy", elements superseding attributes. Also, and this is very important most of the times (I program for intranet apps, so to me it isn't) attributes allow angularJS templates to be valid HTML.
EDIT - my two cents would be that it doesn't matter - in any real scenario, it is a bad idea to trust the "default" configuration of something as primary as the restrict option - setting it explicitly makes for clear, no doubt directives (especially working in team projects, but also anytime, really)
Angular 1.3 changed the default to be 'AE'. See: https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object.
I'm guessing it was changed because it's "restrict", not "include". So, the default for "restrict" should restrict very little.
What does that mean going forward -- if you see a "restrict: 'E'" line, it actually means "This wasn't meant to be used as an attribute", rather than the potential current meaning of "I simply want to use this as an element".
My take on this would be:
An element-based directive would most often represent structural functionality.
For instance, I use element-based directives for popups, dialogs, tabbed widgets, and reusable widgets in general. I then can add attribute-based directives to them (say, adding an ng-click to a <ui-button> directive), but the element name (i.e. the directive name) represents the structural semantics of what is being built.
I'm very(!) new to Angular.js and am trying to understand where to put the various parts of my logic in order to follow best practices and separate business and presentation logic.
My specific use case is that I have a list of courses with a number of signups and a number of available seats. Based on these values I want to present a progress bar (or, if the available seats is not set, just a text).
My question is where to put the various parts of the logic, and how to pass the values along properly. So far I've created the HTML-part of a directive, like so:
<signupprogress available="{{course.available_seats}}" filled="{{course.filled_seats}}"></signupprogress>
My question is then (first and foremost) if a directive is the proper way to do this and, of so, if the logic for constructing the progress bar should go in the compile function, link function, in a template, or some other place. To me the compile of link function seems to be most correct, but I don't want to fill them with HTML, nor am I able to properly get the attribute values from the HTML (the attrs.$observe examples I've seen only implement the getting of one attribute).
Yes, use a directive since you need to modify the DOM.
If all of the HTML for the progress bar and the alternative text can be placed in the directive's template, then do that. And, if that is possible, use '#' for one-way binding, which makes it clear that the directive does not need to modify the "available" and "filled" values. If you find you need a linking function, then as #ShaiRez mentioned, '=' is probably easier. (BTW, here is a way to $watch multiple attributes, instead of using $observe. Maybe the same technique can be used for $observe.)
To display either a progress bar or the alternative text in the template, use ng-hide or ng-show in the template. Here's a simple example of that (which also uses '#').
The directive is the way to go in my opinion.
I would have my HTML content inside of a template, the logic inside of the link function (the compile function is usually more for repeaters etc).
And use the "scope" property in the directive definition, set to "=", that way changes are reflected automatically.
I have a control that should display breadcrumb navigation. It needs data (route & title) to display the navigation correctly. Data is taken from scope and used inside a directive.
What causes my problems is that I use a localization directive in the control that should translate the title. And this localization directive is called even when expression in ng-show is evaluated to false. Then the translation in localization directive ends with exception because it tries to translate incorrect string (see 'localize' directive in http://jsfiddle.net/F97wn/7/).
That seems quite weird. I would expect that if something sets whether the inner content should be visible or hidden, then it is evaluated first and then the inner content..
Ok, then I found that ng-show only sets some css attribute, so it's quite useless for me.
The question is: How should I solve the problem - what to use instead of the ng-show?
An example is at http://jsfiddle.net/F97wn/7/
You could use ngSwitch instead with the on part set to "toshow()" and the inner ng-switch-when="true" part to have your custom directive inside that area. This will then not execute the custom directive if the value of toshow is not true.
If the directive is throwing an exception, more information should probably be passed to the directive, in one of the following ways, so that the directive can decide if it has the required information to do what it needs to do:
attribute data -- e.g., localize="..." show-me="..."
something defined on the scope associated with ctrl -- e.g., $scope.showMe. The directive scope will have access to this property as scope.showMe, based on the way you currently have the directive defined.
or inject a service (that has the data) into the directive -- e.g., directive('localize', function(myShowMeService) { ... }
You might also want to look into <ng-if>. The ngIf directive removes and recreates a portion of the DOM tree (HTML) conditionally based on "falsy" and "truthy" values evaluated within an expression. It might be more intuitive than <ng-show> for your needs.
However, it is currently only available in the unstable version of AngularJS. If you can use that version of AngularJS you can find more information about it here.