Angular directives - element or attribute? - angularjs

I'm part of a team with about 6 UI devs, of varying quality and next to no Angular experience. Many are contractors, with little experience with the code base. The app has a very fancy (complicated) UI. It supports IE8+ (soon hopefully IE9+).
We're introducing Angular for a major extension to the app, and I've been asked to write guidelines on the use of Angular for the team.
We'll use directives to create fancy UI elements, all prefixed with "ipwr" to avoid name clashes. I'm trying to decide whether to mandate that devs give their directives the restriction "element" or "attribute". Mandating only one, to avoid chaos and confusion.
My question is: what restrict is better or more popular for directives, "element" or "attribute"?
My main concern is ease of use for people with little Angular experience who are new to the application code base, to reduce bugs, copy and paste behaviour, etc.

The angular guidance says that you should use the "element" restriction whenever the directive has full control over it's template meaning it has a template that it is rendering out, etc.
For attributes, they suggest to use these only when you are adding "behavior" to an existing element or decorating an existing element.
For example, think of the ng-click directive, this is used a attribute not as a element because the click directive is just adding the click behavior to some element.
Another example would be the ng-repeat directive, it is also used as an attribute not as a element because it is going to repeat the element in which it is being used in.
Now, this guidance is from the angular documentation; however, I don't know necessarily that element vs. attribute is going to give you a "better" approach it's more of a convention.
Now if you have to support older browsers, then you may want to consider using either the comment or class directives.
My personal preference is to just use the attribute restriction; mainly because people that are new to angular get overwhelmed at first when they see the restrict and it's variations of the options that can be used.

I usually defer to the John Papa AngularJS style guide when making these types of decisions. He says:
Lean towards implementing as an element when its standalone and as an
attribute when it enhances its existing DOM element.

If you want to keep your HTML valid you'd use attributes, e.g. if you have a directive ipwr-modal, you can declare it as <div data-ipwr-modal="you-could-put-some-binding-here"></div>.
When creating directives with a custom layout, however, you'd better use element declaration (if you don't need to have your HTML valid). This is the more obvious way to say: "hey, we have a custom component here".
This blog post explains it with some more ideas

