Recently I have been coming across a lot of repetition in Angular.
Example:
ngShow vs ngHide
Pristine vs Dirty
valid vs invalid
Won't ng-show="!condition" and ng-show="condition" cover all cases for showing and hiding content within the view?
Having both ngShow and ngHide directive seems in AngularJs as pointless as having an if and an ifNot statement in JavaScript.
Is there a reason for doing this? When should custom directives be written like this?
Related
I am wondering what the difference between ngIf and ng-if is. In some sources I have seen ngIf used and in some sources ng-if used. I know ngIf vs *ngIf. That is not my question so do not get confused with that question. The dash is what I do not understand.
According to Angular documentation, ngIf is an Angular (v +2) directive
, however, ng-if is an AngularJS (v 1.x) directive.
as also mentioned by #rcoro in comments.
I was thinking which one is faster ng-if or ng-switch? Let's say we have a case: 10 different divs and only one is needed at a time. Is there any difference in speed if ng-switch is used instead of ng-if?
If ng-if is used all the elements will be evaluated separately, but does ng-switch do the same?
Using angular 1.x
Both ng-if and ng-switch create their own scope. So at this point, there is no difference.
In the end, I think it pretty much depends on the use case.
If you have just a couple of elements, it would be probably better to use the ng-switch variant because, as put in my comment, ng-switch has a good chance to avoid matching all possible values as it is not possible in angularjs to create an if / else if / else if / else if clause. Using ng-if, all if conditions are always evaluated.
BUT
Since ng-show leaves the elements alive in the DOM (in contrast to ng-if), it means that all of their watch expressions and performance cost are still there even though the user doesn’t see the view at all. In very large views, that can come with a penalty.
ng-if is a ng-switch itself, the difference is only here that ng-if have only single expression.
so if you have only one expression it's better to use ng-if , otherwise use ng-switch. that's the only thing that you need to consider for using any of them.
Looking at Mastering Web Application Development in Angular, I tried to use the ng-if Directive to not show a <div> if an expression was false.
HTML
<div ng-controller="MyCtrl">
<div ng-if="showSecret">Secret</div>
</div>
JavaScript
var myModule = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.showSecret = (function() {
return false;
})();
}
But, looking at this JsFiddle(http://jsfiddle.net/64GCp/2/), I see that Secret gets rendered. Why?
It looks like you are using v1.0.1 and documentation here doesn't show an ngIf document.
The first instance of it I see is here in version 1.2.0
Changing the library version works: http://jsfiddle.net/64GCp/4/
The version of angular you are using does not support ng-if. Use a newer version. See my fiddlehttp://jsfiddle.net/SdYH5/
The options to hide and show are ng-show or ng-hide and later in version Angularjs 1.2.0+ ngIf
http://jsfiddle.net/64GCp/3/
All directives are executed over the DOM and therefore both ngShow or ngIf will be executed equally.
I have personally used ngShow in a page a large number of times(I decided to used "large number" to highlight the ambiguity) and tested it in all browsers including IE8 with no problems at all.
What the book is probably referring to is that the size of the page will not change because all ngShow/ngHide does is to hide the element:
ng-show output
<div ng-show="showSecret" style="display: none;">Secret</div>
While ngIf only leaves a comment:
ng-if output
<!-- ngIf: checked -->
Important: the first stable version to add ng-if was angular 1.2.0 make sure you have this version or higher before planning to use it.
Please understand 2 things, Angular is rapidly changing framework and so are the browsers it is always a good practice to search for benchmarks/versions to confirm or discard features. It is also possible that the internal functionality of directives may change overtime. For instance Angular Team announced that they dropped IE8 support in version 2.0.
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);
}
}
}])