Angular Runtime Performance Guide

Co-authored by Paul Spears and Andrew Wiens

1.0 Introduction

Smooth, highly-responsive interfaces increase users’ confidence in an application and create an overall positive experience. Whereas small applications with simple interactions are built without a focus on runtime performance, standard approaches sometimes do not scale well as the data size or feature complexity increases. A common scenario that may be familiar to the reader is a table that works well with small quantities of data but begins stuttering and lagging when the amount of data is increased. This guide will show how to increase performance in these kinds of applications.

Additionally, high framerates enable developers to build entirely new types of applications with Angular. Introducing animations and interactive graphics create new and exciting ways to engage with users. Here at Oasis Digital, we used the techniques in this guide to build an interactive visualization for issue tracking [1], multiple customer projects and a demo application that showcases the kind of performance that is possible within an Angular application [2].

Although we typically write Angular applications with relatively little concern for what Angular does behind the scenes, in performance-sensitive applications we achieve the desired responsiveness by knowing more about how Angular works. In this regard, an app’s implementation can have a large effect on performance: while Angular’s change detection system can complete hundreds of thousands of cycles in a few milliseconds for simple changes, application logic takes the overwhelming majority of time to execute. In this guide, we will describe how to meet the expectations of performance-sensitive applications, explain the relevant parts of Angular change detection, and highlight potential pitfalls along the way.

2.0 Toward 60 Frames Per Second

Fig. 1. Top level overview of execution control during change detection. Angular (red) calls application code (A; dark blue) during change detection (B) and updates the DOM. The browser (light blue) then updates the view, completing the change detection cycle (C). This cycle must complete in less than 17ms to achieve 60 FPS.

In the industry, 60 frames per second is the gold standard for application responsiveness, and any application that achieves it must render updates in less than a mere 17 milliseconds. Performance most often suffers in Angular applications when responding poorly to user input or other regularly-occurring events. The total time to re-render a view in response to any changes can be split into three parts: First, as shown in Figure 1A, application-specific callbacks are executed. Second, Angular’s change detection system runs as shown in Figure 1B. This system is responsible for delegating control to the application callbacks and using the results to notify the browser of any necessary DOM updates. The third piece in this process, the browser, paints the required changes. The application then waits for additional input before repeating this cycle (Figure 1C).

Since we generally only have control of our own code and how it interacts with Angular, improving runtime performance tends to involve optimizing three main aspects of our app:

  1. Executing application event handlers quickly
  2. Reducing the number of callback executions needed to complete a change detection cycle
  3. Reducing the execution duration of Angular’s change detection cycle

As the last two of these three aspects may imply, Angular’s change detection system has a substantial effect on runtime performance. Thus, it is important to gain a basic understanding of how the change detection system operates.

3.0 Angular Change Detection System

Once an Angular application is loaded, Angular listens for user events and other asynchronous events. Angular understands the context for these events and calls the appropriate handlers. After these handlers return, control is given back to Angular to perform change detection. Although Angular knows the data bindings between components, changes in other values may affect the template as well. For example, a template element may depend on a property of a shared object. Therefore, by default, the change detection system responds to updates by re-evaluating the template expressions of all components. If the change detection system determines that the value of a template expression has changed, it interacts with the browser to modify the corresponding portion of the DOM.

Fig. 2. Stepwise explanation of an Angular change detection cycle.

For example, a tree of components is shown in Figure 2. In this diagram, child components reside within their parents, and events can occur within any of the components. When a DOM event occurs, Angular will call the associated application event handler. Depending on how the application is structured, this may result in a component event firing rather than a DOM event. If a component event does fire, the associated event handler in the parent component is called, and this process is repeated. Once all events have been handled, Angular begins checking components and their templates for updates. This process starts from the root and works its way down to the leaves in a breadth-first manner.

Although this view of the change detection system is sufficient for our purposes, there are additional resources that explain the inner workings of this system. For a deeper explanation of Angular’s change detection system, see the blog posts from Victor Savkin and Nrwl.io [3-4].

4.0 Executing event handlers quickly

Event handlers can exist in numerous locations within an Angular application. The most obvious examples are DOM and component event bindings. An application responds to events such as mouse clicks or key presses by providing Angular a callback to execute as shown in Figure 3.

Fig. 3. A button executes a callback when clicked, effectively blocking change detection until the callback completes.

When such a callback is executed, Angular must wait for the callback to finish before change detection can continue. Once all events are processed, the change detection process evaluates template data bindings to determine which DOM properties to update. This process includes checking and updating component inputs. Angular provides developers control over how a component should respond to changes to its input bindings in the form of callbacks – OnChanges and input setters – which affect the execution time in a similar manner as event handlers.

The callbacks of event bindings, OnChanges, and input setters are the primary mechanisms for passing data between services and components in an Angular application, and it can be difficult to keep these slim. However, it is not always obvious how much code is executed during these callbacks.

4.1 Event Bindings

