Thursday, 26 April 2018

Angular 4 Updates and some performance tips


Angular 4 — The update

Roughly six month after the release of Angular 2, the next big update for Angular is now available: Angular 4, or rather Angular v4, because the team decided it should be called “just Angular” from now on, without stating the version number explicitly in the name. Originally, the “2” was used to differentiate between AngularJS and the all new Angular Framework, which came with many reassessed and refined concepts. The result, Angular, can be used in many different programming Languages like Dart, TypeScript or ECMAScript 5 among others.
When Angular 2 got published, the team also decided to use “semantic versioning” for further versions, making the relationship between different versions in terms of compatibility and new features visible at a glance.
Version numbers are, therefore, set up according to the MAJOR.MINOR.PATCH schema. The particular digit is changed in accordance with the following criteria:
1.       MAJOR is increased when there is an incompatible change to the API. For Angular, this could be the case if newer versions of a utilized library like zone.js or rxjs gets adopted, that brings along a change to the API.
2.       MINOR is increased when additional functionality is provided, but existing functions and the API are kept stable. For Angular, this could be something like an additional pipe getting shipped as part of the framework.
3.       PATCH is increased when some troubleshooting is done without altering the API, thus being backward compatible.
https://jaxenter.com/wp-content/uploads/2017/04/semver.png
Semantic Versioning in Angular 4
In addition, there is an optional –QUALIFIER specifying a version in a more precise way. For example 4.0.0-rc.1 or 4.0.0 beta would be used to mark a beta version or release candidate.

A jump from 2 to 4. Up next: 8?

Some might have wondered why the version following Angular 2 isn’t Angular 3 but rather “Angular” v4. However, there is a good reason for this: version numbers of all Angular components were to be unified, with the Angular Router having already been developed up to version 3 and consequently had to be upgraded to v4 now. Version 4 was therefore chosen for the entire Angular Framework in the way of unification. Only Angular CLI, the command line tool for project planning and abstraction of build and test execution, has been released as Version 1.0.0.

This is new in Angular 4

The reasons for this new major release are both new features as well as changes incompatible with the previous version. Let’s take a look at the new features:

Router ParamMap

Starting from version 4, it is possible to query a so-called ParamMap in the router, meaning a request for the route- and queryparameter assigned to a route.
Up until now, route parameters were stored in a simple key-value object structure, therefore being accessible by using the standard JavaScript syntax (parameterObjekt[‘parameter-name’] ).
1
2
3
4
5
6
7
8
9
10
11
class MyComponent {
 sessionId: Observable<string>;
  
 constructor(private route: ActivatedRoute) {}
  
 ngOnInit() {
   this.sessionId = this.route
     .queryParams
     .map(params => params['session_id'] || 'None');
 }
}
Now, the parameters are also available as a map, so you can run them as simple method calls (parameterMap.get(‘parameter-name’)).
1
2
3
4
5
6
7
8
9
10
11
class MyComponent {
 sessionId: Observable<string>;
  
 constructor(private route: ActivatedRoute) {}
  
 ngOnInit() {
   this.sessionId = this.route
     .queryParamMap
     .map(paramMap => paramMap.get('session_id') || 'None');
 }
}
The utilization as a map also brings advantages in terms of type security. The old key-value structure had an unsafe type (type Params = {[key : string]: any}), whereby the parameter value could take all possible types. With the new map, however, the parameter value is either a string or an array of strings, depending on the method used. Type definition for individual values (ParamMap.get() : string) and type definition for multiple values (ParamMap.getAll() : string[]) apply.

Animations

Functions necessary for animations up until now were provided as part of @angular/core module, implying that these parts of the code were always included in applications, even if they did not get used in apps without animations. To avoid creating bundles with unnecessary large sizes, this function has been put into its own package. (This isn’t just a new feature but also a change making modifications to existing applications necessary if they contain animations.)
Animations are to be provided in the module BrowserAnimationsModule from @angular/platform-browser/animations.

Necessary adjustments for Angular 4

Let’s take a look at the necessary changes on existing projects to make them work with Angular 4. To facilitate future migrations for developers, the Angular team provides an interactive guide: https://angular-update-guide.firebaseapp.com/. However, since this project is still in its infancy, it should not be used just yet.

Dependencies update

Versions of Angular-Modules can be updated via npm:
npm install @angular/{common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router,animations}@next –save

Changed lifecycle events

Classes are not allowed to implement Lifecycle Events via inheritance – they need to use Interfaces instead:
Foo extends OnInit
needs to be changed to:
Foo implements OnInit
There won’t be many developers affected by this change, anyway it is rather easy to implement too.

Renaming template tags

Up to now, <template> was used as a Tag or Attribute for Templates. This has been marked as deprecated. It has to be replaced by <ng-template>

Access to Renderer

Renderer and RootRenderer could be used so far but that is not possible anymore. Access is now provided by RendererFactory2.

Use of animations

Animations have been part of Angular Core so far. Because of the separation in Angular 4, imports need to be adjusted when animations are being used.
1
2
3
4
5
6
7
import {Component, OnInit} from '@angular/core';
import {
  animate,
  state,
  style,
  transition,
  trigger } from '@angular/animations'
In the example, Angular Core still provides “Component” and “OnInit” while “animate”, “state”, “style”, “transition” and “trigger” are now imported from Angular Animations.

News in Angular CLI

Angular CLI is the command line tool that helps developers to comfortably develop, build and test their Angular project. In conjunction with Angular 4, Angular CLI version 1.0.0 was released – it is now a core element of the Angular Project.
A feature especially worth mentioning is the added support for alternative package managers besides npm: it now supports Facebook Yarn and cnpm, for instance.
Usage of Yarn can be activated this way:
ng set –global packageManager=yarn

Angular CLI

Further improvements include the standardized creation of Angular 4 projects by Angular CLI starting from version 1.0.0 and improved error messages while AoT-compiling of templates.

Prospect: Angular 5 and beyond

The world keeps on spinning and Angular keeps pace with it: Further releases are planned as well.
Ongoing work includes a module for ServiceWorker to realize stuff like push-notifications in Angular this way.
The current schedule is set for a new major release every six month. Angular 5 will therefore be ready around this October, Angular 6 should be released in March 2018. In between there will be some minor releases and bug fixes, if necessary – just like it has been since the release of Angular 2. Furthermore, the Angular team decided to do releases at specific time intervals:
§  Every week: Patch/bugfix releases
§  Every month: Minor release
§  Every six month: Major release, with emphasis on easy migration from the prior version
It is important to know that features marked as “deprecated” will be removed in the next release, giving developers six months to migrate if they want to update to the next major version immediately. That is quite recommendable, though, because migration bottlenecks will sooner or later cause problems due to being stuck with an old version. Also, strongly increased effort when migrating over many different versions would be a result.
From feedback they gained from the community, the Angular team learned that the development pace is considered to be “very quick” especially in enterprise environments. To meet the special needs of large organizations, offering a Long-Term-Support (LTS) for Angular is currently being considered, profiting from bugfixes over an extended period.
This might put some project managers’ minds at ease. However, it might still get interesting with regard to the question how upgrades from one LTS version to the next will be facilitated. Speaking from experience, it is rather beneficial to try and keep up with progress and profit from current developments. Furthermore, the exact length of LTS-Support periods for Angular and AngularJS is still being discussed.
Google is using a tool for automated code-migration in-house. There are some discussions going on about whether to release this tool for public use or build something similar for automated migrations and publish that.
This development shows that Angular – partially because they are always ready to listen to the community – grew into a mature and professional framework both suitable for use in small, agile teams as in large organizations

