Angular: When using ngrx is an overkill

State management is one of the most important aspects of any app that has UI. redux is a popular library for maintaining state in the React community. ngrx is a redux-inspired library for maintaining state in Angular apps. But if redux is a rather simple library, ngrx is not so much, and its workflow is illustrated in the diagram below.

In this blog, I’m not going to explain how ngrx works in detail. You can learn it by going through one of the online tutorials, attending our online workshop, or reading the last chapter of our Angular book. I’ll just show you a video comparing two small apps that have the same functionality, but one of them implements state management by using RxJS BehaviorSubject in an Angular service, while the other uses ngrx.

The app that doesn’t use ngrx has about 80 lines of code and its production build size is 268KB. The ngrx version of this app has about 160 lines of code and its production build weighs 315KB.

In short, I do not recommend using ngrx for state management of small and mid-size apps.

Typically, you use a library to write less code, but the current version of ngrx requires you to write more code.

Advertisements

What’s coming in Angular 6+

The release of Angular 6 is around the corner, and in this blog, I’d like to highlight some of its new features announced by the Angular Team.

I started working with Angular when it was in its early alpha versions. Every new Alpha, Beta, and Release Candidate was full of breaking changes, and the announcement of each new release was giving me goosebumps.

After the official release of Angular 2 in September of 2016, the Angular Team started to care about us the developers from the trenches; new major releases come twice a year and switching from one release to another is an easy task. Angular is gaining popularity in the enterprise world leaps and bounds, and the Angular team continues improving this framework. To the best of my knowledge, these are the new and upcoming features:

Angular Elements. While Angular is a great choice for developing Single-Page Applications, creating a widget that can be added to any existing web page was not a simple task. The package called Angular Elements will allow you to create an Angular component and publish it as a Web Component, which can be used in any HTML page. Say there is an existing non-SPA app built using JavaScript and jQuery. The developers of this app will be able to use Web Components built in Angular in the pages of such an app. In our opinion, this killer feature will open many enterprise doors to the Angular framework.

Ivy renderer. This is a code name of a new renderer that will make the size of the app smaller and the compilation faster. The size of the Hello World app is only 3KB gzipped. The Angular Team promises that switching to Ivy rendered will be smooth, and I’ll take my hat off if they’ll be able to make it a non-breaking change.

Bazel and Closure Compiler. Bazel is a build system used for nearly all software built at Google, including their 300+ apps written in Angular. Closure Compiler is the bundling optimizer used to create JavaScript artifacts for nearly all Google web applications. The Closure Compiler consistently generates smaller bundles and does a better job in dead code elimination compared to Webpack and Rollup bundlers. In the upcoming releases of the Angular framework, you’ll be able to use this toolchain for building your apps as well.

Component Dev Kit (CDK). This package is already used by the Angular Material library, which offers 30+ UI components. What if you don’t want to use Angular Material but want to build your own library of UI components and control page layouts? CDK allows you to do this. It also supports Responsive Web Design layouts eliminating the need for using libraries like Flex Layout or learning CSS Grid. CDK was released in December of 2017, but the Angular Team keeps improving it.

Schematics and ng update. Angular CLI generates Angular artifacts using the technology called Schematics. If you decide to create your own templates and have Angular CLI use it, Schematics will help you with this. Staring from Angular CLI 1.7, you can use the ng update that automatically updates your project dependencies and makes automated version fixes. With Schematics, you’ll be able to create your own code transformations like ng update.

I’ looking forward to these features that will make Angular even better, but what else could be improved, that’s not on the roadmap yet? Angular CLI is a great tool, but it has lots and lots of dependencies on the third-party software. Over the last year, several times I’ve experienced broken builds in the latest releases of Angular CLI that were caused by some erroneous release of a third-party package.

The Angular CLI folks are pretty good at releasing patches when errors are reported, but the process of regression testing should be improved, so these errors wouldn’t even sneak in. I stopped using the ^ sign for the version of @angular/clip in package.json. I use the exact version that works for me unless I have a compelling reason for upgrade.

Go Angular!