It is common practice to use event bindings for communicating user updates to shared locations such as services or components at a higher level of the hierarchy. Figure 4 shows a trivial example.

Fig. 4 A DOM event handler results in the execution of a service method.

As control moves between location,s additional processing is often required. For example, a search term combines with an array to produce a filtered list. The following code in Figures 5 and 6 demonstrates how a button click hands control to a long running service from the component. The service, in turn produces the filtered list.

Fig. 5 A component and service cooperate to produce a filtered list of instructors.

Fig. 6 The results of the calculation of Figure 5 are displayed on the screen with an ngFor.

Thus, a single event can percolate through multiple layers. By default, this computation will occur as part of the change detection cycle started by the original event binding. Figure 7 shows a stack trace of the click handler, change detection and the multiple layers of application code. Notice the “Long process” near the bottom of the image. This was inserted on line 42 of Figure 5 to emulate a calculation that could take longer than normal to run. The trace visually demonstrates that the change detection process cannot complete until all callbacks and their subsequent method calls have finished executing

Fig. 7 A stack trace demonstrating control flow during a click event.

Though not always obvious it is important to remember that function calls usually executes as part of change detection regardless of where they reside. A key to performance is being cognizant of this fact and writing code that respects it.

The pattern of calculating a new application state from user and system events is often used with great success in many enterprise scale applications. In particular the use of a library such as ngrx/store or redux strongly encourages it. In these situations, it is important to ensure that any reducers execute as efficiently as possible. Also, as we will see in the later section on RxJS Observables, it is also possible that event handlers may update an Observable. If the Observable pipeline executes synchronously, as in Figure 8, the cost of this computation is added to the total cost of the change detection cycle.

Fig. 8 The anonymous function defined on line 27 is executed as part of any change detection cycle in which the search value is updated.

4.2 Component Input Setters and OnChanges

Event handlers are not the only application code that executes during a change detection cycle. After event propagation completes, Angular continues the change detection cycle by updating the component hierarchy and template data bindings. As mentioned above, this process starts at the root component and works down towards the templates of the leaf components. Along the way, Angular will execute any setter methods associated with component inputs. Similarly, the ngOnChanges methods, similar to those in Figure 9 will be executed in components that implement OnChanges.

Fig. 9 Line 18 demonstrates the syntax for a basic ngOnChanges method.

Generally, problematic situations are created in the callbacks of the input setters and ngOnChanges relatively infrequently. It is often easier to spot problems when they do occur as issues are usually isolated to a single component. However, there are still a couple hazardous scenarios to point out. It is usually recommended to compute any state or UI changes needed as part of the event propagation phase of the change detection cycle. However, some situations may still occur that encourage the use of OnChanges to compute additional state needed locally within a component. Consider the filtered list example: For the sake of argument, assume that the current filter criteria and the unfiltered list are only available as inputs, and the filtered results must be computed immediately prior to display as shown in Figure 10.

Fig. 10 Demonstration of recalculating a filtered list as Input values update

This could be achieved by utilizing OnChanges. However, doing so would cause every input change to trigger a recalculation of the filtered list. If another input were added to the component (see Figure 11), there would be a wasted calculation every time the new input value is changed.

Fig. 11 The ngOnChanges method defined on lines 19 – 27 demonstrate a extraneous calculations that occur when the selectedInstructor is updated

Input setters serve a similar purpose as OnChanges, however they only fire in response to updates to a corresponding input. Generally speaking, the use of input setters will lead to more performant change handlers as there is no need for identifying which input changed, nor will it be called more often than is necessary. Although the granularity of input setters make for a better default choice, it is still possible to populate the callbacks with expensive operations, and they should be treated with the same level of care as OnChanges.

5.0 Reducing the quantity of call back executions needed

Executing application event handlers during change detection has the potential to hand execution control to multiple services and components. Being mindful of how the change detection cycle hands control to the various callbacks can help reduce its overall run time. For example, the updated values of any reactive form controls are passed to their subscribers, and the associated callbacks are then executed. This can be particularly costly if the application is undergoing a rapid succession of user input. If a debounce (.debounceTime) operator is applied to the value changes, then any processing is deferred until the input has settled. Figure 12 demonstrates the use of debounce by reducing the number of subscription callbacks that are executed. In this example the only values that are operated on are changes that occur after 350 milliseconds of stability to the search term.

Fig. 12 A list filtering example that debounces the user’s input

Similarly, when choosing to emit values to event emitters, any duplicate events whose processing does not provide value should not be emitted. Figure 13 demonstrates this by emitting search terms instead of acting on them immediately. However, it only emits values that have a semantic difference to the previous value.

Fig. 13 A demonstration of selectively emitting values based on context

Also, when working with data-bound objects, Angular calculates equality by reference. This means that OnChanges will fire each time a bound object’s reference changes even if its content has not. Being intentional about changing such backing data can reduce the number of unneeded OnChange and input setter executions.

5.1 Controlling change detection