single-page application architecture promised to unlock the backend’s blazing performance, so we began searching for an appropriate framework, and identified AngularJS as a promising candidate. Following the “fail fast” principle, we began with our toughest challenge, the log view.
This is a real acid test for an application framework. The user can click on any word to search for related log messages, so there may be thousands of clickable elements on the page; yet we want instantaneous response for paging through the log. We were already prefetching the next page of log data, so the user interface update was the bottleneck. A straightforward AngularJS implementation of the log view took 1.2 seconds to advance to the next page, but with some careful optimizations we were able to reduce that to 35 milliseconds. These optimizations proved to be useful in other parts of the application, and fit in well with the AngularJS philosophy, though we had to break few rules to implement them. In this article, we’ll discuss the techniques we used.
log-view
A log of Github updates, from our live demo.
An AngularJS log viewer
At heart, the Log View is simply a list of log messages. Each word is clickable, and so must be placed in its own DOM element. A simple implementation in AngularJS might look like this:
<span class=’logLine’ ng-repeat=’line in logLinesToShow’><span class=’logToken’ ng-repeat=’token in line’>{{token | formatToken}} </span><br></span>
One page can easily have several thousand tokens. In our early tests, we found that advancing to the next log page could take several agonizing seconds of JavaScript execution. Worse, unrelated actions (such as clicking on a navigation dropdown) now had noticeable lag. The conventional wisdom for AngularJS says that you should keep the number of data-bound elements below 200. With an element per word, we were far above that level.
Analysis
Using Chrome’s Javascript profiler, we quickly identified two sources of lag. First, each update spent a lot of time creating and destroying DOM elements. If the new view has a different number of lines, or any line has a different number of words, Angular’s ng-repeat directive will create or destroy DOM elements accordingly. This turned out to be quite expensive.
Second, each word had its own change watcher, which AngularJS would invoke on every mouse click. This was causing the lag on unrelated actions like the navigation dropdown.
Optimization #1: Cache DOM elements
We created a variant of the ng-repeat directive. In our version, when the number of data elements is reduced, the excess DOM elements are hidden but not destroyed. If the number of elements later increases, we re-use these cached elements before creating new ones.
Optimization #2: Aggregate watchers
All that time spent invoking change watchers was mostly wasted. In our application, the data associated with a particular word can never change unless the overall array of log messages changes. To address this, we created a directive that “hides” the change watchers of its children, allowing them to be invoked only when the value of a specified parent expression changes. With this change, we avoided invoking thousands of per-word change watchers on every mouse click or other minor event.  (To accomplish this, we had to slightly break the AngularJS abstraction layer. We’ll say a bit more about this in the conclusion.)
Optimization #3: Defer element creation
As noted, we create a separate DOM element for each word in the log. We could get the same visual appearance with a single DOM element per line; the extra elements are needed only for mouse interactivity. Therefore, we decided to defer the creation of per-word elements for a particular line until the mouse moves over that line.
To implement this, we create two versions of each line. One is a simple text element, showing the complete log message. The other is a placeholder which will eventually be populated with an element per word. The placeholder is initially hidden. When the mouse moves over that line, the placeholder is shown and the simple version is hidden. Showing the placeholder causes it to be populated with word elements, as described next.
Optimization #4: Bypass watchers for hidden elements
We created one more directive, which prevents watchers from being executed for an element (or its children) when the element is hidden. This supports Optimization #1, eliminating any overhead for extra DOM nodes which have been hidden because we currently have more DOM nodes than data elements. It also supports Optimization #3, making it easy to defer the creation of per-word nodes until the tokenized version of the line is shown.
Here is what the code looks like with all these optimizations applied. Our custom directives are shown in bold.
<span class=’logLine’ sly-repeat=’line in logLinesToShow’ sly-evaluate-only-when=’logLines’><div ng-mouseenter=”mouseHasEntered = true”><span ng-show=’!mouseHasEntered’>{{logLine | formatLine }} </span><div ng-show=’mouseHasEntered’ sly-prevent-evaluation-when-hidden><span class=’logToken’ sly-repeat=’tokens in line’>{{token | formatToken }}</span></div></div>
<br>
</span>
sly-repeat is our variant of ng-repeat, which hides extra DOM elements rather than destroying them. sly-evaluate-only-when prevents inner change watchers from executing unless the “logLines” variable changes, indicating that the user has advanced to a new section of the log. And sly-prevent-evaluation-when-hidden prevents the inner repeat clause from executing until the mouse moves over this line and the div is displayed.
This shows the power of AngularJS for encapsulation and separation of concerns. We’ve applied some fairly sophisticated optimizations without much impact on the structure of the template. (This isn’t the exact code we’re using in production, but it captures all of the important elements.)
Results
To evaluate performance, we added code to measure the time from a mouse click, until Angular’s $digest cycle finishes (meaning that we are finished updating the DOM). The elapsed time is displayed in a widget on the side of the page. We measured performance of the “Next Page” button while viewing a Tomcat access log, using Chrome on a recent MacBook Pro. Here are the results (each number is the average of 10 trials):
Data already cached
Data fetched from server
Simple AngularJS
1190 ms
1300 ms
With Optimizations
35 ms
201 ms
These figures do not include the time the browser spends in DOM layout and repaint (after JavaScript execution has finished), which is around 30 milliseconds in each implementation. Even so, the difference is dramatic; Next Page time dropped from a “stately” 1.2 seconds, to an imperceptible 35 ms (65 ms with rendering).
The “data fetched from server” figures include time for an AJAX call to our backend to fetch the log data. This is unusual for the Next Page button, because we prefetch the next page of logs, but may be applicable for other UI interactions. But even here, the optimized version updates almost instantly.

 

Angular Performance  issues and best practices

 ng-if Is Better Than ng-show

The ng-show directive toggles the CSS display property on a particular element, while the ng-if directive actually removes the element from DOM and re-creates it if needed. Further, the ng-switch directive is an alternative to the ng-if with the same performance benefits.

 Don't Use ng-repeat

This is the biggest win for any app if it is not using the ng-repeat directive, so it is advisable to avoid ng-repeat and build the HTML using JavaScript. For vocalizing applications, the use of ng-if results in the addition of unnecessary watchers. The use of the ng-bind-html directive is a better solution to get rid of this problem.

 Use $watchCollection (Includes Third Parameter)

Using $watch with two parameters is good — but using $watch with three parameters, i.e. $watch(‘value’,function(){},true), makes Angular perform deep checking (to check every property of the object). But this could be expensive. Hence, to resolve such a performance issue, Angular provides $watchCollection(‘value’, function(){}) which acts almost alike $watch with 3rd parameter except it only checks first layer of object’s properties at low cost.

