Use React tools for better Angular apps

March 06, 2018 0 Comments

Use React tools for better Angular apps

 

 

Are you ready for some solid fight ? Angular vs React ha ?!
Fight leads to the dark side ( to Twitter /clone/ flame wars )

After seeing that cover image of this article, your first impression must be like: OMG! another clickbait article about why X sucks and Y is the silver bullet. Well, I have to disappoint you. This article is about the complete opposite :)

Instead of wasting our time by participating on twitter rants and so on, we will leverage the best that other ecosystems ( React ) have to offer and thanks to that, we will get better/faster/stronger tools for achieving our daily development tasks with Angular.

Question is where to start? or how to achieve that goal, or even why should I bother with other ecosystems and tools. Great questions indeed! Let’s get some answers, shall we?

Angular is a great piece of technology on it’s own indeed, so why should you look somewhere else?

Well, there is a very tiny(I meant huge) problem.

Everything Angular, is tightly coupled to Angular and its ecosystem…

I don’t know about you, but I really hate to have boundaries for achieving my goals…

Angular is a “huge beast”, but let’s face it, it’s just unreal to cover all the corners of day to day development with a top notch solution, although huge efforts from Angular team.

Kudos to Angular team and OSS contributors for their efforts, keep rocking !!!

By looking outside Angular “ecosystem isolation”, we can learn a lot from others and leverage that for our own benefit and better Angular apps with excellent DX along it’s way, which in the end, saves our clients money and time.

To answer the question “How to leverage other ecosystems” we need to to introduce and take a look at 3 categories that cover an integral part of software development:

  • Tools
  • Architecture
  • Libraries
As a baseline we will use “Industry standard” for booting up new Angular apps — Angular CLI
Angular CLI, industry standard for booting up Angular apps

We can boot new Angular app in matter of seconds, with all “recommended/required tooling and setup”, by executing simple command:

npx @angular/cli new my-app
NOTE: never use a -g flag when installing packages! Global things are bad for your machine/life and thanks to npx, you don't need them anyway :)

Let’s take a look what CLI gives us by default from perspective of our 3 defined categories:

  • Libraries: all @angular/* packages and RxJs
  • Architecture: Component driven architecture with Service layer, but no restrictions on this front ( use what you want )
  • Tools:

→ we’ve got webpack as a module bundler

→ Protractor/Selenium for e2e testing

→ Jasmine with Karma for unit testing

→ and last but not least, the best thing that ever happened to JavaScript — Typescript with solid static analysis extensions for adhering to proper style-guides and — TSLint and Codelyzer.

Angular CLI defaults from our 3 categories perspective

So far so good right ?!

Tooling is indeed crucial for our productivity. Let’s see what can be done on this front.

Let’s start with package manager…

Most of you are probably using npm, which was super annoying to use and slow until version 5. While npm is constantly improving, you don’t have to wait and instead you can switch to a better tool -> yarn

I’m not going to enumerate all the benefits, just a few time savers that you can leverage on daily basis.

How would you execute locally installed TypeScript from CLI with npm, or execute custom npm-script or pass additional arguments to your custom npm-script ? You’re smart and sure know how to do those, but with yarn it’s much easier. Comparison is on following image:

npm vs yarn ( from task runner perspective )

How are you bumping you dependencies ? Manually by hand ? Seriously ? We can do better with yarn:

yarn upgrade-interactive
Just to not be biased against npm, you can achieve the same via 3rd party package(npm-check) and npx via npx npm-check -u just sayin’...

CLI comes with pre-configured TSLint supported by Codelyzer, which helps us lint our code and adhere to strictly set style guides within our project. TSLint takes also care of consistent spaces at various places ( functions, module imports, etc… ), empty lines, single/double quotes, semicolons/no-semicolons… But if you think that this is the right way to do formatting, you are fundamentally wrong. Anyway, Lint is for linting not for formatting !

So how can we efficiently/consistently format our codebase instead of using wrong tool for that job — yes I’m talking about you Mr. linter(TSlint) ?

Ladies and gentleman, please welcome, The humble Prettier!

is an opinionated formatter created by Facebook and OSS community. You can read more about it on prettier website.

We can add it to our CLI very easily:

Install:

yarn add -D prettier

and define a new npm script:

{
"scripts": {
"format": "prettier {src/e2e}/*/ --write"
}
}

By executing yarn format our whole codebase is formatted in matter of seconds and it's super fast!

That’s it? Not entirely! Because TSlint contains formatting rules, which has nothing to do with linting, we need to turn off these rules… Uff looks like lot of manual work !