The effects of carefully controlling which callbacks are executed are magnified when taking direct control of change detection. The description provided earlier concerning change detection was based on Angular’s default behavior. However, Angular has an API that provides additional methods for controlling how and when change detection runs. The first of these APIs, ChangeDetectionStrategy.OnPush, will change the behavior of change detection for a given component. When applied, the change detection process will skip the component unless one of its inputs change or an Observable connected to an async pipe in its template receives an update. Consequently, any child components, located within the component’s template, will also be skipped. The change detection process can thus be reduced to only checking exactly what is needed to render changes by structuring the application to take advantage of this API. Figure 14 illustrates what this looks like by showing the step-wise checks that take place in one such scenario.

Fig. 14 Demonstration of change detection with OnPush in play

Utilizing this new strategy the filtered list code above can easily be rearranged to meet such a requirement as demonstrated in Figures 15 – 17.

Fig. 15 The instructor-list component written to utilize OnPush. Notice that Inputs are the only source of change

Fig. 16 The template for the instructor-list component is also free of data mutation.

Fig. 17 The app-component html indicates that the filtered list is computed before providing it to app-instructor-list.

Alternatively, it is also possible to request that change detection be stopped entirely for a component. Figure 18 demonstrates some of the options available when controlling change detection manually. How to use this properly and effectively is highly dependent on the situation. It is rare that performance issues need this level of control to be resolved and its use should be reserved for exceptional cases.

Fig. 18 A brief highlight of the API available for manual change detection

Another way to control change detection is to execute long-running code outside of change detection entirely. If a particular block of code can be executed asynchronously, Angular provides an API to mark a callback to run outside of change detection. Using this API will allow the current change detection process to complete and the browser to rerender. The callback will then execute; when it finishes, a new change detection cycle will begin to display the results.

Example Coming Soon!

For particularly expensive calculations, a web worker can be used in conjunction with manual change detection. The following repository contains an example that runs d3 force calculation – a particularly expensive operation – inside a web worker [2]. The results are returned after completion, and Angular is informed that change detection is needed using a component change detector reference.

https://github.com/dpsthree/angular-performance-playground/blob/master/src/app/d3-helper.service.ts

6.0 Reducing the duration of change detection

During change detection, Angular checks which data bindings need to be updated to apply the most recent changes. Features built into Angular can be leveraged to speed up this process; similarly, there are pitfalls that can make this process slower.

6.1 Template Methods

Angular has a very convenient feature that allows binding data directly to the result of a method call. By using Angular’s template binding syntax to assign an attribute to a method, the results will be recalculated with every change detection cycle. While this can be convenient, it also adds the results of these calculations to the cost of every change detection cycle. This cost has the potential to greatly impact an application’s responsiveness, for example, when binding to a method is combined with an ngFor. There are generally two approaches for improving performance when this happens: pre-computing the results or implementing the method as a pure pipe.

The most common situation in which an ngFor is combined with a method call is to perform a calculation based on each entry that is displayed. Rather than recomputing the display value on every change detection, there is often opportunity to calculate the additional properties as needed. For example consider the following code:

Fig. 19 (Before) A simple template binding that executes numClasses for each entry in instructorList on every change detection cycle

Fig. 20 (Before) The backing component class for the template sources its data with no upfront processing. Line 37 defines the method to call from the template

Fig. 21 (After) After some changes in how the instructorList is obtained, there is now a numClasses property that contains the desired value

Fig. 22 (After) The backing component class demonstrates how the desired property could be computed upon retrieval and added to the objects.

In this example, object properties are only recalculated if the list changes. This occurs significantly less often than each change detection cycle, possibly never again. This is the most performant way to handle such situations, but it can sometimes be difficult to achieve.

Creating and using a custom pure pipe is generally far more convenient than restructuring the application’s data flow, but it is slightly less performant. A pure pipe is a pipe that behaves much like a pure function: The results of executing it are based solely on its input, and the input is left unchanged. When using a pure pipe in place of method bindings, the pipe is still executed each change detection cycle. However, the execution will benefit from the fact that Angular caches the results of previous executions: If a pipe is executed more than once with the same parameters, the results of the first execution are returned. As a result, although the pipe will still be invoked each change detection cycle in place of the method call, performance will benefit from the caching provided by Angular. Figures 23 and 24 demonstrate the previous example once more with the utilization of a pure pipe.

Fig. 23 The template now executes a pipe to produce the desired class count

Fig. 24 A custom pipe is introduced, removing the need to precompute the additional data property.

6.2 ngFor

ngFor can also cause excessive DOM manipulation. By default, when iterating over a list of objects, Angular will use object identity to determine if items have been added, removed, or rearranged. This works well for most situations. However, if immutable practices are utilized when updating the data within the list, the identities will be updated and ngFor will generate a new collection of DOM elements to be rendered. If the list is long or complex enough, this will increase the time it takes the browser to render the change. To mitigate this issue, it is possible to use trackBy to tell Angular how to identify the entries as seen in figures 25 and 26.

