At my current employer, for certain pages, we started building using Angular 2 just before it hit beta to deliver rich, client-side functionality. I prepared a presentation describing our approach to Angular 2 in general here. Having been sold on the benefits of the Flux pattern and the Redux implementation in particular, we were determined to apply Redux to our Angular 2 application code. There didn’t seem to be much out there in the way of tutorials or guides on how to implement Flux in Angular 2 (though there are plenty of materials for Angular 1). This article aims to explore one method of implementing Flux in Angular 2 with Redux.

From a high level, the architecture discussed in this article looks like this:

If you are not familiar with Redux or Flux, I recommend starting with the egghead.io series “Getting Started with Redux” and the PluralSight series, “Building Applications with React and Flux”.

In summary, each Angular 2 application (a single page) has one “App” component. This App component gets the initial state of the app and subscribes to updates from the singleton Redux store. Data then flows from the App component to its children, which pass needed data to their descendants. Child components subscribe to Angular 2 events (click, change, ng-model) and trigger dispatch of actions to the Redux store. The Redux store prompts the reducer(s) to calculate the next state of the application, and then updates the App component and the cycle continues.

There are a few key principles here, in line with the intent of Flux/Redux.

As much logic as possible is moved out of UI components and into the reducer(s). Reducers are functionally pure and synchronous in nature.

This ensures testability, enforces simplicity and enables things like time-travel debugging or replaying a client session and makes for easy “undo” functionality. It’s much easier to tell what’s going on with a UI component (and often easier to make reusable) when there is no application logic or internal state. It’s also much easier to test and reason about the reducers.

Also it may be noted that in this implementation, action creators have been omitted.

Handling Validation

In Angular 2 we learned that validation embedded in our HTML templates was not ideal, so “model based” validation was introduced, which enabled unit testing of form validation logic outside of the DOM. As I see it, there are still some disadvantages to the model based validation provided in Angular 2, which at this time is also a bit sparse in features.

The first disadvantage is that validation logic is still embedded/coupled within UI component code as opposed to having UI components that only reflect the validation state. The second disadvantage is that there is a tight coupling between FormBuilder “Controls” and fields, via a magic string (ie: ngControl=“FooField”). Finally, I also don’t like that due to Controls and FormBuilder being parts of Angular 2, I need the Angular 2 framework in order to share or test my validation logic (ie: if I implement a “credit card validator”), which is a bit of a bummer because Redux is framework agnostic.

Enter “Form Validators”

This is a basic, high level recipe for implementing validation that keeps the logic out of your UI components. Start with a series of shared “validator” functions. These are pure JavaScript functions with names like “maxlength” and “email”.

A “required” validator function.

“Form Validators” are initialized with a series of fields, each with its own collection of validation rules (made up of the validator functions). Form Validators are stateless and compute a new “ValidationContext” each time they are invoked, accepting a model or a specific field value.

A simple Form Validator with a single field.

The “ValidationContext” becomes part of the state, and so UI components know whether to project validity based on something like state.UserProfile.ValidationContext.isValid.

Form Validators can also implement a formValidator.clean() method, which returns a “valid” version of the ValidationContext, intended to be used on initialization or when a form is reset (in other words, before it is “dirty” or before the user has “touched” any of the fields).

Handling Asynchronous Action Flow

At times there exists the need to ensure that certain actions occur in a certain order, or dependent on the success or failure of a previous action. For example, if a User ID field is updated, I can perform synchronous validation on it immediately like “max length” or “required”, but I also want to kick off an asynchronous request to the server to check if that ID isn’t already taken. The Redux documentation discusses how to handle async actions and async action flow in section 3 and discusses how Redux middleware solves this problem.

In this particular example, instead of adding middleware like redux-thunk or redux-promise, there exists a singleton “Manager” (which might also be called an “orchestrator”). For simple cases, we let UI components dispatch directly, but for complex cases the UI components ask the Manager to handle dispatching any actions that result from an event.

The Manager does not calculate the state of the application, and has as little logic as necessary to orchestrate the flow of actions. In the example of the User ID field, the “User Profile” component may notify the Manager that the User ID field has been updated. The Manager dispatches an action that causes the form to update and validate, both of which are handled by the reducer. The Manager also kicks off an AJAX request to the server to ask if the User ID is valid and subscribes to a successful response from the server. Once the server response is received, the Manager dispatches a new action which might be named something like “USER_ID_VALIDITY_RECEIVED”. The reducer then checks to see if any User IDs (most likely just the one) in the state of the application are invalid from this new information. This way, validation logic and calculating whether the form is valid is kept out of the Manager and remains the responsibility of the reducer.

The Manager also is not concerned with how AJAX requests are made, but rather asks a lightweight pass-through “Service”, such as a “UserIdCheckerService” that makes the AJAX call and returns a promise.

Cross-App Communication

Although this should not be a common practice, at times there exists a need to have two Angular 2 applications on the same page at the same time. Using this example, communication between them is simple, though hopefully you can avoid this practice altogether whenever possible.

If “App One” wishes to trigger a change or event with “App Two”, it need only have access to App Two’s singleton store and dispatch an action.

To keep things simple and easy, one approach to this is simply making the Redux singleton store a window-level variable with a unique name. If you choose to “import” the store from the other application, you should be certain that your compile process (ie: webpack / jspm) doesn’t give you a completely separate copy of the reducer file, which is likely to give you a different instance in memory. I personally find it handy to expose the Redux store to the window which makes it easily accessible from the console for debugging purposes.

In Summary

I and my colleagues continue to experiment with ways to build on and improve our UI architecture. Nevertheless, I’ve found this approach to be both flexible and relatively easy to maintain. In addition to the stated benefits of Flux/Redux, it also keeps application code highly portable. It would be relatively trivial to switch between other frameworks and keep the same business, validation and services logic.