AngularJS - 1 Directive with ng-if statements or two directives? - javascript

Quick question regarding performance in Angular JS.
I have an ng-repeat listing out a load of search results. I use a directive/template to pull out each search item.
Is it better to use an ng-if statement wrapping two directives or to use one directive and have multiple ng-if statements within it, and use expression or statements.
E.G. Ng-if statements wrapping two directives:
<div ng-repeat>
<directive-1 ng-if="this"></directive-1>
<directive-2 ng-if="that"></directive-2>
</div>
OR
<div ng-repeat>
<directive-1></directive-1>
</div>
and within directive-1 you have...
<img src="1" ng-if="this">
<img src="2" ng-if="that">
<span>{{title || title2}}</span>
etc...

What ng-if is? It is directive (1), with isolated scope (2), that removes or append DOM(3).
What does this mean?
Ng-if takes time to initialize. (not much)
Ng-if create new isolated scope. (not much)
Ng-if operates with DOM. (possibly very expensive operation)
So, the main problem with preformance connected with DOM. Best way to speed up ng-if - is to reduce dom manipulation.
Also, try to avoid using ng-if with ng-repeat. Of course you can do this, but do this carefully.

Related

AngularJS Directive - Multiple directives same name

Let's say I have multiple directives with the same name "parent-elem" (on each page I can have a different number of these directives - dynamic number)
<div ng-app="app">
<div parent-elem></div>
<div parent-elem></div>
<div parent-elem></div>
</div>
Is there a way to know (inside the link function) AngularJS finished render all the directives with the same name on the page?
Inside the directive link function - how do I know this directive is the last rendered?
Note: Not using a ng-repeat
You should have had written, same directive, multiple times :P
Now, that totally depends on your directive, how you make it.
It can keep the counter in a service or rootScope may be whenever it is initialized.
If you are using ng-repeat which you should in this case, you have a bool $last that can tell you if the element rendered is last or not, which you can pass to the directive via any attribute.

Angular why asterisk (*)

In Angular document, * and template, we know that the *ngIf, *ngSwitch, *ngFor can be expanded to ng-template tag. My question is:
I think the ngIf or ngFor without * can also be translated and expanded to template tag by Angular engine.
The following code
<hero-detail *ngIf="currentHero" [hero]="currentHero"></hero-detail>
would be the same as
<ng-template [ngIf]="currentHero">
<hero-detail [hero]="currentHero"></hero-detail>
</ng-template>
So why bother designing a strange symbol asterisk(*) in Angular?
Asterisk syntax is a syntatic sugar for more wordy template syntax which directive expands to under the hood, you are free to use any of these options.
Quote from the docs:
The asterisk is "syntactic sugar". It simplifies ngIf and ngFor for
both the writer and the reader. Under the hood, Angular replaces the
asterisk version with a more verbose form.
The next two ngIf examples are effectively the same and we may write in either style:
<!-- Examples (A) and (B) are the same -->
<!-- (A) *ngIf paragraph -->
<p *ngIf="condition">
Our heroes are true!
</p>
<!-- (B) [ngIf] with template -->
<template [ngIf]="condition">
<p>
Our heroes are true!
</p>
</template>
Angular2 offers a special kind of directives - Structural directives
Structural directives are base on the <template> tag.
The * before the attribute selector indicates that a structural directive should be applied instead of a normal attribute directive or property binding. Angular2 internally expands the syntax to an explicit <template> tag.
Since final there is also the <ng-container> element that can be used similarly to the <template> tag but supports the more common short-hand syntax. This is for example required when two structural directives should be applied to a single element, which is not supported.
<ng-container *ngIf="boolValue">
<div *ngFor="let x of y"></div>
</ng-container>
Angular treats template elements in a special way. The * syntax is a shortcut that lets you avoid writing the whole <template> element. Let me show you how it works.
using this
*ngFor="let t of todos; let i=index"
translates it into
template="ngFor: let t of todos; let i=index"
which is then converted into
<template ngFor [ngForOf]="todos" .... ></template>
also Agular's Structural directives like ngFor, ngIf etc. Prefixed by * just to differentiate them from other custom directives and components
see more here
From Angular docs:
Structural directives are responsible for HTML layout. They shape or reshape the DOM's structure, typically by adding, removing, or manipulating elements.
As with other directives, you apply a structural directive to a host
element. The directive then does whatever it's supposed to do with
that host element and its descendants.
Structural directives are easy to recognize. An asterisk (*) precedes the directive attribute name as in this example.
<p *ngIf="userInput">{{username}}</p>
Sometimes you may need <a *ngIf="cond"> for example, when it's only one tag. sometimes you may want to put the ngIf around multiple tags without having a real tag as a wrapper which leads you to <template [ngIf]="cond"> tag. how can angular know wether it should render the the ngIf directive owner in the final result html or not? so it's something more than just making the code more clear. it's a necessary difference.

ng-repeat with includes: best performant way?