Fig. 25 Expanding a basic ngFor to utilize a trackBy method

Fig. 26 Line 23 shows the method structure for a trackBy method.

This will reduce the amount of DOM regeneration needed to render any changes even in the case of rapid changes to an immutable data set.

6.3 AOT

The goal of change detection is to translate data changes into a newly-rendered view by updating DOM attributes. Angular runs in just-in-time (JIT) mode by default where its interpretation of component templates is executed as part of the digest cycle. This mode of operation is great when building and debugging an application, but it adds significant overhead in the browser at run time. Compiling Angular using the command line interface (CLI) with both prod mode and ahead-of-time (AOT) compiling reduces this overhead by precompiling the application’s component templates and removing the need for JIT processing.

7.0 Observable Pipelines

Observables are a powerful abstraction for dealing with asynchronous events. Proper usage can result in drastically reduced line counts in an application. However, as a source of change in Angular applications, they should be subject to the same performance scrutiny as component event handlers and change handlers. Observables are closely related to all three of the primary points listed in section 2.0. As such, it is crucial to select the right operators and understand how they are used to ensure that an application’s performance is not degraded by their use.

7.1 distinctUntilChange

When using Observables it is not uncommon that an Observable may emit consecutive duplicates. Depending on the situation it may not be of any benefit to reprocess the same data twice. Rxjs provides an operation, distinctUntilChanged, that will filter any duplicate, consecutive updates from flowing downstream [6]. This operation is shown in Figure 27 as a marble diagram by RxMarbles [7].

Fig. 27 Marble diagram showing how values pass into and out of distinctUntilChanged

7.2 share

It is quite common in an Angular application to use the data that flows out of an Observable in more than one location. When this happens all of the upstream processing needed to produce the data executes once for each subscription and usage of async pipe. If the callback in the Observable pipeline contain any sufficiently lengthy calculations the cost will add up quickly. Ideally the computation would be executed once for each unique update and the result would be made available to all subscribers. This can be achieved with the observable operator share [8]. Figure 28 utilizes share by sending the result of the Http call into a list display as well as a method used to calculate the total number of classes. In the absence of share, any processing that may be added between lines 12 and 16 of Figure 29 (as well as the Http request!) will be executed for each reference to “results”.

Fig. 28 Lines 16 and 31 demonstrate separate uses of the same Observable data.

Fig. 29 share is introduced on line 16 to prevent extraneous calculations

7.3 withLatestFrom

Another common use case with Observables is the need to combine multiple streams of data together to calculate some results. This is most often achieved with the usage of the combineLatest operator. As the name implies it is used to combine the latest results from each Observable that is passed in. The callback is passed the results and executed each time any of the supplied observables receive an update. There are situations however, where the calculations need only run when one specific observable changes, but the most recent values of other others are still needed for calculation. In these scenarios it is possible to reduce the number of executions by switching to withLatestFrom [9]. As described above, withLatestFrom will rerun the desired calculation only when the observable it is applied to changes, but makes available the most recent values of all other observables passed as parameters. This operation is shown in Figure 30 as a marble diagram.

Fig. 30 Marble diagram showing how values pass into and out of withLatestFrom

7.4 throttleTime

Some forms of streaming data occur at a very high frequency. Though it may not be necessary to display each update in the UI. Some use cases only require notifying the user of updates once every n milliseconds. In these situations it may be possible to utilize an operator called throttleTime [10]. This operation is shown in Figure 31 as a marble diagram.

Fig. 31 Marble diagram showing how values pass into and out of throttleTime

8.0 Conclusion

Angular’s change detection system is incredibly quick. However, the ease that Angular affords developers to synchronize custom application functionality to UI updates makes it possible to create unintended performance bottlenecks. Knowing where to look to eliminate these bottlenecks can be difficult. Armed with the performance improvements outlined in this guide, a motivated Angular developer can meet runtime performance needs by designing the application to use Angular’s resources optimally or moving code blocks outside of the Angular layer.

9.0 References

  1. http://expium.com/visualizer-for-jira
  2. https://www.angularperformanceplayground.com/app/graph
  3. https://vsavkin.com/change-detection-in-angular-2-4f216b855d4c
  4. https://blog.nrwl.io/essential-angular-change-detection-fe0e868dcc00
  5. https://github.com/dpsthree/angular-performance-playground/blob/master/src/app/d3-helper.service.ts
  6. http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-distinctUntilChanged
  7. http://rxmarbles.com/http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-share
  8. http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-withLatestFrom
  9. http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-throttleTime

Dependency Injection in Angular 2.0 (alpha) – Angular Lunch

Another in our series of Angular lunch talks. Paul Spears walks through dependency injection in Angular 2.0. This covers an alpha version, the details could easily be different when the real thing ships. Recorded 2015-09-15.

Transcript

We have transcribed the talk to text, provided below.

Continue reading Dependency Injection in Angular 2.0 (alpha) – Angular Lunch

Getting Started With DI in Angular 2