Some points to consider:
Most of the time attributes is the best/most convenient option (it's not the default by chance).
Anything you can do with element-bound directives, you can do with attribute-bound as well.
Element-bound directives can be more descriptive/readable at times.
If you want your code to pass certain types of validation, you should use attributes.
Since you want to support IE8, keep in mind that custom tags have an extra overhead (more info), which hurts maintainability.
BTW, you can't limit directives to elements only (without affecting functionality), so it is more a question of allowing 'element' or not. Note that an element often has more than one directives and directives placed on the same element can cooperate and augment each other's behaviour. So limiting the use of directives to 'element', means limiting the number of custom directives per element to 1, which severly reduces the functionality-potential.
That said, this is what I ('d) do:
If IE8 is not an issue, allow both (mostly use attributes).
If IE8 (or the overhead for custom tags) is an issue, use only attributes.
In general, if only one form should be allowed, it should be attributes (works anywhere, no extra overhead, offers all functionality).

Related

Custom class or attribute in E2E testing

Having pretty complex angular application with many pages (states) and conditional sections that creates a lot of test scenarios I need to perform e2e tests. I'm tired of nested selectors like 'div.SomeComponent > ul:nth-child(2) > ... ' and so on even using BEM namings (especially when app is evolving and it's easy to spoil tests by little change of html structure).
The question is, would you guys opt for creating some dummy (empty) classes or data-* attrs just to simplify protractor (or groovy) selector at the expense of loosing semantics? What's the alternative?
To avoid changing your element definitions every time developers change the css and to avoid using long css strings to select your elements, you can try referring to them by other means (id, className, model, etc.). See https://github.com/angular/protractor/blob/master/docs/locators.md for examples.
My personal favorite is to use element(by.css('[ng-click="executeSomeAction()"]')) as this will most likely not change with any application updates. It also works for other directives as well.
As for testing applications with a high volume of pages and elements, it's nice to define your elements in a class(es) and then call them in the test spec as needed. This reduces the code in your specs and makes them easier to read. You may also want to create a separate file for actions/functions your tests perform.
Hope this helps answer to your questions.

Putting presentation data in angular controller?

Got a webapp I'm building in Angular.
This app walks a user to authorizing accounts, presenting specific instructions based on the users choices.
I've implemented this as HTML that is shown or hidden based on values in the model, so for 3 different choices, I have 3 different sets of HTML sections that are similar but with different texts.
In the spirit of DRY, I should instead have one set of HTML sections, and instead switch the text based on the values of the model. This means putting text data inside the model, including small snippets of markup, like anchor and strong tags.
Does putting presentation data into the controller violate the principals of Angular?
There are quite a number of options to avoid repeating code depending on what you are looking to do. The following ideas are things I would consider and use when they make sense (I placed these from simple to complex, so you probably can skip the first few):
ng-bind -- Put it on a span/div. Simple & works to bind the model to the display
ng-switch, ng-if, ng-hide, ng-show -- Work to conditionally show an element
custom directive -- use this when you want to alter the behavior of an element or if you want to alter the dom based on a template. If you use "ng-transclude" the contents of the element you template will be included in the result. This can be very elegant but it works best when you have a single format. I can provide examples but angular's documentation also has excellent examples.
service -- I generally use this just to provide data only. This could be via a restful api and $resource or via $http calls. Either way, I wouldn't recommend doing much more than load/save data here.
$scope method -- In other words:
$scope.myMethod = function(x,y,z) { /* code making decisions based on the model */ }
Then you can call this method from one of the previous either via a prebuilt directive (ng-show, etc) or via a custom directive that manipulates the dom for how you expect it to be.
ng-bind-html -- Last option I know to suggest is to use this directive combined with the $sce service to bind whatever you want to the DOM. If you are binding something with angular code in it - make sure to use the $compile service as well. I generally don't favor this approach except as a last resort because it makes it harder to find where elements in the DOM are coming from and it can make debugging + testing a real pain. That said, these tools wouldn't exist if people didn't need them.
I'm sure that this isn't complete and maybe others have suggestions but that is where I would start. Best of luck!
I would put the text data in a separate angular service. This article gives an example: http://joelhooks.com/blog/2013/04/24/modeling-data-and-state-in-your-angularjs-application/
Then if you decided at some point to move it to some other storage, your service would still be the single access point for the rest of the app.

Angularjs: Find all instances of a directive

I'm trying to create a directive to allow the user to navigate the page with arrow keys by section. But I also want to be able to have those sections be scattered around the dom, and to have this not break when stuff gets added and removed. I can think of several ways to do this, but none of them are satisfactory:
Create a directive with a controller that lets other directives register themselves (and unregister on $destroy). But this will be out of order if I add something in the middle later. Also, I've tried writing it this way, and it seems like way more code than necessary.
Whenever the user hits an arrow key, make an empty array, and $broadcast an event, with a callback for directives to register themselves on that list. Then, once that list is full, advance or go backwards on it. They (should?) come back in the order they're in on the DOM, but I'm not sure since this way seems crazy and hackish.
Mark things that are 'tabbable' with css, and write this the simple way in jquery, something like this: On a new click event, var all = $('.tabbable'), and then do the obvious with that. But I really don't want to do it that way, because it's not 'the angular' way. Not out of some sense of purity, but because I'm building this as part of a larger library of widgets, and I want this functionality to be accessibly to them.
So, is there any way for me to get the scopes of all directives of a certain type, without resorting to weird hacks, or spreading the logic out all over the place?
This is a good question. +1
First, finding all directives or nodes by type goes against the Angular way. The View is the official record in AngularJS, so directives should say what they do and do what they say. Coding some process somewhere to scan for DOM nodes and act accordingly is problematic for several reasons, not the least of which are separation of concerns and testability.
I'm glad to see you're looking at other options, but I agree that the other options you provided are sub-optimal for the very reasons you mentioned. But I have one more. This is one that I've used for a different application, but that required knowledge of scattered DOM nodes.
First, we create a service to manage the state of this component. It's simple. Let's call it SectionsService. Next, we create a directive to register sections. Let's call that section for simplicity. The section directive registers the DOM node's ID (maybe created programmatically to ensure uniqueness) with the SectionsService during its linking phase. Since the DOM is processed (mostly) in order, the nodes added to the SectionsService will also be in order. So the DOM looks something like this (irrelevant stuff omitted):
<div section>...</div>
<!-- other stuff -->
<div section>...</div>
<!-- other stuff -->
<!-- etc. -->
(Though out of scope here, it would not be very difficult to program it in such a way that the order wouldn't matter, but it'd be based on specifics of your app that I don't know.)
Next, you create your triggers, like an arrow key handler. On these events, you simply tell the SectionService to go to the previous/next node in the list. AngularJS comes with a service called $anchorScroll that can be used to emulate the browser's hash-based positioning we're familiar with. You could obviously also use a jQuery plugin to animate the scrolling if you wanted to.
And that's it! A very simply directive, a fairly simple service, and whatever trigger(s) you need. All told, I'd guess less than 100 lines of code including tests. All components are decoupled and easily testable, but still really quite simple. The view remains The Truth. The Angular Way is preserved.
And there was much rejoicing.
I hope this sets you on the right direction, but of course feel free to ask a follow-up question. We can also talk code specifics too if you'd like; as I said, they wouldn't be very complicated.
AngularJS services are singletons and can be required via dependency injection. You could have your directives require a state manager service and call incrementers/decrementers.
Alternatively, a little easier but more brittle, you could keep an array in $rootScope. It's more idiomatic "angular" (but not by much) than a jquery selector global, but probably not the best route if you're building a widget library.

Mail validation angularjs

I have an issue with angularjs and the email input type.
I want to create dynamic inputs with a directive, but the input type validation might be buggy.
Here is the jsfiddle of my test
http://jsfiddle.net/NPCHr
To avoid some trouble I have to use this trick
element.find('input')[0].type = input.type;
When I had a second character in the input the model field disappears (In the html panel)
I don't know why is this a bug or am I doing something wrong ?
The problem with your directive is not that e-mail validation doesn't work but the fact that dynamic type attribute is not supported by AngularJS (and jQuery BTW). This is due to the fact that IE doesn't allow changing input's type on-the-fly (Changing the <input> type in IE with JavaScript).
This topic was discussed in great details on the AngularJS mailing list, here is the reference: https://groups.google.com/forum/#!topic/angular/Itl-fYzeF18 where someone had exactly the same problem as yours.
The way out of this situation is to manually compile a template using the $compile service. Unfortunately this is not trivial, you can see evidence of some experiments here: https://github.com/angular-ui/angular-ui/pull/191
An alternative, simpler approach is to use ngInclude directive to include different inputs based on their type. Yet another possibility is to use the compile function and manually transform template's markup. But yes, all of those techniques require several lines of non-trivial code.

What is the difference between facelets's ui:include and custom tag?

Ui:include and xhtml based tag (the one with source elt) seem to be much the same for me. Both allow to reuse piece of markup. But I believe there should be some reason for having each. Could somebody please briefly explain it? (I guess if I read full facelets tutorial I will learn it, but I have not time to do it now, so no links to lengthy docs please :)
They are quite similar. The difference is mainly syntactical.
After observing their usage for some time it seems the convention is that fragments that you use only in a single situation are candidates for ui:include, while fragments that you re-use more often and have a more independent semantic are candidates for a custom tag.
E.g.
A single view might have a form with three sections; personal data, work history, preferences. If the page becomes unwieldy, you can divide it into smaller parts. Each of the 3 sections could be moved to their own Facelet file and will then be ui-include'ed into the original file.
On the other hand, you might have a specific way to display on image on many views in your application. Maybe you draw a line around it, have some text beneath it etc. Instead of repeating this over and over again you can abstract this to its own Facelet file again. Although you could ui:include it, most people seem to prefer to create a tag here, so you can use e.g. <my:image src="..." /> on your Facelets. This just looks nicer (more compact, more inline with other components).
In the Facelets version that's bundled with JSF 2.0, simple tags can be replaced by composite components. This is yet a third variant that on the first glance looks a lot like custom tags, but these things are technically different as they aren't merely an include but represent true components with declared attributes, ability to attach validators to, etc.

Resources