[Post using Angular2 RC4 + @angular/forms module]

Imagine you want to validate a field using another one with the last angular2 form API. In fact you can’t do that directly in a custom field validator but a really simple way to do it exists and this is what i’m gonna show you there.

➜ The form API

Here are the three differents form elements in Angular2:

  • FormControl: A form entry, could be an input[type=text], textarea, select,..
  • FormGroup: Composed of FormControl and FormGroup/FormArray, each item contained got a string identifier. It could be an entire form or a subform.
  • FormArray: Same as FormGroup but acts like an array (you can push, insert, or removeAt), useful for dynamic length (add a list of urls for example).

Each of these elements inherits from AbstractControl. And if you go check this abstract class, you will see it means all got these following properties:

  • pristine / dirty / touched / untouched / pending / valid > Boolean showing state of the control/group/array.
  • valueChanges / statusChanges > Observables to catch whatever changed in the control/group/array.
  • errors > A key/value object with all errors (if errors it is) on the control/group/array.
  • value > Typed any because it is an object litteral for FormGroup, an array for FormArray and anything for a FormControl (string for a input[type=text] for example).
  • validator / asyncValidator > list of sync and async validators.

This module is awesome and allow to code complex things properly. Like you can see, FormGroup have a validator property and this is the one to use to solve our problem.

➜ Slice our form

So create a subform containing only fields needed for your validation and add your custom validator as parameter like this:

// Note: formBuilder.group(...) === new FormGroup(...)
this.rangeForm = formBuilder.group({
  'range':          new FormControl('', Validators.required),
  'specificValue':  new FormControl(null)
}, {
  validator: this.specificValueInsideRange.bind(this)
});

Then create your main form including it:

this.mainForm = formBuilder.group({
  'rangeForm': this.rangeForm,
  // others controls
});

Th HTML markup looks like this:

<form [formGroup]="mainForm" (ngSubmit)="send()" novalidate>
	<div [formGroup]="rangeForm">
		<select formControlName="range">
			<option value="">Select a range</option> 
			<option *ngFor="let r of listRanges" [value]="r.id"></option> 
		</select>
		<input formControlName="specificValue" type="number"/>
	</div>
	<!-- Others controls -->
	<button type="submit" [disabled]="!mainForm.valid">SEND</button>
</form>

And finally, here is our custom validator accessing our 2 FormControl value to check if it fit well:

specificValueInsideRange(group: ControlGroup) {
  const range = this.listRanges.find(r => r.id === Number(group.value.range));
  if(range && (group.value.specificValue < range.min || group.value.specificValue > range.max)) {
    return {
      outsideRange: true
    };
  }
}

Here it is, we got our custom fields validation, the specificValueInsideRange validator function will be called anytime something change inside rangeForm.

➜ Demo

You can check this example in the following plunker:

Admittedly for most developers state management is one of the toughest parts of growing an application safely while staying maintainable over time.

Angular1 doesn’t promote a specific way to take care of this except for adding it inside services (instead of scopes/components) but the change detection system makes options like immutability or observables useless.

Angular2 corrects this and allows us many more possibilities. We can now use the full power of libraries like Flux/Redux, Immutable.js, RxJS,..
Lately, I’ve documented a lot about these Javascript libraries in addition to concepts like functional programming & reactive programming. All of this is really vast and exciting!

➜ Starting point

I will start a job in a few months and will be in charge of a new big webapp. There will be a lot of data to take care of and interact with. Angular is the de facto choice because of its historical front framework company position and as a full stack solution. Angular2 will be used and I’m really happy about this but I want to avoid some of the pains that I’ve experienced on previous applications.

➜ Centralize data & unidirectional data flow

Difficulties can appear when data is dispatched in lots of places and managed from everywhere. The result is an application that is hard to update, debug and test. This is why it is fundamental that application state stays at one place only inside the entire application and follows a unidirectional data flow.

This post reflects my exploration about different current and emerging ways of solving this.


Let's talk about immutability

Working with immutable objects means that you always create a new object when you want to modify it.

You can’t mutate an object. If you want to add an item to an array, you create a new array containing all of the existing items plus the new one. If you want to modify a boolean property on an object, you create a new object with all same properties as the one that you want to change and with the modified boolean.

Immutability leads to better performances for tracking changes: just look at the object references (objA !== objB), if it didn’t change, there’s nothing to do, no need to check deeper.

↪ Introduction to immutability in Javascript

➜ How to use it

We can do this with vanilla JS but there is no default type for doing this (contrary to Haskell, Scala,..) so we have to be careful and always keep this in mind. Otherwise there are libraries made for this, and ↪Immutable.js is the most popular implementation of immutable data structures.