Use console.time for Debugging Issues

If your application is struggling with debugging issues and effecting Angular performance, then use console.time, which is a great API.

Debounce ng-model

You can debounce the input with ng-model. For example, to de-bounce the search input like GOOGLE, you have to use ng-model-options=”{debounce:250}”. This makes the digest cycle get triggered no more than once per 250ms due to the changes in this input model.


ngIf: Can also be used with “else”

It’s quite a frequent thing to use “conditional rendering” in templates to display information depending on some condition. This is done by using *ngIf. If a condition isn’t met, the corresponding element and all child elements are not added to the DOM-tree. Many times there was also a need for the opposing case, making it necessary to formulate the same condition just the other way around and add another *ngIf.
This has some nasty implications for readability and maintainability of the code – after all, you have to work on multiple lines of code when implementing some changes.
In Angular 4, this use case can be solved with a newly added else. Maybe unexpected for some, Angular uses a separately referenced template fragment, which in the else-case will be used in place of the element marked with *ngIf.
This example illustrates the difference in code between old and new syntax. Use case is to show the user name of a user which is logged in, or a login button for a user not logged in.
Old syntax:
1
2
3
4
5
Logged in as {{auth.username}}



Please log in: <button>Login</button>
New syntax:
1
2
3
4
5
6
7
8
9
<ng-template #needsLogin>
   

Please log in: <button>Login</button>

</ng-template>


  Eingeloggt als {{auth.username}}
The functionality of *ngIf in the field of reactive programming got improved when interacting with the async-pipe. It was already possible to “subscribe” (and “unsubscribe”) to asynchronous objects like observables and such from within a template by using the async-Pipe. The new *ngIf-Syntax now makes it possible to also add a local template variable to the result of the if-clause. In the example below the observable, placed inside the variable auth, is resolved by the async-pipe. The result can be used within the template by means of the user variable.
1
2
3
4
5
6
7
8
9
<ng-template #loading>
   

Loading User Data...

</ng-template>


  {{user.username }}
Note that the async-Pipe is not a necessary precondition for using the new syntax, but rather used for illustrational purposes in this example. Using any other Pipe or no Pipe at all will also work.

Dynamic Components with NgComponentOutlet

The new *ngComponentOutlet-Directive makes it possible to build dynamic components in a declarative way. Up until now, it has been quite a lot of work to build and produce components dynamically at runtime. It’s not enough to just write some HTML code! Angular needs to be notified about the component and add it to the lifecycle, take care of the data binding and change detection. The old way of using ComponentFactory therefore involved relatively much programming work.
The following example shows how that looks like with *ngComponentOutlet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component({
  selector: ‘app-first-time-visitor’,
  template: ‘
<h1>Welcome to our Page!</h1>

’,
})
export class FirstTimeVisitorComponent {}
@Component({
  selector: ‘app-frequent-visitor’,
  template: ‘
<h1>Welcome Back!</h1>

’,
})
export class FrequentVisitorComponent {}
@Component({
  selector: ‘app-root',
  template: `
         
<h1>Hello Angular v4!</h1>

        <ng-container *ngComponentOutlet="welcome"></ng-container>
  `,
})
export class App implements OnInit{
  welcome = FirstTimeVisitorComponent;
   
  ngOnInit() {
    if(!this.user.isfirstVisit){
      this.alert = FrequentVisitorComponent;
    }
  }
}
In this example, either FirstTimeVisitorComponent or FrequentVisitorComponent will be displayed as a greeting to visitors, depending on whether they are accessing the page for the first time or have visited it before. The check for prior visits is conducted in the OnInit-Lifecycle-Hook; based on the result, different components are handed over to the template for display.

TypeScript 2.1/2.2

Type security of Angular applications and the speed of ngc-Compiler have been improved – and for that, we need to thank the official support for the most recent TypeScript versions.

StrictNullChecks

Sadly, some parts of the improved Type Checks could not be included in Angular 4 [for now] because some incompatibilities were found in the RC phase. There are plans to include this feature in v4.1, though.

Angular Universal

With Angular Universal, it’s possible to render Angular applications outside of the browser, for instance directly on the web server. With that, JavaScript is no longer necessary for initially rendering the page content, so websites can be optimized better for search engines. Another use case is the utilization of WebWorker Threads to render content outside the GUI Thread. This rendered content can simply be added to the DOM Tree for display later.
For Java developers, the tickets for Spring Framework support [or rather Spring Boot] might be of interest because it adds the option for server side rendering via Nashorn or J2V8:
Unfortunately, no results that could be used in production are available yet. Angular Universal can already be used in different programming languages through node-Runtime.

New Pipe: Title Case

The new titlecase-Pipe will change the first letter of each word to upper case, while all other letters remain in lower case.

Forms get assigned “novalidate” automatically

Up until now, forms had to be marked with “novalidate” if HTML5-validation by the browsers was supposed to be suppressed and full control over the validation was to be given to the Angular application.
Therefore, developers marked all their forms as “novalidate” on a regular basis. Angular 4 will set this attribute automatically.

Source maps also for templates

When it comes to debugging and finding errors, source maps are of vital importance. They show the relation from source code to result and thereby help to narrow down the error. The new Template Compiler will create such source maps also for templates. Therefore there is more contextual information available both when working with the browser debugger and also in Crash-Reports and Log-Messages.

Angular Performance: ng-show vs ng-if

 | Comments
You’ve probably come across ng-if and ng-show and wondered why they both exist and what’s the difference between them. After all they usually have the same behavior as far as the user is concerned.
The devil is in the details and the differences between these directives can allow your to boost your application’s performance easily.

The Differences

Both ng-show and ng-if receive a condition and hide from view the directive’s element in case the condition evaluates to false. The mechanics they use to hide the view, though, are different.
ng-show (and its sibling ng-hide) toggle the appearance of the element by adding the CSS display: none style.
ng-if, on the other hand, actually removes the element from the DOM when the condition is false and only adds the element back once the condition turns true.
Since ng-show leaves the elements alive in the DOM, 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 cases where you have a few big views that are toggled with ng-show you might be noticing that things are a bit laggy (like clicking on buttons or typing inside input fields).
If you just replace that ng-show with an ng-if you might witness considerable improvements in the responsiveness of your app because those extra watches are no longer happening.
That’s itreplace ng-show and ng-hide with ng-if!

Caveats and Pitfalls of ng-if

  • Measure, measure, measure: As with every optimization, you should not apply this without measuring and validating that it does speed up your app. It can, potentially, actually make things slower, as I explain below.
  • Your controllers will be rerun: The controllers and directives in the element that’s being removed and added again will actually be recreated and so their initialization logic will run again. This is in contrast to ng-show where things are always there in memory, and so are only initialized once. You need to make sure your code handles being rerun properly.
  • Sometimes initialization is more expensive than keeping things around: That’s to say that in some cases the cost of removing the element from the DOM and then recreating it to add again can be a heavy operation all by itself. In those cases you might feel that it takes too long for the element to reappear. In those cases this trick might actually degrade your app’s performance, so remember to check and measure!
