I have spent quite a lot of time reading through AngularJS documentation and several tutorials, and I have been quite surprised at how unapproachable the documentation is.
I have a simple, answerable question that may also be useful to others looking to pick up AngularJS:
What is an AngularJS directive?
There should be a simple, precise definition of a directive somewhere, but the AngularJS website offers these surprisingly useless definitions:
On the home page:
Directives are a unique and powerful feature available in AngularJS. Directives let you invent new HTML syntax, specific to your application.
In the developer documentation:
Directives are a way to teach HTML new tricks. During DOM compilation directives are matched against the HTML and executed. This allows directives to register behavior, or transform the DOM.
And there is a series of talks about directives which, ironically, seem to assume the audience already understands what they are.
Would anyone be able to offer, for clear reference, a precise definition of what a directive is that explains:
What it is (see the clear definition of jQuery as an example)
What practical problems and situations it is intended to address
What design pattern it embodies, or alternatively, how it fits into the purported MVC/MVW mission of AngularJS.
What it is (see the clear definition of jQuery as an example)?
A directive is essentially a function† that executes when the Angular compiler finds it in the DOM. The function(s) can do almost anything, which is why I think it is rather difficult to define what a directive is. Each directive has a name (like ng-repeat, tabs, make-up-your-own) and each directive determines where it can be used: element, attribute, class, in a comment.
† A directive normally only has a (post)link function. A complicated directive could have a compile function, a pre-link function, and a post-link function.
What practical problems and situations is it intended to address?
The most powerful thing directives can do is extend HTML. Your extensions are a Domain Specific Language (DSL) for building your application. E.g., if your application runs an online shopping site, you can extend HTML to have "shopping-cart", "coupon", "specials", etc. directives -- whatever words or objects or concepts are more natural to use within the "online shopping" domain, rather than "div"s and "span"s (as #WTK already mentioned).
Directives can also componentize HTML -- group a bunch of HTML into some reusable component. If you find yourself using ng-include to pull in lots of HTML, it is probably time to refactor into directives.
What design pattern does it embody, or alternatively, how does it fit into
the purported MVC/MVW mission of angularjs
Directives are where you manipulate the DOM and catch DOM events. This is why the directive's compile and link functions both receive the "element" as an argument. You can
define a bunch of HTML (i.e., a template) to replace the directive
bind events to this element (or its children)
add/remove a class
change the text() value
watch for changes to attributes defined in the same element (actually it is the attributes' values that are watched -- these are scope properties, hence the directive watches the "model" for changes)
etc.
In HTML we have things like <a href="...">, <img src="...">, <br>, <table><tr><th>. How would you describe what a, href, img, src, br, table, tr, and th are? That's what a directive is.
Maybe a really simple and initial definition for angular directives would be
AngularJS directives (ng-directives) are HTML attributes with an ng prefix (ng-model, ng-app, ng-repeat, ng-bind) used by Angular to extends HTML. (from: W3schools angular tutorial)
Some examples of this would be
The ng-app directive defines an AngularJS application.
The ng-model directive binds the value of HTML controls (input,
select, textarea) to application data.
The ng-bind directive binds application data to the HTML view.
<div ng-app="">
<p>Name: <input type="text" ng-model="name"></p>
<p ng-bind="name"></p>
</div>
Check this tutorial , at least for me it was one of the best introductions to Angular. A more complete approach would be everything that #mark-rajcok said before.
Looking at the documentation, directives are structures you can write that angularjs parses in order to create objects and behaviors.In other words it's a template in which you use mix of any arbitrary nodes and pseudo-javascript and placeholders for data to express intentions of how your widget (component) is structured, how it behaves and how it is feed with data.
Angularjs then runs against those directives to translate them into working html/javascript code.
Directives are there to so you can build more complex components (widgets) using proper semantics. Just take a look at the angularjs example of directives - they're defining the tab pane (which isn't of course valid in regular HTML). It's more intuitive than using like div-s or spans to create structure which is then styled to look like a tab pane.
In AngularJS Directives are html re markers for a HTML DOM element like an attribute(restrict- A), element name(restrict- E), comment(restrict- M) or CSS class(restrict - C) that tell AngularJS's HTML compiler ($compile) to perform a specified behavior to that DOM element or even transform the DOM element and its children.Some Example are ng-bind ,ng-hide/show etc.
The homepage is very clear about this:
When you hover over tabs in the last section:
We've extended HTML's vocabulary with a custom tabs
element. The tabs abstracts the complex HTML structure
and behavior necessary for rendering of tabs. The result is a more
readable view and very easily reusable syntax."
Then in the next tab:
angular.module('components', []).
directive('tabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope, $element) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
}
So you can invent html elements i.e tabs and let angular handle the rendering of those elements.
Related
I'm seeing weird behaviour when using containerless functionality for aurelia components. We're creating custom elements for in SVG container, which required us to use containerless tag to strip the custom element tags before adding it to the DOM, as only SVGElements tags are allowed within a SVG container.
We're using Aurelia release version 1.0.0 and build our SPA with webpack.
Here you can find a gistrun example which displays the 2 implementation of containerless usage.
https://gist.run/?id=58ba6282ad54c1263eec3a141fe42183
In this example i've created 2 viewmodels and bind this to our custom elements. Using as-element="compose" to tell aurelia that i've created the viewmodel and that Aurelia shouldnt create a VM. The difference between these to custom elements are the containerless tag:
CustomElement doesnt have the #containerless tag in the Viewmodel but have 'containerless' in the HTML view.
withattr component does'nt have 'containerless' in the HTML view, but it does have the #containerless tag in the Viewmodel, as described in the Aurelia HUB.
I expect in both situations that I would see a blue rectangle. customelement tags are stripped by Aurelia because of the containerless tag. however the #containerless tag doesnt seems to work, as in implementation 2.
Question:
Any clue why these implementation have different outputs?
Which is the correct one? I would expect 2, as stated in the Aurelia Docs that the #containerless tag should be placed on the viewmodel.
Any help would be appreciated :)
The #containerless decorator works directly on the element you place it on.
What's happening is that the decorator is applied to your withattr element, but as-element="compose" turns it into a compose element under the hood. This compose element then does not have the #containerless tag applied to it.
Likewise, with your customelement you are in fact not applying #containerless to customelement, but to the compose that it is turned into.
Remove the as-element="compose" part and simply declare your <withattr/> element naked in the markup, and containerless will work because the actual element will still be withattr.
Note that it's not recommended to use #containerless with as-element unless there is no other way to accomplish something, as is the case with using custom elements inside table elements.
Why not simply have a compose inside your custom element, and bind the path to the view through a bindable property on the custom element?
EDIT
Sorry, I kind of overlooked the fact that you wanted to specify your own ViewModel instance.
This requirement limits you to using the compose element because that's the only way Aurelia supports providing your own ViewModel instance.
It's also certain that you need #containerless. And you need that #containerless to be on the compose element.
Conclusion, your first solution seems perfectly fine from a technical perspective.
As a matter of personal preference, I would do this:
<compose containerless view.bind="'./customelement.html'" view-model.bind="customElementViewModel"/>
Instead of this:
<customelement containerless as-element="compose" view-model.bind="customElementViewModel"/>
To be a little more flexible with dynamic views, make it clearer that we're using compose, and not having to <require> the view. But that really boils down to preference and other requirements.
I am thinking of making a web app and was contemplating using dojox/app to do it.
I would prefer to use a more programmatic approach to dojo but it seems dojox/app is mostly declarative.
After some searching I found an archive basically asking the same question I have
http://dojo-toolkit.33424.n3.nabble.com/Questions-about-dojox-app-design-td3988709.html
Hay guys,
I've been looking at the livedocs for dojox.app and while it seems quite cool I >have to say some stuff isn't clear to me.
Specifically, is the "template" property of views - specifying an html file - a >must or optional?
This was in 2012.
Since then I have found the customeApp test in the examples in the documentation which seems to show basic programmatic views in dojox/app however I am having some difficulty understanding it.
I would like to create the different views of my app like this
require([
"dojo/dom",
"dojo/ready",
"dojox/mobile/Heading",
"dojox/mobile/ToolBarButton"
], function(dom, ready, Heading, ToolBarButton){
ready(function(){
var heading = new Heading({
id: "viewHeading",
label: "World Clock"
});
heading.addChild(new ToolBarButton({label:"Edit"}));
var tb = new ToolBarButton({
icon:"mblDomButtonWhitePlus",
style:"float:right;"
});
tb.on("click", function(){ console.log('+ was clicked'); });
heading.addChild(tb);
heading.placeAt(document.body);
heading.startup();
});
});
but I can only find examples like this
<div data-dojo-type="dojox/mobile/Heading" data-dojo-props='label:"World Clock"'>
<span data-dojo-type="dojox/mobile/ToolBarButton">Edit</span>
<span data-dojo-type="dojox/mobile/ToolBarButton"
data-dojo-props='icon:"mblDomButtonWhitePlus"'
style="float:right;" onclick="console.log('+ was clicked')"></span>
</div>
Is there a way to go about this programmatically or somewhere I can find some clarification on whats happening here https://github.com/dmachi/dojox_application/tree/master/tests/customApp
Absolutely. I have been creating them programmatically for a long time and believe it is far superior way than templating. Difficulty in tackling a framework is knowing keywords to search for. Your answer, I believe, can be found by learning Dojo WidgetBase, and anything else that uses the word "Widget".
Good start is here http://dojotoolkit.org/reference-guide/1.10/quickstart/writingWidgets.html . To successfully work with Dojo Widgets you will also need:
concept of a LifeCycle http://dojotoolkit.org/reference-guide/1.10/dijit/_WidgetBase.html#id5. Lifecycle injection points will allow you to modify DOM tree of the template using JavaScript native API so you do not have to use data-dojo in attributes all over. You will capture nodes as private class properties during buildRendering phase so you can apply constructor parameters to them passed during instantiation in the parent. Finally, you will return the final DOM in postCreate() or startup(), depending on whether you need to specially handle child components or not.
concept of Evented http://dojotoolkit.org/reference-guide/1.10/dojo/Evented.html . This is what you need to do widgetInstance.on("someEvent", eventHandler) programmatically
Only custom attribute I use within an HTML tags of templateString is data-dojo-attach-point and data-dojo-attach-event. These are very convenient, saving lots of time and makes data binding less bug prone, to automatically connect widget's class properties with values in the tag. Although you can do those programmatically too with innerHTML, the amount of tedious boilerplate code in my opinion is not worth the effort.
Go through that tutorial and if by end you do not understand something do let me know and I will elaborate (I am not the type who just sends askers away on a link and not bother elaborate on the material).
Let's say I have the following html in which masterdir and innerdir are custom directives:
<div master-dir>
<div inner-dir ng-repeat="x in set"></div>
</div>
How can I make the directives execute in the following order (without using $timeout, 'cause it looks ugly to me):
ng-repeat
n times innerdir
masterdir
Background: I want to alter the dom in every directive and the directives kind of depend on each other (the innerdir should alter the dom that ng-repeat produces and the masterdir should alter the dom produced by innerdir)
I've tried it with priority, require and pre/post/compile but I didn't find the right mix. I'm really out of ideas at this point.
In https://docs.angularjs.org/guide/compiler they say that, but none chart is there. Anyway, I found that in each directive docs page, in Directive Info section, the priority appears wrote like that (for ng-repeat):
ng-repeat:
Directive Info
This directive creates new scope.
This directive executes at priority level 1000.
I also found the next link that seems to be very helpful. I´m still reading it trying to learn (didn´t know too much about angular priorities). Hope this is helpful for you too.
http://www.bennadel.com/blog/2447-exploring-directive-controllers-compiling-linking-and-priority-in-angularjs.htm
Firstly, sorry for my poor English.
I'm learning AngularJS recently and I found that many of AngularJS's concept are very advanced especially the Directive. It's much like the Shadow DOM or Web Components, and even better.
But I came up with a question about the directive(We just talk about the directives with the restrict of 'E' and have a template which will replace or insert into the directive here.)
That is: How does a directive user(not the directive's writer) style a directive without knowing how it conforms? How could the user ensure that his style do not overwrite the directive's style?
As you know, directive is a reusable component, and much of the times, the user isn't the writer of the directives, he just use it.
Let's say a case here:
a directive written in HTML like this:
<myDirective></myDiective>
my be replaced by the below Emmet expression(or even more complex):
div>h1+ul>li*8>a+img
Here comes the question, how does the user style this myDirective? The user does not know the DOM structure of the directive.
Should he inspect the directive in the devtools when it is replaced by the template and then style the replaced DOM?
Or he should go check the source code of the directive?
If the user style the directive, how should he avoid the overwrite?
Or the directive writer should style the directive using it's namespace?
I think this is a big problem that AngularJS Directive should have to face.
Notice that this is difference with the Shadow DOM or the Web Components because the latter's style can not overwrited by the user, it's in an isolate style scope.
To style an html chunk, whatever code can be, you must see in action at least one time. Grab the markup, and then proceed to style it. You can see the code most of the times in the template property. (Most of the times, some others the markup is a composition).
It would be highly desirable that the markup doesn´t contain specific classes so when you - the user - start styling it, don´t have to face any styling issues.
If you are the directive code owner, then the things should be pretty straightforward. Even more, you can specify an url to the templateUrl property that just contains your html code for that given directive.
I would also encourage you to apply the OOCSS technique for styling reusable objects. And as a suggestion avoid the selectors that you have specified in your post, they don´t perform very well.
Regarding your questions:
1 and 2, yes, they´re valid options.
3 and 4, investigate OOCSS technique, and CSS Specificity concept.
Hope that this helps,
Best Regards.
Angular provides us with a mechanism to write directives - which is extremely powerful in what it can do. But the thing I keep wondering is - in what scenario should you be actually writing a custom directive of your own.
We keep seeing questions in and around Stack Overflow with various people attempting to write directives which ( in my opinion ) need not be written in the first place. In most cases they can be solved with a combination of repeat, switch and show. See examples of questions containing directives that I think shouldnt be directives in the first place!
https://stackoverflow.com/questions/16101073/angularjs-directive-is-not-working-in-ie-10
Fire button click in AngularJS
angularjs: using a directive inside the ui-bootstrap modal
Some examples scenarios. I am not picking on them in anyway..because I am sure it is not clear to anyone when we should be using / writing a directive.
We see scenario's where people use directives as a mechanism for templating. Is this the right way of doing things? Or is there a better way? ( ng-include perhaps? ) Are there any upsides / downsides to using directives as a templating mechanism? The reason for this question is that sometimes I wonder if people write directives because coming from the jquery world the first thing they can think of is writing DOM manipulating code and since the Angular way is to not manipulate the DOM in controllers it all gravitates towards writing all that code in a directive.
EDIT :
I believe this confusion ( of shoving things inside a directive ) arises because Angular does not have a separate concept of a "view" - unlike Backbone ( which only has a "view" but no component! ). Directives are amazing at defining components - But I think if you use them to create "views", you will lose some of the "angular" way. This is my opinion though -which is why I am soliciting what the rest of the angular community thinks.
The good thing about simpler directives ( directives that do just 1 thing! ) is that they are absolutely easy to test. If you look at all the ng directives they all do 1 thing and do that thing pretty well.
What is the best way of defining reusable "views" ( not components! ) in Angular ? Should that be written in a directive? Or is there a better way?
It would be awesome if one of the Angular Dev's have an opinion in this matter!
Well... quite a good question.
I believe directives are mainly meant to "extending HTML so you can build a DSL", improving productivity and code quality.
The question is that this is achieved through componentizing things. But it is of most importance that we understand that directive is not about visual components only, neither templating only, but also, about behavior.
To summarize, using directives you could:
create a DSL to augment elements behavior
create DSL widgets, so you can stop repeating yourself
wrapping already existent components, buying you productivity.
optimization
Augmenting behavior is nothing more than componentizing behavior. ng-click, for example, adds the clickable behavior to any element. Imagine you're creating an app with dozens of draggable elements. Than you would create a directive to augment element behavior, making it draggable without even touching the element visual (<span draggable>Test</span>). Yet another example, imagine you gonna have special hints on mouse hover. title attribute is not suitable to this, then you can create your own my-title attribute, that automatically create your "special hint" on mouse hover (<span my-title="Some caption">Test</span>).
And when developing an app, you have a plenty of domain specific concepts and behaviors. Stackoverflow, for example, have a strong concept of voting. You can vote up/down questions, answers, comments... So you could create a reusable votable directive, that would add "vote behavior" and "vote widget" (up/down arrows) to praticaly any element.
This last one gives us another face: templating. Not only for lazy ones, but to improve code quality and maintainability following DRY principle. If you are repeating controllers code, HTML structure, or anything else, why not templating and componentizing it, right? Directive is your guy for this job.
Of course, you also have some generic application of directives. Many application (not to say all of them) rely on clickable elements, this is why we have a ng-click, for example. Many applications have upload areas. And what would you do in a jQuery way of thinking? You would create a jQuery plugin. Right? In Angular, you would create a Angular Widget (using directives). You could even wrap an already existing plugin using a directive, and, once more, augmenting its behavior so it can smoothly talk to your application.
Regarding wrapping plugins, to every jQuery plugin (but could be MooTools, Ext...), you gonna have to create a controller, call $('element').plugin() on it, and caring that jQuery events change and $digest your scope for you. This is another perfect use of directive. You can create a directive that apply .plugin() on your element, listening to the events and changing/digesting your scope for you. This is what Angular UI Project is all about, take a look.
One last point is the optimization. I recently created and app that creates tables with dynamic columns and rows (a grid). The question is that data is updated in real time! And the ng-repeat inside ng-repeat, to create headers, was killing application performance (nested loops in every $apply cycle, happening each half second). So you can create a directive that create the columns template and still use ng-repeat inside it, but you would have a loop through columns only upon columns edition.
So, wrapping up, I believe directives are about componentizing behavior and form (templating), which buy you producitivity and code quality.
I personally write directives quite a lot, as they tend to make my program much more declarative.
An example: in a JSON -> HTML form parser I made recently, I created a "form-element" directive, that parses the JSON element a creating the necessary directives as it's children. That way I have a directive for each field type, with specific behavior and methods. Also, any common behavior shared between all elements is in the form-element directive.
This way, a group element is a directive with a ng-repeat in it's template, and a title element is as simple as a h1. But all can have the same conditional behavior (a group can only appear if a previous field has a certain value set, for instance). And all extremely clean - any time i need to add/change, it all stays perfectly put, and the html is extremely declarative.
EDIT - included a snippet of code, as requested via comments.
/**
* Form Element
* ============
*
* Handles different elements:
* Assigns diferent directives according to the element type
* Instanstiates and maintains the active property on the formElem
*/
.directive("formElement", ['$compile', function($compile){
return{
restrict: "E",
scope:{formElemModel: '='},
link: function(scope, element, attrs){
var template = '';
var type = scope.formElem.type;
switch (type){
case "field":
template =
"<form-field-"+scope.formElemModel.fieldType+" ng-switch-when='true'>\
</form-field-"+scope.formElemModel.fieldType+">";
break;
default:
template = "<form-"+type+" ng-switch-when='true' ></form-"+type+">";
break;
}
element.html(template);
$compile(element.contents())(scope);
// Active state of form Element
scope.formElem.active = true;
scope.testActive = function(){
if(scope.$parent.formElem && scope.$parent.formElem.active == false){
scope.formElem.active = false;
}
else{
scope.formElem.active =
scope.meetsRequirements(scope.formElem.requirements);
}
}
scope.$watch("meetsRequirements(formElem.requirements)", scope.testActive);
scope.$watch("$parent.formElem.active", scope.testActive);
}
}
}])