↪ Using immutable data structures in Javacript with Immutable.js



➜ Angular2 and Immutability

Thanks to ↪ChangeDetectionStrategy, we can now tell Angular2 to rerender a component only when its inputs object changes, so it plays well with immutable objects!

↪ Change detection in Angular2
↪ Immutability and encapsulation in Angular2
↪ Build a minesweeper with Angular2 and Immutable.js: ↪Part1 & ↪part2
↪ Angular2 with Immutable.js


Three main solutions

I’ve identified three main ways to manage data properly: Use a Flux architecture like Redux, do it ‘manually’ with RxJS observables, or use the young Ngrx.

All of these options play well with immutables, Redux makes it a duty and it’s as you want for others.


Solutions 1 : Let Redux take care of it

↪Redux is a tiny (2kb) state container library implementing an unidirectional data flow with predictability as the watchword. It has three main principles:

  • The state of the whole application is stored in an object tree (single source of truth).
  • Application state is read only (unidirectional data flow).
  • State changes are written as pure functions (why it works with immutable data).

➜ Redux history

Facebook was the first to introduce Flux and then a lot of other implementations appeared: Reflux, Flummox, Fluxible, Fluxxor, Fynx, Fluxify,.. and Redux won the battle.



A big advantage for using Redux is its amazing DevTools with features like time travel.

➜ Learn Redux smoothly

Go in this order:
↪ A nice Flux cartoon explanations without code, concepts only.
↪ Another cartoon about Redux and its differences from Flux.
↪ Best explanations about Redux from its author, Dan ABRAMOV, in 30 small live coding videos ★
↪ RDFM: Read the official documentation.
↪ Understand what is a Redux Middleware.

And then you can go deeper about asynchronous actions, using Middlewares like:
↪ Redux-thunk
↪ Redux-saga

➜ Redux with Angular

Redux is historically combined with React to render web application but you can use it to manage your data with any other front-end framework like Angular 1&2, Ember, Aurelia,..

First, you need to understand how this architecture could be implemented with angular, ↪this post shows you how switch a Todo application using Angular1 to using Redux-like state management.

Then read these:
↪ Managing state with Redux and Angular.
↪ Build an Angular2 application with Reflux and Immutable.js
↪ How to use Redux in Angular2 Applications.

You have angular bindings libraries similar to react-redux for React (totally optional):
↪ Ng-redux for Angular 1.X
↪ Ng2-redux for Angular 2.X


Solution 2 : Do it manually with RxJS

If you impose on yourself (and your team) strict rules about state management in your application, you can build well maintainable applications. Here are few tools and resources to help you achieve this. Immutable data are not mandatory.

➜ A unique facade

Build your own convention to manage your data properly using a single façade following this article:
↪ Anatomy of a large angular application

This facade abstracts the data management part from components. It reduces dependencies and allows more flexibility in developing the system. Under the hood, you can call multiple services.

➜ Observable streams

Observables are data streams, a very powerful mix between arrays and promises. We can subscribe and unsubscribe to them. The observer Pattern helps us to keep the unidirectional data flow.

You can use observables in Javascript by installing RxJS, it will be part of ES7/ES2016 (currently TC39 part 1).

↪ FunFunFunction episode about streams
↪ Introduction to RxJS

➜ RxJS with Angular2

We can use RxJS observables inside Angular2 services. It allows your components to subscribe and be informed when data changes.

Angular2 is really linked to RxJS (which is in version 5 beta 1) and promotes it. You can use RxJS observables to build stores-like inside a Angular2 application.

↪ Angular2 Observable data services.
↪ Managing state in Angular 2 using RxJs.
↪ Reactive Data Flow in Angular 2
↪ Managing state in Angular2 using RxJS in a Redux-like way ★


Solution 3 : NgRx Everything is an Observable

↪ Ngrx: RxJS powered state management for Angular2 apps, inspired by Redux.

This library goes a step further and makes every state part an observable, then components can subscribe to them. This approach is really promising and seems to be the next ‘big thing’ in the angular community as the following tweet from @egghead mentions.

Use this library with the included async pipe and ChangeDetectionStrategy.OnPush and you will have a massive performance boost in large ng2 applications.

↪ Read more explanations from the author @robwormald.
↪ Build a Better Angular 2 Application with Redux and ngrx. ★


Conclusion

I haven’t made my choice actually. Redux is really attractive but things are moving fast (as we are used to with javascript!).

Keep an eye on NG-NL, the next big Angular conference in Amsterdam, there will be many talks about these subjects:

Hope this helps some of you. Tells me your thoughts about it and if I made mistakes.