That’s it. Enjoy making your app a bit snappier. If you care about performance in Angular you really should read about speeding ng-repeat with the track by syntax.

Flat ES-Modules (Flat ESM / FESM)

Instead of many small files that belong to a module, “flat” versions of modules will also be delivered. In this context, flat means that only one file is shipped per module, containing everything belonging to that module. These flat modules are supposed to help with better performance on compiling and on execution in the browser. Furthermore, this shall improve Tree Shaking and Build, so applications will become smaller.

New View Engine for phenomenal speed

From templates and the @Component Elements, Angular generates the View-Layer. In doing so, there are the Just-In-Time Compiler (JIT) and the Ahead-of-Time Compiler (AoT). The JIT is primarily used in development, essentially being an Interpreter. The AoT Compiler generates executable (JavaScript-)Code with embedded HTML fragments from templates and components. This step, often called Codegen, produces a lot of code: Event-Handling, Change-Detection, Data-Binding and handling of dynamic component behaviour will be woven into the result.
Applications generated this way will be very fast but still got a lot of code that slows down the initial launch-time. All that code has to be downloaded first anyway!
The Angular-Team takes a very open approach to this, using other frameworks, for instance the Inferno.js framework, as a reference for performance and design decisions. The different requirements, goals and approaches for the template layer in Angular are also discussed in the extensive design documentation for the View Engine.
As a result, AOT-compiled templates in Angular 4 are substantially smaller, both reducing loading times and at the same time increasing overall page speed through the reduced code size. On average, one can expect a reduction of 60 percent, making for some significant improvement especially on mobile applications.
Additionally, the amount of work to be done by garbage collectors in the browser was reduced, also resulting in some noticeable improvements on performance. The generated code is not just well suited to be packed with classic tools like gzip. It can also be minified with Google Closure Compiler, reducing the size even further.


In this article by Chandermani, the author of AngularJS by Example, we focus our discussion on the performance aspect of AngularJS. For most scenarios, we can all agree that AngularJS is insanely fast. For standard size views, we rarely see any performance bottlenecks. But many views start small and then grow over time. And sometimes the requirement dictates we build large pages/views with a sizable amount of HTML and data. In such a case, there are things that we need to keep in mind to provide an optimal user experience.
Take any framework and the performance discussion on the framework always requires one to understand the internal working of the framework. When it comes to Angular, we need to understand how Angular detects model changes. What are watches? What is a digest cycle? What roles do scope objects play? Without a conceptual understanding of these subjects, any performance guidance is merely a checklist that we follow without understanding the why part.
Let’s look at some pointers before we begin our discussion on performance of AngularJS:
·         The live binding between the view elements and model data is set up using watches. When a model changes, one or many watches linked to the model are triggered. Angular’s view binding infrastructure uses these watches to synchronize the view with the updated model value.
·         Model change detection only happens when a digest cycle is triggered.
·         Angular does not track model changes in real time; instead, on every digest cycle, it runs through every watch to compare the previous and new values of the model to detect changes.
·         A digest cycle is triggered when $scope.$apply is invoked. A number of directives and services internally invoke $scope.$apply:
o    Directives such as ng-clickng-mouse* do it on user action
o    Services such as $http and $resource do it when a response is received from server
o    $timeout or $interval call $scope.$apply when they lapse
·         A digest cycle tracks the old value of the watched expression and compares it with the new value to detect if the model has changed. Simply put, the digest cycle is a workflow used to detect model changes.
·         A digest cycle runs multiple times till the model data is stable and no watch is triggered.
Once you have a clear understanding of the digest cycle, watches, and scopes, we can look at some performance guidelines that can help us manage views as they start to grow.
(For more resources related to this topic, see here.)

Performance guidelines

When building any Angular app, any performance optimization boils down to:
·         Minimizing the number of binding expressions and hence watches
·         Making sure that binding expression evaluation is quick
·         Optimizing the number of digest cycles that take place
The next few sections provide some useful pointers in this direction.
Remember, a lot of these optimization may only be necessary if the view is large.

Keeping the page/view small