Thanks to OSS we can leverage tslint-config-prettier package, which handles everything for us!

Install:

yarn add -D tslint-config-prettier

and extend tslint.json rules with it:

{
"extends": [
"tslint-config-prettier"
],
"rulesDirectory": [
"nodemodules/codelyzer"
],
"rules": {...}
}

There are still issues though. CLI defines lot’s of rules within tslint.json which are overriding anything that extends our config. Again, tslint-config-prettier comes with a handy CLI tool, that detects those rules in conflict, which need to be removed:

By executing local binary:

yarn tslint-config-prettier-check ./tslint.json

We will get output like this:

tslint-config-prettier-check output
I see a great PR opportunity here, to automate removal of those conflicting lint rules ;)

Formatting/Linting Done!

There is room for improvement though! I don’t know about you, but I’m lazy and just the thought of manually executing former command to format/lint my code, makes me ill…

Robots (I mean npm packages) can handle that for us. This can be achieved with following node libraries:

  • lint-staged 🚫💩 — Run linters/formatters on git staged files
  • husky 🐶 Git hooks made easy

Install:

yarn add -D lint-staged husky

Configure lint-staged:

lint-staged config within package.json

And add commit hook via custom npm script, which will be recognised by husky and executed on particular git-hook. In our case, we want to hook into pre-commit git life cycle and execute lint-staged binary which will consume our formerly defined configuration.

npm script, which will be executed by husky, looks like following:

{
"scripts": {
"precommit": "lint-staged"
}
}

Now, every time we will commit to the repo, our staged files will be formatted and linting violations will be fixed by TSLint (if they are auto-fixable), otherwise our commit will fail !

Excellent, you will never ever have to argue with your PR reviewer about semicolons vs no-semicolons, spaces…you name it…👌.

With that said, let’s switch to more serious stuff…

If you’re not testing your codebase I will find you and I will… :D

Angular CLI comes with Karma test runner and Jasmine expectation/spy library. Those tests are run usually against browser.

Karma is indeed an old tool ( levorutionary at one point some years ago ) but let’s be honest, it’s slow, debugging test is cumbersome and it needs extensive configuration. We can look outside our Angular boundaries and we will discover the “ultimate salvation”.

Everyone please welcome, silver bullet to all our testing nightmares, Dr.Jest .

Jest is a complete testing solution, that includes both test runner and assertion/spy library and much more…

It’s super easy to setup, it’s blazingly fast and introduces a brand new type of testing — snapshot testing ( which can be leveraged with everything that is serializable — component snapshots, state snapshots, image snapshots …)

Jest integration with Angular CLI

To integrate Jest with Angular CLI, we need to install

  • jest
  • jest-preset-angular, which handles everything for test environment setup specific to Angular ( zones and stuff ya know ? )
yarn add -D jest jest-preset-angular

Now we need to configure jest, to consume our preset:

// jest.config.js
module.exports = {
preset: 'jest-preset-angular',
setupTestFrameworkScriptFile: '<rootDir>/src/setupJest.ts',
}
For more info see the docs

Now we can add new npm-scripts for testing:

{
"scripts": {
"test": "jest --watch",
"test:ci": "jest --runInBand",
"test:coverage": "jest --coverage"
}
}

I briefly mentioned, that Jest is fast. How fast ?

Unit testing speed comparison Karma vs Jest

Why is it so fast ? Well it runs against Node and JSDOM, tests are executed in parallel and efficiently cached.

Jest comes with snapshot testing, you just need to use toMatchSnapshot matcher within your test expectation on component fixture:

Component snapshot testing

This will save a snapshot footprint of your component in that particular moment(state) - a file physically on disk - and when something changes in the component implementation afterwards, you’ll get following failing test with detailed DIFF about what changed:

Component snapshot diff change

I love this !

Jest comes with advanced watch mode — a CLI like tool with lot of perks, like filtering, running only tests that changed and various other features:

Behold — Jest interactive watch mode:

Beautiful isn’t it ?!

There are moar things that comes with Jest, I will name just few:

  • Powerful mocking features ( ES2015 modules, assets )
  • Code coverage — 0CJS jest --coverage
  • Pluggable ( run Puppeteer with Jest )
  • Huge ecosystem: jest-axe ( a11y ), jest-images-snapshots

End to End testing is equally or even more important than unit testing. Let’s see what we get by default with CLI.

Uh?! What did you just say? SELENIUM ? Did I tell you that every time I hear “SELENIUM” I wanna fight someone… and you don’t want to fight me, trust me 👀😇