One of the core features of Angular 2 is dependency injection. This post serves as an introduction to using DI in Angular 2 apps.

Hierarchical DI

Angular 2 uses a hierarchical system for managing injectable objects. For developers this means that the choice of having a new instance or a previously created one is up to the consumer of the injectable. Let’s see how this works. Consider the following example of a single component and a single service (service definition not shown):

@Component({
    selector: 'parent-component'
    bindings: [MyUtilsService]
})
@View({
    template: '<h1>Hello World</h1>'
})
class ParentComponent{
    constructor(myUtils: MyUtilsService) {
        console.log(myUtils.getMessage());
    }
}

In the above sample line 3 creates a binding to “MyUtilsService”. This informs Angular that ParentComponent needs a fresh injector to provide access to MyUtilsService. The reference to MyUtilsService on line 9 is the actual request to perform injection. At this point Angular will create a new instance of MyUtilsService which is available in ParentComponent’s constructor. Let’s add another piece.

@Component({
    selector: 'child-component-1',
})
@View({
    template: '<h2>Child 1 Content</h2>'
})
class ChildComponent1{
    constructor(myUtils: MyUtilsService) {
        //Same instance of myUtils as injected into ParentComponent
        console.log(myUtils.getMessage());
    }
}

@Component({
    selector: 'parent-component'
    bindings: [MyUtilsService]
})
@View({
    template: '<child-component-1></child-component-1>'
    directives: [ChildComponent1]
})
class ParentComponent{
    constructor(myUtils: MyUtilsService) {
        console.log(myUtils.getMessage());
    }
}

In this example ChildComponent1 is also referencing an injection of MyUtilsService. Since a binding was not specified for MyUtilsService on ChildComponent1 Angular injects the same instance that was created for the ParentComponent. But what if we wanted a new instance? Let’s say that ParentComponent manipulates the state of MyUtilsService in some way that makes its use undesirable in some additional child component. To achieve this a new instance would be needed instead of using the parent’s copy.

//ChildComponent1
@Component({
    ...
})

//ChildComponent2
@Component({
    selector: 'child-component-2'
    bindings: [MyUtilsService]
})
@View({
    template: '<h2>Child 2 Content</h2>'
})
class ChildComponent2{
    constructor(myUtils: MyUtilsService) {
        //This references a different instance of myUtils from ParentComponent and ChildComponent1
        console.log(myUtils.getMessage());
    }
}
@Component({
    selector: 'parent-component'
    bindings: [MyUtilsService]
})
@View({
    template: '<child-component-1></child-component-1><child-component-2></child-component-2>'
    directives: [ChildComponent1, ChildComponent2]
})
class ParentComponent{
    constructor(myUtils: MyUtilsService) {
        console.log(myUtils.getMessage());
    }
}

Now a new instance of the MyUtilsService is requested on line 9 by creating a child injector. As a result, ChildComponent2’s constructor will be supplied with a different instance than was injected into the ParentComponent. Lets add one more layer and assume that ChildComponent2 is the only component in this hierarchy in need of a separate instance of MyUtilsService. ChildComponent2’s child wants to inject the same instance that was created for and injected into ParentComponent. One may assume the following approach can be taken:

//ChildComponent1
@Component({
    ...
})

