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?
I have a ng-repeat with multiple ng-show condition inside. Something like this (fictive example):
<ul>
<li ng-repeat="item in items | filter:searchFilter" >
<label ng-show="item.label==1">{{item.label}}</label>
<label ng-show="item.label==2">{{item.label}}</label>
<label ng-show="item.label==3">{{item.label}}</label>
<label ng-show="item.label==4">{{item.label}}</label>
<label ng-show="item.label==5">{{item.label}}</label>
<label ng-show="item.label==1">{{item.label}}</label>
<label ng-show="item.label==2">{{item.label}}</label>
<label ng-show="item.label==1">{{item.label}}</label>
</li>
</ul>
I am using ng-show for formatting purpose, e.g. :
I want to show the cellphone column, when the item.cellphone is not empty...
I have a big data source (+1000 rows) and I have noticed performance problem when I use the filter.
If I remove most of the ng-show conditions, the performance is good. Here's live example :
Ng-show performance problem
Without ng-show
I know you can improve the performance with a "track by" (here's an topic about it), but it look like it is not enough to make the filter "smooth" (at least, not too laggy).
Is there a way to improve the filter performance of ng-repeat with multiple ng-show condition and a large data source ?
Performance tuning really depends on some of the constraints that you're facing. Here are a few suggestions:
1) Do you really need to show/hide the labels, or will not creating them at all suffice? If they don't need to exist, use ng-if instead of ng-show. This will reduce the number of watchers as well as the number of DOM elements in your example drastically.
2) If you can use Angular 1.3+ and can assume the labels are static ids, use one-time binding to avoid having so many watchers {{::label}}
Modifying your example with these suggestions results in: http://jsbin.com/madefuqami/2/edit
Ultimately, however, if you keep adding elements then at some point your app will become slow. Angular's dirty-checker will look at each of these ng-show (or ng-if) and {{}} bindings on every $digest cycle. Plus the DOM will get unnecessarily large - there's a good chance that you don't need the browser to do all of the work associated with maintaining HTML and styling for element 3000 when only 1-50 fit on your screen.
A more robust solution would involve looking into pagination or virtualization. This can either be done server-side, or in Javascript.
I suggest server-side pagination. Ultimately, it will scale better and make for a cleaner solution. However, if you decide to go the Javascript virtualization route, there are libraries available already such as angular-virtual-scroll
In AngularJS,
how would I make a HTML exist only if a scope variable is true?
I know there is the ng-show directive but this will not work for me as it will only make it invisible with display: none, but what I need is actually that the element only exists in the DOM when something evaluates.
Something like this would work for me: <div ng-exists="myvar==myothervar"></div>
You can use Angular's ng-if directive (see the docs):
<div ng-if="myvar==myothervar"></div>
https://docs.angularjs.org/api/ng/directive/ngIf
is what you are looking all over
may be what shall give you what you are looking for. Check you angular version though.
Getting used to AngularJS and have been building quite a few directives.
Typically, where the directive is an Element I have been setting the replace option to true
I wondered though, does doing this have any negative impact on anything? I'm setting it to true so that it just keeps the source html nice and tidy, and as close to html specs as possible.
However, if it's likely that setting this to true has some negative impact I'd forego the specs...
So - are there advantages/disadvantages to replace:true ?
There are no negative impacts, but there is a specific use case for setting replace to true.
If you want to replace whatever markup may be in the element already with your own markup, set it to true.
If you want to live in harmony with whatever markup is already in the directive, set it to false.
One thing to keep in mind when using replace: true is that your template can only have one root node. So, if you want to have a template with several siblings at its root level, use replace: false and your template will be inserted into the inner HTML of the original element instead of replacing it, completely.
This rule is enforced because angular tries to transfer all attributes of the original element to the template's root node.
With that in mind, I think it's a stylistic choice: What makes most sense, semantically, for your application?
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);
}
}
}])