Selenium you said ? You monster !
Selenium was indeed useful at some point in our development carer history, but it’s 2018 and there are much better tools out there nowadays.

Please welcome, the cure for your E2E testing sickness, Dr. TestCafe 👨‍⚕️

TestCafe is pure NodeJS, non framework specific, open source tool for all our E2E scenario needs !

It’s 100% reliable, fast, zero config solution, works cross platform, cross browser( even works on custom environments like Windows Subsystem Linux or custom browsers ). Last but not least I forgot to mention that Typescript is 1st class citizen for writing e2e scenarios, so yay for type safety within your E2E tests!

Install:

yarn add -D testcafe testcafe-live

Now we can add new npm-scripts for executin e2e tests:

npm scripts for running e2e with TestCafe

On CI, we wanna run our suite against all browsers and by providing --app flag, we are telling TestCafe to boot UI for us and then execute the scenarios on it, so for those purposes we will execute yarn e2e:ci

For development we will use yarn e2e which runs e2e tests in watch mode in chrome, so after every change our tests are re-run. DX experience at it's best !

Let’s see it in action ( TestCafe watch mode )!

TestCafe demo example

In following e2e scenario, we are testing creation of new pizza, and after it’s created, we delete it to clean up after ourselves.

What might not be visible from the demo is, that when you wanna delete a pizza, browser native confirm dialog is shown, TestCafe handles all of this with ease.

After initial run, we’re adding toppings to the pizza, just to showcase complex UI entities selection within our e2e test.

Whole test scenario is powered by traditional PageObject pattern with custom helpers for our particular needs.

E2E tests run in watch mode — brilliant DX for writing our scenarios

Angular CLI doesn’t come with anything related to developing Components in isolation or for building a Demo project with components showcased in various state. Thanks to React community we can leverage Storybook !

Storybook is powered by React under the hood and generates whole UI catalogue of your component demos/stories. Within Angular scope, it supports much more than just components. You can write stories for Services, Modules etc…

This is how the Storybook UI looks like:

Storybook UI with Angular

Storybook is also capable to build our stories app, so we can deploy it on server, and with that, to provide a documentation for our component consumers. You can see it deployed with various Angular stories examples here

Storybook CLI integration

To add Storybook to Angular CLI, we will leverage storybook CLI:

npx @storybook/cli@alpha getstorybook
While we are enjoying our morning quick shot of espresso, everything is setup for us. Amazing!
add storybook to angular CLI

Then we execute

yarn storybook

and our local storybook app is ready to use!

Storybook Addons

Storybook comes also with various plugins, although framework support varies. You can learn more here

Storybook: write a story

Let’s look very quickly how to write a story for simple button component:

Angular Componet Story

Storybook: Component folder structure

With storybook covered, our final component folder structure should look like this:

Final folder structure
  • implementation
  • unit test with snapshots
  • external styles
  • story

Thanks to other communities and ecosystems, we are able to leverage better exisitng tools for our Angular toolkit.

We replaced Karma/Jasmine with Jest, Protractor/Selenium with TestCafe, TSlint with Prettier for formatting and added husky with lint-staged for adhering to styleguides automatically. As a last step, we added Storybook for developing components in isolation.

Let’s talk briefly about architecture which is the 2nd category we mentioned in the beginning.

We can use similar patterns that are effectively used within other libraries ( in our case with React ), to re-use same design patterns and principles in Angular and beyond.

Those common patterns can be applied in 2 most important parts of every app:

  • Components
  • State management

Following image compares both Angular and React in terms of Component creation patterns, and as we can see those are almost identical.

  • React.Component/@Component ( both React and Angular use Component architecture — app is a tree of components)
  • HOC/@Directive ( HOC just decorates behaviour of an Component in immutable way. Directive can do the same in Angular )
  • render JSX/Inline template ( Always use Inline templates to have everything in one file, logic + rendering )
  • Inline/External CSS
  • Immutable props/@Input with CDS.OnPush ( React triggers re-render only when reference changes, Angular allows the same via OnPush strategy )

Also we don’t have to forget important pattern for building components: Stateful ←→ Stateless component pattern.

How to handle state ?

It can be handled similarly in both React and Angular:

  • Local Component state ( this.state ) / @Component|@Directive instance state => suitable for private state ( mostly UI state)
  • Hierarchical DI in React via Context API / Hierarchical DI in Angular via SOA => suitable for whole app state
  • FLUX architecture powered by Redux => most suitable for app state, universal solution for both Angular and React