//ChildComponent2
@Component({
    selector: 'child-component-2'
    bindings: [MyUtilsService]
})
@View({
    template: '<grandchild-component></grandchild-component>'
    directives: [GrandchildComponent]
}

//GrandchildComponent
@Component({
    selector: 'grandchild-component'
    bindings: [MyUtilsService]
})
@View({
    template: '<h3>Grandchild Content</h3>'
})
class GrandchildComponent{
    constructor(myUtils: MyUtilsService) {
        //This will look for and find the instance of MyUtilsService created by ChildComponent2 
        console.log(myUtils.getMessage());
    }
}

//ParentComponent
@Component({
    ...
}

However, the GrandChildComponent is receiving a third instance of MyUtilsService. This is also not ideal. To correct this and achieve the desired result let’s revisit ChildComponent2 and make a few small adjustments.

//ChildComponent1
@Component({
    ...
})

//ChildComponent2
@Component({
    selector: 'child-component-2'
    viewBindings: [MyUtilsService]
})
@View({
    template: '<grandchild-component></grandchild-component>'
    directives: [GrandchildComponent]
}

//GrandchildComponent
@Component({
    selector: 'grandchild-component'
})
@View({
    template: '<h3>Grandchild Content</h3>'
})
class GrandchildComponent{
    constructor(myUtils: MyUtilsService) {
        //This will look for and find the instance of MyUtilsService created by ChildComponent2 
        console.log(myUtils.getMessage());
    }
}

//ParentComponent
@Component({
    ...
}

View bindings allow for the creation of injectors that will create instances of an injectable that are only available to the component they are declared on. When MyUtilsService is injected into the GrandchildComponent the DI system will climb the hierarchy looking for an injector that is aware of MyUtilsService. The instance created by ChildComponent2 is not present in the hierarchy and instead the instance created on the ParentComponent is used.

App Level Instances

In addition to creating dependency bindings at the component level it is also possible to specify a set of top level, application dependencies. This is done using the bootstrap function to initialize the application. For example:

bootstrap(ParentComponent, [MyUtilsService])

Now any component of the application can inject the same instance of MyUtilsService or request a new copy using the binding approaches listed above.

Conclusion

There is great flexibility and power in the new dependency system. This article covers some of the basics that can be used to get your application off the ground but there is still plenty more to learn about how it works under the hood. For more information about how the Angular 2 dependency injection system works check out one of the following resources:
http://victorsavkin.com/post/102965317996/angular-2-bits-unified-dependency-injection
https://angular.io/docs/js/latest/api/di/

Angular 2: Working With Alphas

At the St. Louis Angular Lunch this month, I talked about working with the recent Angular 2 alpha versions. The rapid changes to the Angular framework have made it difficult to pin down its usage. As a result information explaining how one might build a full application has been mostly non-existent. The goal of this talk was to explain some of the big picture concepts of how Angular 2 applications are composed and provide live coded examples.  See the video embedded here, and transcript below.

 

Transcript

Here is the talk, transcribed to text. This is a lightly edited, draft transcription, so any errors are probably from that process.

Continue reading Angular 2: Working With Alphas

Building a Configurator in Angular

Introduction

Modl Buildr is a configurator that allows users to select and configure a product of choice. It was inspired by a desktop configurator application and the question, “How can this problem be solved better in the browser?.”

As with many of our projects, we used AngularJS. Built as a framework for enhancing and augmenting the HTML language, Angular provides developers with a toolset supporting web applications consisting of dynamic, data driven content. Angular’s primary components include services for the isolation of business logic, routing for application architecture, controllers for marshaling data and directives for concise templates and view. Modl Buildr is created on these four core concepts.

We introduced Modl Buildr in a previous blog post.

If you prefer a video exclamation to text, the video below explains how it works in considerable detail.

For text, read on.

Routing

Client side routing is needed in most non-trivial Angular applications. The router allows users to navigate between features of the application without the need for additional server round-trips. When establishing the routes of the application Modl Buildr takes advantage of the syntax and creates a declarative road map of the application’s “pages”. The state declarations that are added highlight what routes are available and their associated models, views and controllers. By utilizing the resolve mechanism it is immediately clear what pieces of data are needed in order for any given route to function properly. This also removes the need for the controller to communicate with an API service or contain promise handling boilerplate.

Controllers and Services

Connecting the models to the view are various combinations of controllers and services. The router takes advantage of custom services to allow the creation of clean, simple and maintainable controllers. Consider the following code used to connect the landing page’s view to its model.

Controller:

(function () {
   'use strict';
   angular.module('app.home', [])
       .controller('HomeCtrl', function ($state, featuredProducts, popularProducts, productDataService) {
           var hctrl = this;
           hctrl.featured = featuredProducts;
           hctrl.popular = popularProducts;
           hctrl.selectProduct = selectProduct;

           //Prefetch for next route in navigation
           productDataService.getProductList();

           function selectProduct(selectedProduct) {
               $state.go('configure', {productId: selectedProduct.qsBaseID});
           }
       });
}());

Services:

that.getFeaturedList = function () {
   return that.getProductList().then(function (list) {
       return _.where(list, { Featured: true });
   });
};

that.getPopularList = function () {
   return that.getProductList().then(function (list) {
       return _.where(list, {Popular: true});
   });
};

The code responsible for obtaining and shaping the data is isolated nicely in a service, consumed by the router, and made available to the controller. Meanwhile, the controller simply passes the data to the view. This MVVM (MVC, MVW*) pattern is seen throughout the application.

Not all of the controllers and services are this simple. Some data bindings require constant updating in the form functions. For example, as a user configures a product there is a significant amount of calculation that occurs to determine if the product’s configuration is still valid. The result of this calculation is bound to the view for the user to see. Best practices encourage a clean separation of controllers and services while allowing the service to take responsibility for complex computation. The following code snippet demonstrates how Modl Buildr achieves this separation while maintaining two way data binding.

(function () {
   'use strict';
   angular.module('app.configure')
       .controller('ConfigureCtrl', function ($scope, $stateParams, $state,
                    productDataService, productStateService,appStateService) {
           var confCtrl = this;

           //the result of execution valid changes with every user input
           confCtrl.valid = valid;
           confCtrl.reset = reset;
           confCtrl.goToSummary = goToSummary;
           confCtrl.optionDisplay = optionDisplay;

           //Guard against extra API calls by only updating when the user’s selections have changed
           $scope.$watchCollection(function () {
               return confCtrl.selections;
           }, function (n, o) {
               if (n && n._seriesId) {
                  productDataService.getValidity(n).then(function () {
                       updateAll();
                   });
               }
           });

           function goToSummary() {
               var modelNumber = productStateService.productOptions.BaseLabel;
               modelNumber += _.map(productStateService.levelsToBuildModelNumber(), function (level) {
                   return productStateService.selections[level.Tag];
               }).join('');
               $state.go("summary", {productId: productStateService.productOptions.qsBaseID, number: modelNumber});
           }

           //This is called after every change to the user options
           function updateAll() {
               confCtrl.levels = productStateService.levelsToConfigure();
               confCtrl.modelDisplayOptions = productStateService.levelsToBuildModelNumber();
               confCtrl.valid = productStateService.valid;
               confCtrl.selections = productStateService.selections;
               confCtrl.productInfo = productStateService.productOptions;
           }
       });
}());

In the example above productInfo (the model number) and selections (the user’s choice of options) are bound to the view. The data in productInfo is a result of a function whose input is the selections. This approach not only provides a simple set of bindings, but also matches the application’s primary use case perfectly. The primary use case of the application is to select from a set of options and generate the corresponding model number. The challenge with this solution is that the server acts as a block box while performing the calculation. Fortunately the binding function is designed in such a way that it doesn’t matter where the complex calculation takes place or what it does. As the selections are updated the controller makes a service call and updates the bindings based upon the updated data that’s returned. The services handle the question of what to do and isolates the controller from any complexities or potential changes to this process.

Directives

There are several elements that appear on multiple screens of the application. These include simple pieces of functionality such as a hover text explaining why a button is disabled to custom widgets such as the product tiles. While controllers and services are great for laying out the big picture. Directives shine when used to create reusable and named components.

Modl Buildr contains a custom product tile directive. A reusable tile with a card-like appearance is created by combining a bootstrap panel, an image and a styled definition list. This tile is used to present products to the user. By specifying a few custom properties the same tile directive can be used across multiple situations.

Jade Template:

div.panel.panel-default.products-container(ng-click="click()",
   ng-class="{'product-tile': configure==='false', 'configure-tile': configure==='true'}")
   div.panel-heading
       | {{product.BaseLabel}}
       i.fa.fa-2x.fa-star.pull-right.featured(ng-show="product.Featured")
   div.panel-body.product-details
       img(ng-src="{{product.Image}}")
       hr
       dl.dl-horizontal.centered-block
           dt Name:
           dd {{product.BaseDesc}}
           dt Mfr:
           dd {{product.Manufacturer}}
           dt Item Type:
           dd {{product.ItemType}}
           dt Subtype:
           dd {{product.ItemSubType1}}

Javascript:

(function () {
   'use strict';
   angular.module('app.tile', [])
       .directive('descriptionTile', function () {
           return {
               restrict: 'E',
               templateUrl: "app/tile/descriptionTile.html",
               scope: {
                   product: '=info',
                   click: '&',
                   configure: '@'
               }
           };
       });
}());

With this definition and an ng-repeat these tiles are easily stamped out in a grid formation. This directive is also used on subsequent screens to display the product being configured.

Another helpful directive is the model number directive. The model number at the top of the configure and summary screens is generated by the users actions. Rather than complicate the main page controllers with the logic for displaying this interactive control a directive was created to isolate this concern. Now a model number can be displayed anywhere in the application by adding the following line of HTML (Jade) in the desired location:

model-number(product-name='{{cfg.productInfo.BaseLabel}}',
    current-selections='cfg.modelDisplayOptions',
    scroll-target='scroll-container')
// (the above is Jade, http://jade-lang.com/

Conclusions

Angular turned out to be a good fit for this application, yielding a working solution with a modest amount of code. Moreover, the application works quite nicely in a browser, at least as nicely as the native desktop application we were inspired by. The browser environment in 2015 is completely suitable for complex rules-based configurators.

 

 

Modl Buildr – A Rules Based Configurator Demo

Introduction

Modl Buildr is a demo application we created at Oasis Digital, a rules-based “configurator”. Its inspiration comes from older desktop application configurators. Modl Buildr can be used to generate model numbers for highly customizable products. A user can select a product, choose from a set of options for that product, review the final product and request a quote. This article explains the details of this process and highlights some key features.

For this demo application, we picked an example of a product with lots of options – hospital beds. All of the data used for this demo is openly available on the Internet; it is not from any customer project. (Our customers’ real information is all hidden behind firewalls and NDAs…)

Choosing a product

Users can select a product from a short list of common products, or by searching a catalog. The common products are shown first:

image05

Should the user choose to browse the product catalog, they see each product presented as a “tile” containing an image of the product and any additional spec information.

image04

Adjacent controls provide the ability to filter through the catalog. This is done by typing a product’s name or choosing one of several product categories. This was designed to resemble many other online marketplaces to create a familiar experiences for first time users.

As the user interacts with the filters the catalog responds immediately by adding or removing the products that fit the user’s criteria. The results respond to the user’s input without the need for the user to request an update. This type of workflow greatly speeds up the process of identifying the desired product and moves them forward in the process quickly.

image03

For users desiring a more compact view of the data there is also a grid view that displays the product information in a dense format. The grid view behaves in the same way as the tile view when interacting with the filtering tool. Once a user clicks on a product they are taken to the configuration page to set the product feature options.

image00

Configuration

The configuration page contains a display of the model number associated with the selected product. The display updates automatically to reflect the changes from interacting with the product options.

image02

A user configures the model by scrolling through the products feature list and selecting various options. As options are selected additional features will be added and removed automatically. If a user’s selection becomes invalid at any point that option will appear in red with an icon indicating that a conflict has occurred. The corresponding component in the model number will also turn red. The user can click on this portion of the model number and the list of features will automatically scroll to the problematic feature allowing the user to update their selection. Once a valid model number is configured the user can click on the “next” button to navigate to the review screen before requesting a quote.

image01

The rules that determine the validity of a product are not “hard-wired” into the application. Instead they are entered by the application administrator and executed on the fly. Examples include, “There are no options for TV controls unless the electric option has been selected” or “Choosing a manually positioned bed invalidates the electric footrest option.” These English phrases can be easily into rules and are associated with a product. These rules are then used by the configuration page to guide the user towards generating a product that meets their needs and the suppliers capabilities.

Having selected and configured the product the user arrives at the summary page. The details of the product and the user’s customizations are displayed and they are presented with a simple contact form from which they can request a quote.

Conclusion

Once the user submits a quote the application demo reaches its end. But the usage for this platform could be extended in a number of directions. Modl Buildr could be extended to include an additional step for browsing and manipulating the components that go into manufacturing the final products at another level of detail. The application could be expanded to include sales information and trends. Additional features common to other marketplace applications could be added such as shopping carts, order history, billing information, etc… On the administrative side of the software additional screens could be added for the management of products, orders and other common administrative tasks.

Modl Buildr is a small example of the kind of business-centric software that we create every day. If you are interested in how it works we would love to show you more.

More about Modl Buildr:

 

 

Angular 2 Component Router

This month at the St. Louis Angular Lunch I spoke about Angular’s upcoming “component router”. Originally created for Angular 2 and to-be-back-ported to Angular 1.x, the component router serves as a replacement to the current routing options. While still too early to use in production it looks like a promising alternative. In this talk (video below) I outline where the component router stands and a brief example of how it can be used.

 

Transcript

Here is the talk, transcribed to text. This is a lightly edited, draft transcription, so any errors are probably from that process.

Continue reading Angular 2 Component Router

Localization in AngularJS

At the St. Louis Angular Lunch this month (which we sponsor), Mark Volkmann of OCI gave a talk on localization. In his talk he addresses how to setup AngularJS services and filters to determine which rules to apply for i18n and l10n runtime support. AngularJS includes good i18n support, probably because of extensive use inside Google across their global operations.

Video below:

 

Transcript

Here is the talk, transcribed to text. This is a lightly edited, draft transcription, so any errors are probably from that process.

Continue reading Localization in AngularJS

Using GulpJS

I recently gave a talk at the St. Louis Angular Lunch on GulpJS. While not directly related to Angular, GulpJS is a great utility to have while working on frontend development, and it very popular among Angular developers. This talk explains the basic usage of Gulp and provides some minor comparisons to its largest competitor, Grunt.

Here is a video of the talk, from our YouTube channel.

Transcript

Here is the talk, transcribed to text. This is a lightly edited, draft transcription, so any errors are probably from that process.

Continue reading Using GulpJS

Advanced Directives in AngularJS

AngularJS directives have a wide array of uses, some more difficult to implement than others. Understanding how the link function, directive controllers, scopes, required, restrict all interact and how to implement them can be daunting. Bill Odom and Matt Follett recently gave a talk at the St. Louis Angular Lunch explaining in detail about advanced directives they have written.

Transcript

We have transcribed the talk about to text, provided below. This is a rough, first-draft transcription, so any errors are probably from that process.

Continue reading Advanced Directives in AngularJS

Custom Directives in AngularJS

Here is another talk from the St. Louis Angular Lunch we sponsor / operate here at the Oasis Digital. In this talk, Mark Volkmann of OCI explains how to use AngularJS to extend HTML to include custom, modular components.

The ability to define custom directives is one of the most powerful features of AngularJS, and as always Mark does a great job summarizing it in a short lunch talk.

Enjoy the video, embedded below.

Transcript

We have transcribed the talk about to text, provided below. This is a rough, first-draft transcription, so any errors are probably from that process.

Continue reading Custom Directives in AngularJS

Learning AngularJS

At a recent St. Louis Angular Lunch Brian Gilstrap talked about his experience with learning AngularJS. In his talk Brian outlined a classic scenario that we have seen in many of our Angular Boot Camp classes. A polyglot developer with years of experience finds themselves in need of, or with a desire to learn, Angular. What is the best way to get started and dig deep into Angular? See the video of Brian’s talk below.