From a convenience store to Redux architecture

Imagine that you’re a proud owner of a convenience store that sells various products. Remember how did you start? You rented an empty place (i.e. the store was in its initial state). Then you purchased shelves and ordered products. After that, multiple vendors started delivering products. You hired employees that arranged these products on the shelves in a certain order changing the state of the store. Then you put out the Grand Opening sign and lots of colorful balloons.

Customers started visiting your store and buy products. When the store is open, some products lay on the shelves, and some products are in the shopping carts of customers. Some customers are waiting in lines at the cash registers, and there are store employees there. In other words, we can say that at any given moment, your store has the current state.

If a customer takes an action, e.g. buys (or returns) five bottles of water, the cashier scans the barcode, and this reduces (or increases) the number of bottles in the inventory, i.e. updates the state. If a vendor delivers new products, your clerk updates the inventory (i.e. state) accordingly.

Your web app can also maintain a store that holds the state of your app. Similarly to a real store, at any given time your app’s store has a current state. Some data collections have specific data retrieved from the server and, possibly modified by the user. Some radio buttons are checked, the user selected some products and navigated to some routes represented by a specific URL.

If the user interacts with the UI, or the server sends new data, these actions should ask the store object to update the state. But to keep track of the state changes, the current state object is never updated, but a new instance of the state object is created.

What’s Redux?

Redux an open-source JavaScript library that takes care of managing application state. Initially, the React.js developers made Redux popular, but since it’s just a library for maintaining an app state, it can be used in any JavaScript app. Redux is based on the following three principles:

  • Single source of truth. There is a single store where your app contains the state that can be represented by an object tree.
  • State is read-only. When an action is emitted, the reducer function won’t update, but clone the current state and update it based on the action.
  • State changes are made with pure functions. You write the reducer function(s) that takes an action and the current state object, and returns a new state.

In Redux, the data flow is unidirectional:

1. The app component dispatches the action on the store
2. The reducer (a pure function) takes the current state object and then clones, updates, and returns it.
3. The app component subscribes to the store, receives the new state object and updates the UI accordingly.

This is how the Redux unidirectional data flow can be presented if used in a web app.

An action is a JavaScript object that has a property `type` describing what happened in your app, e.g. the user wants to buy the IBM stock. Besides the property type, and action object can optionally have another property with the payload of data that should somehow change the app state, for example:

{
  type: 'BUY_STOCK',
  stock: { symbol: 'IBM', quantity: 100} 
}

This object only describes the action and provides the payload, but it doesn’t know how the state should be changed. Who does? The reducer.

A reducer is a pure function that specifies how the state should be changed. The reducer never changes the current state but creates a new (and updated) version of it. In other words, the state object is immutable. The reducer creates a copy of the state object and returns a new reference to it. If you use the Angular framework, it’s a binding change event, and all interested parties will immediately know that the state has changed without requiring expensive value checking in the entire state tree.

A reducer function has the following signature:

function (previousState, action): State {...} 

Should the reducer function implement the app functionality like placing an order, which requires work with external services? Of course not, because reducers are meant for updating and returning the app state, e.g. the stock to buy is “IBM”. Besides, implementing the app logic would require interaction with the environment external to the reducer, i.e. it would cause side effects, and pure functions can’t have side effects.

The reducer can implement minimal app logic related to the state change. For example, the user decides to cancel the order, which requires a reset of certain fields on the state object. The main app logic remains in your application code (e.g. in services) unless a concrete implementation of the Redux-inspired library offers a special place meant for the code with side effects.

Why storing an app state in a single place is important

Recently, I was working on a project for a car manufacturer. This was a web app that allowed a prospective buyer to configure a car by selecting from tons of packages and options (e.g. model, interior and exterior colors, length of the chassis et al.) This app was developed for many years, the software modules were written using JavaScript, jQuery, Angular, React, Handlebars as well as the HTML templating engine Thymeleaf on the server. Nothing unusual, right?