So as we can see, Flux Architecture ( via Redux ) is the ultimate solution for both React and Angular in terms of true State separation.

Thanks to this separation, we can write Redux boilerplate once and literally reuse it in both Angular or React. With FLUX architecture, only feature that we’re leveraging in any UI framework is hydration of data to our view ( components ) and rendering.

That’s it !

NOTE: This pattern is probably not suitable for some weekend pet project of yours, I’m talking about enterprise level or similar complexity…

Ok we are almost done. Let’s talk about what libraries can be used in both React and Angular so we can reuse our knowledge.

We know now, that only true separation of state/logic can be achieved via Redux. So yup, you guess it, we can use pure Redux library in both Angular in React, altough in Angular world ngrx/store is the "industry solution" with one important "problem" for our use-case: it's tightly coupled to Angular, which is a no go in terms of our reusable patterns methodology. Also when we're using pure Redux, we can leverage all existing ecosystem around it ( mostly middleware and tooling ).

NOTE: don’t get me wrong ngrx/store is great, if you’re using solely Angular, probably stick to it, again principles/patterns are very similar for both Epics/Effects

So let’s install redux and redux bindings for Angular:

yarn add redux @angular-redux/store

To abstract our state shape away, we also need to use Selectors, and to make them performant we should reach for reselect

yarn add reselect

What about handling side effects? Well again, in Angular world, there is another package from ngrx -> @ngrx/effects, which is unfortunatelly for us, again, coupled to Angular.

Have no fear a have universal solution is here:

Redux-observable — RxJS 5 — based middleware for Redux which uses Epics.

yarn add redux-observable rxjs

It’s similar to Effects with 2 important distinctions:

  • Epics are executed after all reducers have been run ( Actions always run through your reducers before your Epics even receive them )
  • Epic needs always return an Action Stream ( Actions in, Actions out — thanks to that, it prevents us to introduce anti-patterns, that can be observed within ngrx/effects, for instance using tap and similar noop operators, for triggering new side effects within effects )
NOTE: If you have absolutely no other option and need to execute side effect like within Effects, you can do that and comlplete the current stream so nothing get’s emitted futher via ignoreElements Rx operator.
NOTE 2: Redux-observable provides currently no “default” lazy loading mechanism (ngrx/effects does!), it needs to be implemented on your own as there is no “official” solution yet. We have our own custom solution, which will be open sourced soon.
What is Epic

One last thing to cover… How to communicate with our API.

Angular has build in solution — an HttpClient which is great! But...

This may be quite controversial, but do you really need that Observables laziness, for executing GET/POST requests agains your API? Especially, when those are executed within Epics/Effects, which use Observables, so you get all that laziness and reactive composition over there? So yeah, probably not, at least I didn't find much use for it in real life projects...

My services for REST endpoints look something like this ( in both Angular/React):

const base = 'users'
class HeroService {
constructor(private httpClient: HttpClient) {}
getOne(id: number): Promise<User> {
return this.httpClient.get<User>(${users}/${id}).then(normalize)
}
getAll(): Promise<User[]> {
return this.httpClient.get<User[]>(base).then(normalize)
}
create(payload: NewUser): Promise<User> {
return this.httpClient.post(base, payload).then(normalize)
}
update(payload: User): Promise<User> {
return this.httpClient.put(base, payload).then(normalize)
}
remove(id: number): Promise<boolean> {
return this.httpClient.delete(base, payload).then(normalize)
}
}

Where httpClient is an axios instance injected via Angular DI, in case of React a custom DI mechanism is applied.

So yup, you can definitely ditch @angular/common/http and use axios.

With promise based Service calls, your Epics will contain also less boilerplate, like can be observed on following diff:

Using Promise based Axios within Services simplifies/reduces Epics boilerplate

And with that covered, we are at the end!

In this article we covered what tools, architecture and libraries from React ecosystem can be used for Angular app development and for reusing existing solutions, so in the end we leverage the same tools and knowledge, no matter what rendering solution/Component framework are we using.

With that we destroyed boundaries that you might encounter when using Angular and it’s ecosystem and in the end instead of fighting, we embraced the knowledge provided by outside world ( React etc ) and we became best friends and will live in piece forever :)

Angular and React devs — Best Friends

Our final Angular CLI solution looks like this:

Better Angular CLI defaults with tools from React ecosystem and beyond

As always, don’t hesitate to ping me if you have any questions here or on twitter (my handle @martinhotell) and besides that, always remember — don’t hate, just skate ! ‘till next time folks! Cheers!


Tag cloud