I am facing a performance issue with Angular (Ionic), and I'd like to know which would be the most performant way to solve this problem.
I get a list of objects from a service that I have to show in my app:
$scope.objectList = [
{
id: 123456,
name: "abcdefg",
state: [1|2|3|4|5|6|7|8...],
....
},
....
];
The list won't never have more than 20-25 objects.
The problem is that the elements in the list, although they are of the same type, they have to be shown in a quite different way. I have a different template for each posible state an object can be in.
I am using ng-repeat this way to show the list. The list won't change so I can use one way binding.
<div ng-repeat="obj in ::objectList track by obj.id">
....
</div>
Inside this ng-repeat I have to include the presentation template of each object, which changes depending on the object state.
I have tried different solutions but don'f find one that improves substantialy the rendering performance.
For example, I've tried this but I'm not sure that binding a function to ng-include is very performant.
HTML:
<div ng-repeat="obj in ::objects track by obj.id">
<div ng-include="getTemplateToInclude(obj)"></div>
</div>
JS:
$scope.getTemplateToInclude = function(obj){
if(obj.state === 1){
return "tmpls/template-a.html";
}else if(obj.state === 2){
return "tmpls/template-b.html";
}
....
};
Any suggestion of how to deal with this problem? Whicch would be the best way to include this different templates in the same list?
Do I have to use the one way binding notation(::) in the included templates or it's enough with the one used in the ng-repeat?
thanks in advance
I haven't used the one-way binding template syntax (::) so I won't speak to that. But based on your comments and original question, I think the best approach would be something along these lines:
create a single item renderer template that will process your repeater items
continue to use track by syntax in your repeater in order to recycle the item renderers
based on the item's type, use a combination of ng-show & ng-hide to display the correct children nested in your template.
<!-- item renderer template.html -->
<div>
<div ng-show="item.type == 'foo'"></div>
<div ng-show="item.type == 'bar'"></div>
<div ng-show="item.type == 'baz'"></div>
</div>
Creating a single template combined with track by in the repeater will reuse the DOM elements, negating the need to pull them from the DOM and then insert new ones.
Using ng-show or ng-hide vs. ng-if is based on the same idea as above. You are toggling the visibility of the DOM elements rather than destroying/creating them.
The reason I argue for this is that in nearly all performance-related tweaking, reusing or recycling objects are usually faster than destroying and then recreating new objects. This is certainly true of Javascript, and while I've yet to test that theory in DOM manipulation, I would bet it too is true. I'm making an educated guess but since you are using the Ionic framework, this should benefit mobile performance as well,
Just keep in mind, given the various permutations of what you could be showing and the number of rows of data, you might only see marginal performance gains. Depending on the number of bindings inside your template, you might reach a point where the performance penalty for Angular's digest cycle to complete is the same as the performance gains from recycling your DOM elements.

Using class with angular vs ng-class while using a mixed expression

I have a div that I want to give a dynamic class with AngularJS.
The div is withing an ng-repeat statement where lang.prefix is first en then sv
Using the following code works and sets the class to i-flag-en than i-flag-sv, but is it correct?
<div class="float-left flag i-flag-{{lang.prefix}}"></div>
I know there exist a ng-class directive which can be used to dynamically set the class of an element with AngularJS.
I think I read somewhere in a book, that the normal class directive not should be used to set the class property dynamically with AngularJS because of the way Angular manipulates the dom.
However, the following code does not work:
<div class="float-left flag" ng-class="i-flag-{{lang.prefix}}"></div>
and I rather want to set the class in this way instead of creating another scope variable.
Is it bad practice to use the class attribute with AngularJS to dynamically set the class? Does it work all the time even if it would be bad practice?
The book you have mentioned may have talked about the problems of using ng-class and class {{}} interpolations together wherein updates in the interpolation removes the ng-class classes, this problem has already been resolved, reference. Thus, using interpolation within class attributes is totally fine, and does not break good practice because both has its own quirks.
Your usage of ng-class however is incorrect, you have to concatenate the literal string value with your scope string variable:
<div class="float-left flag" ng-class="'i-flag-' + lang.prefix"></div>
but I think it is much preferable to use your first example instead:
<div class="float-left flag i-flag-{{lang.prefix}}"></div>

Angularjs - ng-model undefined

I am building a rather complex directive in which I need access to ng-model on a specific element in the template which is enclosed into ng-if directive... I have a plunker and the explanation on how to see the issue is below.
In the .compile function of the directive I have a line of code that looks like this
ngModel = elm.find('textarea').controller('ngModel');
console.log(ngModel);
In my template, I have a something like this:
<div ng-if="views.view">
<textarea ng-model="exp.val"></textarea>
</div>
When I use ng-show my console.log gets an object of ng-model, no problem ...
The requirement is very strict however and I have to use ng-if. When I use ng-if my console log gets undefined
The actual working version of the problem exists in this plunker
If you change the line 6 in template.html into ng-if you can see the behavior.
How do I have to write this line to retrieve the model when inclosed in ng-if.
ngModel = elm.find('textarea').controller('ngModel');
I also tried using attach-if directive by Simon Sparks. This directive is pretty cool, it preserves the scope unlike ng-if so if you specifically need to not render HTML but you need to preserve scope it works great.
I still have the same problem with invoking ngModel as I am doing it but because of applying custom filters in the directive I have to update ng-model in this way.
I am this one step away from finishing this directive. Hoping someone can help.

Categories