From the user’s perspective, this was one workflow that consisted of several steps resulting in configuring and pricing the car based on selected options. But internally, this process was switching from one module to another, and each module would need to know what was selected in the previous step to show the available options.

In other words, each module would need to know the current state of the app. Depending on the software used in any particular module, the current user’s selections were stored using one of the following places:

* URL parameters
* HTML data attributes
* Browser’s local and session storages
* Angular services
* React store

New requirements were coming in, new JIRA tickets were created and assigned, and we’d start implementing them. Time and time again, implementing a seemingly simple new requirement would turn into a very time consuming and expensive task. Good luck in explaining to your manager why showing the price in page B would take a half day if this price was already known in page A. The thing is that the state object used in page B didn’t expect to have the price property, and if in page A the price was a part of the URL, page B was expecting to get the current state from the local storage.

Re-writing an app from scratch was not an option, but it would be so much easier if the app state was implemented in a uniform way and stored in a single place!

While the Redux is widely used in the web apps developed with the React framework, for Angular apps you can use the ngrx library that also implements the unidirectional data flow and can be used for implementing the app state management.
While the Redux API is simple, the ngrx is not. The reason is that ngrx is a combination of Redux and RxJS libraries that offers more than just keeping track of the round-trips between the UI and the app state. With ngrx, you can inject the code that communicates with external services into the data flow. Also, you can monitor the state of the router.

I’m planning to write a series of blogs to present ngrx, so it wouldn’t require a Ph.D. to master it. If you can’t wait, consider enrolling in our online workshop on using ngrx for state management in Angular apps. You’ll need to invest 7 hours of your time, but this may save you a lot more time in the long run.

Always commit your yarn.lock file into the source code repo

I use the Yarn package manager for all my Angular projects. Besides being faster than npm, yarn creates a file yarn.lock that stores the exact versions of installed dependencies. Last year, I wrote about a specific use case that caused a breaking change during one of my Angular workshops.

Yesterday, I had to create a new project using Angular CLI. The project was successfully generated and started with ng serve. Then, I wanted to create a production build with ng build –prod, but the build failed with the error Cannot find module ‘uglifyjs-webpack-plugin’. I know that Webpack uses UglifyJS for optimizing sizes of the bundles, which is great, but my production build fails, which is not great at all.

The generated package.json file located in the root of my project has no direct dependency on uglifyjs-webpack-plugin, but there is one deep inside node_modules in one of the thousand (literally) dependencies used in Angular CLI projects.

To make the long story short, the WebPack team pushed to npmjs.org the new version 1.1.7 this plugin, which was empty. The Angular team got in touch with them, and an hour later, the fixed version 1.1.8 of the Webpack UglifyJS plugin was pushed to the npm central repo.

My project already had the yarn.lock file with an erroneous version of this plugin. I deleted it and ran yarn install again. This time it picked up the fixed 1.1.8 version and the ng build –prod command started working again. Thank you, the Webpack team for the quick turnaround! Happy end!

This was a specific issue with the newly generated project created at the “wrong time”. But imagine a team working on a project for some time, and their github repo didn’t include the yarn.lock file that stored the reference to the working version 1.1.6 of that plugin, and Mary (the QA engineer) decided to make a production build. A minute later, the entire team got an email titled “Who broke the build?”

The bottom line: Push the yarn.lock of the project that works and builds correctly into your version control system to ensure that the entire team has a reproducible and working build. This way, your project won’t depend on someone from across the globe submitting erroneous packages to the npmjs.org.

P.S. While npm 5 also creates the file package-lock.json with the registry of all installed dependencies, it doesn’t guarantee that each npm install will install exact versions of the packages listed there.

Creating an Angular app that weighs only 55KB

With Angular CLI, generating a new app and installing dependencies takes 30 seconds. Creating a production build takes another 15 seconds. And the best part is that the size of the deployed app can be as small as 55KB gzipped. Let’s do it.

1. Generate a minimalistic app with the inline template and styles with the following Angular CLI command:

ng new hello-world --minimal

2. If you’re not creating custom decorators and perform the AoT build, you don’t need to import the reflect polyfill, so comment out the following line in the generated file polyfills.ts:

import 'core-js/es7/reflect';

3. In the generated file app.component.ts remove most of the HTML markup from the component template so it looks as follows:

template: `Welcome to {{title}}!`

4. Make a production build (it performs the AoT compilation by default):

ng build -prod

5. The npm package serve is a small web server that can gzip static files on the fly. Install it globally:

npm install serve -g

6. Change to the dist directory where the application bundles were generated and serve the app (it’ll run on port 5000 by default):

serve .

7. Enjoy the small size!

To see it in action, watch this 5-minute video.

Update. Hello World in the upcoming rendering engine Ivy is only 3.3KB!
See https://ng-ivy-demo.firebaseapp.com/

Wrapping a RxJS observable stream into an Angular service

Angular’s dependency injection mechanism allows us to cleanly separate business logic (services) from UI (components). What if our app generates a stream of values and we want to implement it as an injectable service? In this blog, I’ll create an injectable service that emits a stream of values and a UI component subscribes to this stream displaying its values real time.

In one of my RxJS blogs  I showed you how to use the method Observable.create() providing an observer as an argument. Let’s create a service with a method that will take an observer as an argument and will emit the current time every second.

import {Observable} from 'rxjs/Observable';

export class ObservableService{

  createObservableService(): Observable<Date>{  // 1

      return new Observable(  // 2
          observer => {   // 3
              setInterval(() =>
                  observer.next(new Date())  // 4
              , 1000);
          }
      );
  }
}

1. Return an observable stream of dates
2. Create an observable
2. Provide an observer
4. Emit the new date every second

In this service, we create an instance of the RxJS Observable object, assuming that the subscriber will provide an Observer that knows what to do with the emitted data. Whenever the observable invokes the method next(new Date()) on the observer, the subscriber will receive the current date and time. Our data stream never throws an error and never completes.

We’ll inject the ObservableService into the AppComponent, which invokes the method createObservableService() and subscribes to its stream of values, creating an observer that knows what to do with the data. The observer just assigns the received time to the variable currentTime which renders the time on UI.

import 'rxjs/add/operator/map';
import {Component} from "@angular/core";
import {ObservableService} from "./observable.service";

@Component({
  selector: 'app-root',
  providers: [ ObservableService ],
  template: `<h1>Custom observable service</h1>
       Current time: {{currentTime | date: 'mediumTime'}}  // 1
  `})
export class AppComponent {

  currentTime: Date;

  constructor(private observableService: ObservableService) { // 2

    this.observableService.createObservableService()  // 3
      .subscribe( data => this.currentTime = data );  // 4
  }
}

1. Display the time using the date pipe
2. Inject the service that wraps the observable
3. Create the observable and start emitting dates
4. Subscribe to the stream of dates

This app doesn’t use any servers, and you can see it in action in the great Stackblitz online IDE  here.

In the browser’s window, the current time will be updated every second. You use DatePipe here with the format ‘mediumTime’, which displays only hours, minutes, and seconds (all date formats are described in the DatePipe documentation).

This simple example demonstrates a basic technique for wrapping any application logic in an observable stream and subscribing to it. In this case, we use setInterval(), but you could replace it with any application-specific code that generates one or more values and sends them as a stream.

Don’t forget about error handling and completing the stream if need be. The following code snippet shows a sample observable that sends one element to the observer, may throw an error and tells the observer that the streaming is complete:

return new Observable(
    observer => {
      try {
        observer.next('Hello from observable');

        //throw ("Got an error");

      } catch(err) {
         observer.error(err);
      } finally{
         observer.complete();
      }
    }
);

If you uncomment the line with a throw, observer.error() is invoked, which results in the invocation of the error handler on the subscriber if there is one.

The data producer for our observable stream was generating date/time but it could be any app code that generates some useful values, e.g. a WebSocket server generating stock quotes, auction bids, actions of online game players, etc. During my workshops, I show a sample online auction app that has a Node server emulating users’ bids on products. That server uses a WebSocket connection to push new bids for products to all users that are interested in receiving them.