The sanest advice is to keep the amount of content available on a page small. The user cannot interact/process too much data on the page, so remember that screen real estate is at a premium and only keep necessary details on a page.
The lesser the content, the lesser the number of binding expressions; hence, fewer watches and less processing are required during the digest cycle. Remember, each watch adds to the overall execution time of the digest cycle. The time required for a single watch can be insignificant but, after combining hundreds and maybe thousands of them, they start to matter.
Angular’s data binding infrastructure is insanely fast and relies on a rudimentary dirty check that compares the old and the new values. Check out the stack overflow (SO) post (http://stackoverflow.com/questions/9682092/databinding-in-angularjs), where Misko Hevery (creator of Angular) talks about how data binding works in Angular.
Data binding also adds to the memory footprint of the application. Each watch has to track the current and previous value of a data-binding expression to compare and verify if data has changed.
Keeping a page/view small may not always be possible, and the view may grow. In such a case, we need to make sure that the number of bindings does not grow exponentially (linear growth is OK) with the page size. The next two tips can help minimize the number of bindings in the page and should be seriously considered for large views.

Optimizing watches for read-once data

In any Angular view, there is always content that, once bound, does not change. Any read-only data on the view can fall into this category. This implies that once the data is bound to the view, we no longer need watches to track model changes, as we don’t expect the model to update.
Is it possible to remove the watch after one-time binding? Angular itself does not have something inbuilt, but a community project bindonce(https://github.com/Pasvaz/bindonce) is there to fill this gap.
Angular 1.3 has added support for bind and forget in the native framework. Using the syntax {{::title}}, we can achieve one-time binding. If you are on Angular 1.3, use it!

Hiding (ng-show) versus conditional rendering (ng-if/ng-switch) content

You have learned two ways to conditionally render content in Angular. The ng-show/ng-hidedirective shows/hides the DOM element based on the expression provided and ng-if/ng-switch creates and destroys the DOM based on an expression.
For some scenarios, ng-if can be really beneficial as it can reduce the number of binding expressions/watches for the DOM content not rendered. Consider the following example:
<div ng-if='user.isAdmin'>

   <div ng-include="'admin-panel.html'"></div>

</div>
The snippet renders an admin panel if the user is an admin. With ng-if, if the user is not an admin, the ng-include directive template is neither requested nor rendered saving us of all the bindings and watches that are part of the admin-panel.html view.
From the preceding discussion, it may seem that we should get rid of all ng-show/ng-hidedirectives and use ng-if. Well, not really! It again depends; for small size pages, ng-show/ng-hide works just fine. Also, remember that there is a cost to creating and destroying the DOM. If the expression to show/hide flips too often, this will mean too many DOMs create-and-destroy cycles, which are detrimental to the overall performance of the app.

Expressions being watched should not be slow

Since watches are evaluated too often, the expression being watched should return results fast.
The first way we can make sure of this is by using properties instead of functions to bind expressions. These expressions are as follows:
{{user.name}}

ng-show='user.Authorized'
The preceding code is always better than this:
{{getUserName()}}

ng-show = 'isUserAuthorized(user)'
Try to minimize function expressions in bindings. If a function expression is required, make sure that the function returns a result quickly. Make sure a function being watched does not:
·         Make any remote calls
·         Use $timeout/$interval
·         Perform sorting/filtering
·         Perform DOM manipulation (this can happen inside directive implementation)
·         Or perform any other time-consuming operation
Be sure to avoid such operations inside a bound function.
To reiterate, Angular will evaluate a watched expression multiple times during every digest cycle just to know if the return value (a model) has changed and the view needs to be synchronized.

Minimizing the deep model watch

When using $scope.$watch to watch for model changes in controllers, be careful while setting the third $watch function parameter to true. The general syntax of watch looks like this:
$watch(watchExpression, listener, [objectEquality]);
In the standard scenario, Angular does an object comparison based on the reference only. But if objectEquality is true, Angular does a deep comparison between the last value and new value of the watched expression. This can have an adverse memory and performance impact if the object is large.

Handling large datasets with ng-repeat

The ng-repeat directive undoubtedly is the most useful directive Angular has. But it can cause the most performance-related headaches. The reason is not because of the directive design, but because it is the only directive that allows us to generate HTML on the fly. There is always the possibility of generating enormous HTML just by binding ng-repeat to a big model list. Some tips that can help us when working with ng-repeat are:
·         Page data and use limitTo: Implement a server-side paging mechanism when a number of items returned are large.
Also use the limitTo filter to limit the number of items rendered. Its syntax is as follows:
<tr ng-repeat="user in users |limitTo:pageSize">…</tr>
Look at modules such as ngInfiniteScroll(http://binarymuse.github.io/ngInfiniteScroll/) that provide an alternate mechanism to render large lists.
·         Use the track by expression: The ng-repeat directive for performance tries to make sure it does not unnecessarily create or delete HTML nodes when items are added, updated, deleted, or moved in the list. To achieve this, it adds a $$hashKey property to every model item allowing it to associate the DOM node with the model item.
We can override this behavior and provide our own item key using the track by expression such as:
<tr ng-repeat="user in users track by user.id">…</tr>
This allows us to use our own mechanism to identify an item. Using your own track by expression has a distinct advantage over the default hash key approach. Consider an example where you make an initial AJAX call to get users:
$scope.getUsers().then(function(users){ $scope.users = users;})
Later again, refresh the data from the server and call something similar again:
$scope.users = users;
With user.id as a key, Angular is able to determine what elements were added/deleted and moved; it can also determine created/deleted DOM nodes for such elements. Remaining elements are not touched by ng-repeat (internal bindings are still evaluated). This saves a lot of CPU cycles for the browser as fewer DOM elements are created and destroyed.
·         Do not bind ng-repeat to a function expression: Using a function’s return value for ng-repeat can also be problematic, depending upon how the function is implemented.
Consider a repeat with this:
<tr ng-repeat="user in getUsers()">…</tr>
And consider the controller getUsers function with this:
$scope.getUser = function() {

   var orderBy = $filter('orderBy');

   return orderBy($scope.users, predicate);

}
Angular is going to evaluate this expression and hence call this function every time the digest cycle takes place. A lot of CPU cycles were wasted sorting user data again and again. It is better to use scope properties and presort the data before binding.
·         Minimize filters in views, use filter elements in the controller: Filters defined on ng-repeat are also evaluated every time the digest cycle takes place. For large lists, if the same filtering can be implemented in the controller, we can avoid constant filter evaluation. This holds true for any filter function that is used with arrays including filterand orderBy.

Avoiding mouse-movement tracking events

The ng-mousemoveng-mouseenterng-mouseleave, and ng-mouseover directives can just kill performance. If an expression is attached to any of these event directives, Angular triggers a digest cycle every time the corresponding event occurs and for events like mouse move, this can be a lot.
We have already seen this behavior when working with 7 Minute Workout, when we tried to show a pause overlay on the exercise image when the mouse hovers over it.
Avoid them at all cost. If we just want to trigger some style changes on mouse events, CSS is a better tool.

Avoiding calling $scope.$apply

Angular is smart enough to call $scope.$apply at appropriate times without us explicitly calling it. This can be confirmed from the fact that the only place we have seen and used $scope.$apply is within directives.
The ng-click and updateOnBlur directives use $scope.$apply to transition from a DOM event handler execution to an Angular execution context. Even when wrapping the jQuery plugin, we may require to do a similar transition for an event raised by the JQuery plugin.
Other than this, there is no reason to use $scope.$apply. Remember, every invocation of $apply results in the execution of a complete digest cycle.
The $timeout and $interval services take a Boolean argument invokeApply. If set to false, the lapsed $timeout/$interval services does not call $scope.$apply or trigger a digest cycle. Therefore, if you are going to perform background operations that do not require $scopeand the view to be updated, set the last argument to false.
Always use Angular wrappers over standard JavaScript objects/functions such as $timeout and $interval to avoid manually calling $scope.$apply. These wrapper functions internally call $scope.$apply.
Also, understand the difference between $scope.$apply and $scope.$digest$scope.$applytriggers $rootScope.$digest that evaluates all application watches whereas, $scope.$digestonly performs dirty checks on the current scope and its children. If we are sure that the model changes are not going to affect anything other than the child scopes, we can use $scope.$digest instead of $scope.$apply.

Lazy-loading, minification, and creating multiple SPAs

I hope you are not assuming that the apps that we have built will continue to use the numerous small script files that we have created to separate modules and module artefacts (controllersdirectivesfilters, and services). Any modern build system has the capability to concatenate and minify these files and replace the original file reference with a unified and minified version. Therefore, like any JavaScript library, use minified script files for production.
The problem with the Angular bootstrapping process is that it expects all Angular application scripts to be loaded before the application can bootstrap. We cannot load modules, controllers, or in fact, any of the other Angular constructs on demand. This means we need to provide every artefact required by our app, upfront.
For small applications, this is not a problem as the content is concatenated and minified; also, the Angular application code itself is far more compact as compared to the traditional JavaScript of jQuery-based apps. But, as the size of the application starts to grow, it may start to hurt when we need to load everything upfront.
There are at least two possible solutions to this problem; the first one is about breaking our application into multiple SPAs.

Breaking applications into multiple SPAs

This advice may seem counterintuitive as the whole point of SPAs is to get rid of full page loads. By creating multiple SPAs, we break the app into multiple small SPAs, each supporting parts of the overall app functionality.
When we say app, it implies a combination of the main (such as index.html) page with ng-app and all the scripts/libraries and partial views that the app loads over time.
For example, we can break the Personal Trainer application into a Workout Builder app and a Workout Runner app. Both have their own start up page and scripts. Common scripts such as the Angular framework scripts and any third-party libraries can be referenced in both the applications. On similar lines, common controllers, directives, services, and filters too can be referenced in both the apps.
The way we have designed Personal Trainer makes it easy to achieve our objective. The segregation into what belongs where has already been done.
The advantage of breaking an app into multiple SPAs is that only relevant scripts related to the app are loaded. For a small app, this may be an overkill but for large apps, it can improve the app performance.
The challenge with this approach is to identify what parts of an application can be created as independent SPAs; it totally depends upon the usage pattern of the application.
For example, assume an application has an admin module and an end consumer/user module. Creating two SPAs, one for admin and the other for the end customer, is a great way to keep user-specific features and admin-specific features separate. A standard user may never transition to the admin section/area, whereas an admin user can still work on both areas; but transitioning from the admin area to a user-specific area will require a full page refresh.
If breaking the application into multiple SPAs is not possible, the other option is to perform the lazy loading of a module.

Lazy-loading modules

Lazy-loading modules or loading module on demand is a viable option for large Angular apps. But unfortunately, Angular itself does not have any in-built support for lazy-loading modules.
Furthermore, the additional complexity of lazy loading may be unwarranted as Angular produces far less code as compared to other JavaScript framework implementations. Also once we gzip and minify the code, the amount of code that is transferred over the wire is minimal.
If we still want to try our hands on lazy loading, there are two libraries that can help:
·         ocLazyLoad (https://github.com/ocombe/ocLazyLoad): This is a library that uses script.js to load modules on the fly
·         angularAMD (http://marcoslin.github.io/angularAMD): This is a library that usesrequire.js to lazy load modules
With lazy loading in place, we can delay the loading of a controller, directive, filter, or service script, until the page that requires them is loaded.
The overall concept of lazy loading seems to be great but I’m still not sold on this idea. Before we adopt a lazy-load solution, there are things that we need to evaluate:
·         Loading multiple script files lazily: When scripts are concatenated and minified, we load the complete app at once. Contrast it to lazy loading where we do not concatenate but load them on demand. What we gain in terms of lazy-load module flexibility we lose in terms of performance. We now have to make a number of network requests to load individual files.
Given these facts, the ideal approach is to combine lazy loading with concatenation and minification. In this approach, we identify those feature modules that can be concatenated and minified together and served on demand using lazy loading. For example, Personal Trainer scripts can be divided into three categories:
o    The common app modules: This consists of any script that has common code used across the app and can be combined together and loaded upfront
o    The Workout Runner module(s): Scripts that support workout execution can be concatenated and minified together but are loaded only when the Workout Runnerpages are loaded.
o    The Workout Builder module(s): On similar lines to the preceding categories, scripts that support workout building can be combined together and served only when the Workout Builder pages are loaded.
As we can see, there is a decent amount of effort required to refactor the app in a manner that makes module segregation, concatenation, and lazy loading possible.
·         The effect on unit and integration testing: We also need to evaluate the effect of lazy-loading modules in unit and integration testing. The way we test is also affected with lazy loading in place. This implies that, if lazy loading is added as an afterthought, the test setup may require tweaking to make sure existing tests still run.
Given these facts, we should evaluate our options and check whether we really need lazy loading or we can manage by breaking a monolithic SPA into multiple smaller SPAs.

Caching remote data wherever appropriate

Caching data is the one of the oldest tricks to improve any webpage/application performance. Analyze your GET requests and determine what data can be cached. Once such data is identified, it can be cached from a number of locations.
Data cached outside the app can be cached in:
·         Servers: The server can cache repeated GET requests to resources that do not change very often. This whole process is transparent to the client and the implementation depends on the server stack used.
·         Browsers: In this case, the browser caches the response. Browser caching depends upon the server sending HTTP cache headers such as ETag and cache-control to guide the browser about how long a particular resource can be cached. Browsers can honor these cache headers and cache data appropriately for future use.
If server and browser caching is not available or if we also want to incorporate any amount of caching in the client app, we do have some choices:
·         Cache data in memory: A simple Angular service can cache the HTTP response in the memory. Since Angular is SPA, the data is not lost unless the page refreshes. This is how a service function looks when it caches data:
var workouts;

service.getWorkouts = function () {

   if (workouts) return $q.resolve(workouts);

   return $http.get("/workouts").then(function (response)

{

       workouts = response.data;

       return workouts;

   });

};
The implementation caches a list of workouts into the workouts variable for future use. The first request makes a HTTP call to retrieve data, but subsequent requests just return the cached data as promised. The usage of $q.resolve makes sure that the function always returns a promise.
·         Angular $http cache: Angular’s $http service comes with a configuration option cache. When set to true$http caches the response of the particular GET request into a local cache (again an in-memory cache). Here is how we cache a GET request:
$http.get(url, { cache: true});
Angular caches this cache for the lifetime of the app, and clearing it is not easy. We need to get hold of the cache dedicated to caching HTTP responses and clear the cache key manually.
The caching strategy of an application is never complete without a cache invalidation strategy. With cache, there is always a possibility that caches are out of sync with respect to the actual data store.
We cannot affect the server-side caching behavior from the client; consequently, let’s focus on how to perform cache invalidation (clearing) for the two client-side caching mechanisms described earlier.
If we use the first approach to cache data, we are responsible for clearing cache ourselves.
In the case of the second approach, the default $http service does not support clearing cache. We either need to get hold of the underlying $http cache store and clear the cache key manually (as shown here) or implement our own cache that manages cache data and invalidates cache based on some criteria:
var cache = $cacheFactory.get('$http');

cache.remove("http://myserver/workouts"); //full url

Using Batarang to measure performance

Batarang (a Chrome extension), as we have already seen, is an extremely handy tool for Angular applications. Using Batarang to visualize app usage is like looking at an X-Ray of the app. It allows us to:
·         View the scope data, scope hierarchy, and how the scopes are linked to HTML elements
·         Evaluate the performance of the application
·         Check the application dependency graph, helping us understand how components are linked to each other, and with other framework components.
If we enable Batarang and then play around with our application, Batarang captures performance metrics for all watched expressions in the app. This data is nicely presented as a graph available on the Performance tab inside Batarang:AngularJS by Example
That is pretty sweet!
When building an app, use Batarang to gauge the most expensive watches and take corrective measures, if required.
Play around with Batarang and see what other features it has. This is a very handy tool for Angular applications.
This brings us to the end of the performance guidelines that we wanted to share in this article. Some of these guidelines are preventive measures that we should take to make sure we get optimal app performance whereas others are there to help when the performance is not up to the mark.

Summary

In this article, we looked at the ever-so-important topic of performance, where you learned ways to optimize an Angular app performance.
AngularJS is one of the most popular Javascript frameworks available today. One of AngularJS's goals is to simplify the development process which makes it great for prototyping small apps, but its power allows scaling to full featured client side applications. The combination ease of development, breadth of features, and performance has led to wide adoption, and with wide adoption comes many common pitfalls. This list captures common AngularJS mistakes, especially when scaling an app.

1 MVC directory structure

AngularJS is, for lack of a better term, an MVC framework. The models aren't as clearly defined as a framework like backbone.js, but the architectural pattern still fits well. When working in an MVC framework a common practice is to group files together based on file type:
templates/
    _login.html
    _feed.html
app/
    app.js
    controllers/
        LoginController.js
        FeedController.js
    directives/
        FeedEntryDirective.js
    services/
        LoginService.js
        FeedService.js
    filters/
        CapatalizeFilter.js
This seems like an obvious layout, especially if coming from a Rails background. However once the app begins to scale, this layout causes many folders to be open at once. Whether using Sublime, Visual Studio, or Vim with Nerd Tree, a lot of time is spent scrolling through the directory tree.
Instead of keeping the files grouped by type, group files based on features:
app/
    app.js
    Feed/
        _feed.html
        FeedController.js
        FeedEntryDirective.js
        FeedService.js
    Login/
        _login.html
        LoginController.js
        LoginService.js
    Shared/
        CapatalizeFilter.js
This directory structure makes it much easier to find all the files related to a particular feature which will speed up development. It may be controversial to keep .html files with .js files, but the time savings are more valuable.

2 Modules (or lack thereof)

It's common when starting out to hang everything off of the main module. This works fine when starting a small app, but it quickly becomes unmanageable.
var app = angular.module('app',[]);
app.service('MyService', function(){
    //service code
});
app.controller('MyCtrl', function($scope, MyService){
    //controller code
});
A common strategy after this is to group similar types of objects:
var services = angular.module('services',[]);
services.service('MyService', function(){
    //service code
});
 
var controllers = angular.module('controllers',['services']);
controllers.controller('MyCtrl', function($scope, MyService){
    //controller code
});
 
var app = angular.module('app',['controllers', 'services']);
This strategy scales as well as the directory structures from part 1: not great. Following the same concept of grouping features together will allow scalability.
var sharedServicesModule = angular.module('sharedServices',[]);
sharedServices.service('NetworkService', function($http){});
 
var loginModule = angular.module('login',['sharedServices']);
loginModule.service('loginService', function(NetworkService){});
loginModule.controller('loginCtrl', function($scope, loginService){});
 
var app = angular.module('app', ['sharedServices', 'login']);
When working on large applications everything might not be contained on a single page, and by having features contained within modules it's much simpler to reuse modules across apps.

3 Dependency injection

Dependency injection is one of AngularJS's best patterns. It makes testing much simpler, as well as making it more clear upon which any particular object depends. AngularJS is very flexible on how things can be injected. The simplest version requires just passing the name of the dependency into the function for the module:
var app = angular.module('app',[]);
 
app.controller('MainCtrl', function($scope, $timeout){
    $timeout(function(){
        console.log($scope);
    }, 1000);
});
Here, it's very clear that MainCtrl depends on $scope and $timeout.
This works well until you're ready to go to production and want to minify your code. Using UglifyJS the example becomes the following:
var app=angular.module("app",[]);app.controller("MainCtrl",function(e,t){t(function(){console.log(e)},1e3)})
Now how does AngularJS know what MainCtrl depends upon? AngularJS provides a very simple solution to this; pass the dependencies as an array of strings, with the last element of the array being a function which takes all the dependencies as parameters.
app.controller('MainCtrl', ['$scope', '$timeout', function($scope, $timeout){
    $timeout(function(){
        console.log($scope);
    }, 1000);
}]);
This then minifies to code with clear dependencies that AngularJS knows how to interpret:
app.controller("MainCtrl",["$scope","$timeout",function(e,t){t(function(){console.log(e)},1e3)}])

3.1 Global dependencies

Often when writing AngularJS apps there will be a dependency on an object that binds itself to the global scope. This means it's available in any AngularJS code, but this breaks the dependency injection model and leads to a few issues, especially in testing.
AngularJS makes it simple to encapsulate these globals into modules so they can be injected like standard AngularJS modules.
Underscore.js is a great library for simplifying Javascript code in a functional pattern, and it can be turned into a module by doing the following:
var underscore = angular.module('underscore', []);
underscore.factory('_', function() {
  return window._; //Underscore must already be loaded on the page
});
var app = angular.module('app', ['underscore']);
 
app.controller('MainCtrl', ['$scope', '_', function($scope, _) {
    init = function() {
          _.keys($scope);
      }
 
      init();
}]);
This allows the application to continue in the AngularJS dependency injection fashion which would allow underscore to be swapped out at test time.
This may seem trivial and like unnecessary work, but if your code is using use strict(and it should be!), then this becomes a requirement.

4 Controller bloat

Controllers are the meat and potatoes of AngularJS apps. It's easy, especially when starting out, to put too much logic in the controller. Controllers should never do DOM manipulation or hold DOM selectors; that's where directives and using ng-modelcome in. Likewise business logic should live in services, not controllers.
Data should also be stored in services, except where it is being bound to the $scope. Services are singletons that persist throughout the lifetime of the application, while controllers are transient between application states. If data is stored in the controller then it will need to be fetched from somewhere when it is reinstantiated. Even if the data is stored in localStorage, it's an order of magnitude slower to retrieve than from with a Javascript variable.
AngularJS works best when following the Single Responsibility Principle (SRP). If the controller is a coordinator between the view and the model, then the amount of logic it has should be minimal. This will also make testing much simpler.

5 Service vs Factory

These names cause confusion for almost every AngularJS developer when starting out. They really shouldn't because they're syntactic sugar for (almost) the same thing!
Here are their definitions from the AngularJS source:
function factory(name, factoryFn) { 
    return provider(name, { $get: factoryFn }); 
}
 
function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
}
From the source you can see that service just calls the factory function which in turn calls the provider function. In fact, AngularJS also offers a few additional provider wrappers with valueconstant, and decorator. These other objects don't cause nearly the same level of confusion and the docs are pretty clear on their use cases.
Since service just calls the factory function, what makes it different? The clue is in $injector.instantiate; within this function the $injector creates a new instance of the service's constructor function.
Here's an example of a service and a factory that accomplish the same thing.
var app = angular.module('app',[]);
 
app.service('helloWorldService', function(){
    this.hello = function() {
        return "Hello World";
    };
});
 
app.factory('helloWorldFactory', function(){
    return {
        hello: function() {
            return "Hello World";
        }
    }
});
When either helloWorldService or helloWorldFactory are injected into a controller, they will both have a hello method that returns "Hello World". The service constructor function is instantiated once at declaration and the factory object is passed around every time it is injected, but there is still just one instance of the factory. All providers are singletons.
Why do the two styles exist if they can accomplish the same thing? Factories offer slightly more flexibility than services because they can return functions which can then be new'd. This follows the factory pattern from object oriented programming. A factory can be an object for creating other objects.
app.factory('helloFactory', function() {
    return function(name) {
        this.name = name;
 
        this.hello = function() {
            return "Hello " + this.name;
        };
    };
});
Here's an example controller using the service and the two factories. With the helloFactory returning a function, the name value is set when the object is new'd.
app.controller('helloCtrl', function($scope, helloWorldService, helloWorldFactory, helloFactory) {
    init = function() {
      helloWorldService.hello(); //'Hello World'
      helloWorldFactory.hello(); //'Hello World'
      new helloFactory('Readers').hello() //'Hello Readers'
    }
 
    init();
});
When starting out it is best to just use services.
Factories can also be more useful when designing a class with more private methods:
app.factory('privateFactory', function(){
    var privateFunc = function(name) {
        return name.split("").reverse().join(""); //reverses the name
    };
 
    return {
        hello: function(name){
          return "Hello " + privateFunc(name);
        }
    };
});
With this example it's possible to have the privateFunc not accessible to the public API of privateFactory. This pattern is achievable in services, but factories make it more clear.

6 Not utilizing Batarang

Batarang is an excellent Chrome extension for developing and debugging AngularJS apps.
Batarang offers Model browsing to get a look inside what Angular has determined to be the models bound to scopes. This can be useful when working with directives and to isolate scopes to see where values are bound.
Batarang also offers a dependency graph. If coming into an untested codebase, this would be a useful tool to determine which services should get the most attention.
Finally, Batarang offers performance analysis. AngularJS is quite performant out of the box, but as an app grows with custom directives and complex logic, it can sometimes feel less than smooth. Using the Batarang performance tool it is quite simple to see which functions are taking the most time in the digest cycle. The performance tool also shows the full watch tree, which can be useful when having too many watchers.

7 Too many watchers

As mentioned in the last point, AngularJS is quite performant out of the box. Because of the dirty checking done in a digest cycle, once the number of watchers exceeds about 2,000 the cycle can cause noticeable performance issues. (The 2,000 number isn't guaranteed to be a sharp drop off, but it is a good rule of thumb. Changes are coming in the 1.3 release of AngularJS that allow tighter control over digest cycles. Aaron Gray has a nice write up on this.)
This Immediately Invoked Function Expression (IIFE) will print out the number of watchers currently on the page. Simply paste it into the console to see how many watchers are currently on the page. This IIFE was taken from Words Like Jared's answer on StackOverflow:
(function () { 
    var root = $(document.getElementsByTagName('body'));
    var watchers = [];
 
    var f = function (element) {
        if (element.data().hasOwnProperty('$scope')) {
            angular.forEach(element.data().$scope.$$watchers, function (watcher) {
                watchers.push(watcher);
            });
        }
 
        angular.forEach(element.children(), function (childElement) {
            f($(childElement));
        });
    };
 
    f(root);
 
    console.log(watchers.length);
})();
By using this to determine the number of watchers and the watch tree from the performance section of Batarang, it should be possible to see where duplication exists or where unchanging data has a watch.
When there is data that does not change, but you want it to be templated using Angular, consider using bindonce. Bindonce is a simple directive which allows you to use templates within Angular, but this does not add a watch to keep the count from increasing.

8 Scoping $scope's

Javascript's prototype-based inheritance differs from class-based inheritance in nuanced ways. This normally isn't a problem, but the nuances often arise when working with $scope. In AngularJS every $scope inherits from its parent $scope with the highest level being $rootScope. ($scope behaves slightly differently in directives, with isolate scopes only inherit properties explicitly declared.)
Sharing data from a parent to a child is trivial because of the prototype inheritance. It is easy however to shadow a property of the parent $scope if caution is not taken.
Let's say we want to have a username displayed in a navbar, and it is entered in a login form. This would be a good first try at how this might work:
<div ng-controller="navCtrl">
   <span>{{user}}</span>
   <div ng-controller="loginCtrl">
        <span>{{user}}</span>
        <input ng-model="user"></input>
   </div>
</div>
Quiz time: when a user types in the text input with the user ng-model set on it, which template will update? The navCtrlloginCtrl, or both?
If you selected loginCtrl then you probably already understand how prototypical inheritance works.
When looking up literal values, the prototype chain is not consulted. If navCtrl is to be updated simultaneously then a prototype chain lookup is required; this will happen when the value is an object. (Remember, in Javascript, functions, arrays, and objects are objects)
So to get the desired behavior it is necessary to create an object on the navCtrl that can be referenced from loginCtrl.
<div ng-controller="navCtrl">
   <span>{{user.name}}</span>
   <div ng-controller="loginCtrl">
        <span>{{user.name}}</span>
        <input ng-model="user.name"></input>
   </div>
</div>
Now since user is an object, the prototype chain will be consulted and the navCtrl's template and $scope will be updated along with loginCtrl.
This may seem like a contrived example, but when working with directives that create child $scope's like ngRepeat this issue can arise easily.

9. Manual Testing

While TDD might not be every developer's preferred development method, every time a developer checks to see if code works or has broken something else he or she is doing manual testing.
There are no excuses for not testing an AngularJS app. AngularJS was designed to be testable from the ground up. Dependency injection and the ngMock module are evidence of this. The core team has developed a couple of tools that can bring testing to the next level.

9.1 Protractor

Unit tests are the building blocks of a test suite, but as the complexity of an app grows integration tests tease out more realistic situations. Luckily the AngularJS core team has provided the necessary tool.
We have built Protractor, an end to end test runner which simulates user interactions that will help you verify the health of your Angular application.
Protractor uses the Jasmine test framework for defining tests. Protractor has a very robust API for different page interactions.
There are other end to end test tools, but Protractor has the advantage of understanding how to work with AngularJS code, especially when it comes to $digestcycles.

9.2. Karma

Once integration tests have been written using Protractor, the tests need to run. Waiting for tests to run, especially integration tests, can be frustrating for developers. The AngularJS core team also felt this pain and developed Karma.
Karma is a Javascript test runner which helps close the feedback loop. Karma does this by running the tests any time specified files are changed. Karma will also run tests in parallel across different browsers. Different devices can also be pointed to the Karma server to get better coverage of real world usage scenarios.

10 Using jQuery

jQuery is an amazing library. It has standardized cross-platform development and is almost a requirement in modern web development. While jQuery has many great features, its philosophy does not align with AngularJS.
AngularJS is a framework for building applications; jQuery is a library for simplifying "HTML document traversal and manipulation, event handling, animation, and Ajax". This is the fundamental difference between the two. AngularJS is about architecture of applications, not augmenting HTML pages.
In order to really understand how to build an AngularJS application, stop using jQuery. jQuery keeps the developer thinking of existing HTML standards, but as the docs say "AngularJS lets you extend HTML vocabulary for your application."
DOM manipulation should only be done in directives, but this doesn't mean they have to be jQuery wrappers. Always consider what features AngularJS already provides before reaching for jQuery. Directives work really well when they build upon each other to create powerful tools.
The day may come when a very nice jQuery library is necessary, but including it from the beginning is an all too common mistake.

Conclusion
This code has been in production for two months, and we’re very happy with the results. You can see it in action at the Scalyr Logs demo site. After entering the demo, click the “Log View” link, and play with the Next / Prev buttons. It’s so fast, you’ll find it hard to believe you’re seeing live data from a real server.
Implementing these optimizations in a clean manner was a fair amount of work. It would have been simpler to create a single custom directive that directly generated all of the HTML for the log view, bypassing ng-repeat. However, this would have been against the spirit of AngularJS, bearing a cost in code maintainability, testability, and other considerations. Since the log view was our test project for AngularJS, we wanted to verify that a clean solution was possible. Also, the new directives we created have already been used in other parts of our application.
We did our best to follow the Angular philosophy, but we did have to bend the AngularJS abstraction layer to implement some of these optimizations. We overrode the Scope’s $watch method to intercept watcher registration, and then had to do some careful manipulation of Scope’s instance variables to control which watchers are evaluated during a $digest.


No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...