Angular Best Practices

From bibbleWiki
Jump to navigation Jump to search

Getting Setup

Naming

Keep with the style guide. E.g. catalog.component.ts which has CatalogComponent inside.

File Structures

LIFT,

  • Locate Code Quickly
  • Identify code at a glance
  • Flattest structure possible
  • Try to be DRY

General Coding

Single Responsibility

The single-responsibility principle (SRP) is a computer-programming principle that states that every module, class or function in a computer program should have responsibility over a single part of that program's functionality, which it should encapsulate. All of that module, class or function's services should be narrowly aligned with that responsibility.[1]

Symbol Naming

Stick with the naming conventions in the style guide. Generally for angular we use camel case.

Immutability

You should try and use this all the time in javaScript and typeScript. E.g.

Example 1

this.currentUser.classes.push(classId)

Best Practice would be

this.currentUser = Object.assign(
   {},
   this.currentUsers, 
   {
      classes: this.currentUser.classes.concat([classId]}
   })

Example 2

This is another example. Although the this.currentUser is assigned it is assign with an existing value user

saveUser(user): Observable<any> {
   user.classes = user.classes || []
   this.currentUser = user
   return Observable.empty().delay(1000)
}

Best Practice

saveUser(user): Observable<any> {
   user.classes = user.classes || []
   this.currentUser = Object.assign(
      {},
      user,
      {classes: user.classes || []}
   )
   return Observable.empty().delay(1000)
}

Why Immutability

The reason immutability is deemed to be good is because assignments of existing values are by reference which means in the example 2, if user which is passed in to saveUser changes outside of saveUser, the currentUser will change too.

Small Functions

Do not create large functions. In my view make minimum functions and separate flow code from calculation code.

Modules In Angular

Modules help

  • single responsibility principle
  • lazy loading
  • managing resources, requirements

Standard approach is

  • app module
  • routing module
  • core module (Services)
  • shared module (Components, Directives and Pipes)
  • n Feature modules

Angular Components

Selectors

This was mentioned but linting now takes care of this.

Separating CSS and Templates

Don't know what the course says but always separate them.

Input Outputs

Again always use decorators and never use input: on the @Component decorator

Delegating Complex Logic

In my view, if it is not directly connected to the UI put it in a service. I guess similar to HCO in React. This also makes the code more open to unit testing.

Ordering of Component Code

Just use lint and prettier.

Services

  • Use @Injectable decorator
  • Put data retrieval in a service
  • Remember @Injectable is hierarchical

Performance

Ahead of time Compilation

I think this means always use --prod=true for builds.

Lazy Loading

For lazy loading make a module and change the routing to have loadChildren. For example

 const routes: Routes = [
  {
    path: 'test',
    loadChildren: () =>
      import('./../../pages/test/test.module').then((mod) => mod.TestModule),
  },
...

Bundle Sizes

Intro

Keep tracking these by doing

cd dist
du -sh *

Also you can use the npm package source-map-explorer to look a this. You will need to do a production build with source maps. i.e.

ng --prod=true --sourceMap=true


Now by running source-explorer-map on the bundle we can see which packages are taking up the space.
Alternatively you can use webpack-bundle-analyzer. First install the package

npm install -D webpack-bundle-analyzer

Then add the scripts to create the json and run the analyzer in packages.json

    "build:stats": "ng build --stats-json=true",
    "analyze": "webpack-bundle-analyzer dist/myproject/stats.json"

Now you can run this with

npm run analyze
npm run build:stats

This will bring up details similar to example 2

Example 1

In the example for the tutorial the issue was the imports statements

import {Observable, Subject} from 'rxjs/Rx'

Instead of the more efficient

import {Observable} from 'rxjs/Observable'
import {Subject} from 'rxjs/Subject'

Example 2

Running this on my website showed a problem with moment.js. Angular best practice moment.png And the fix was

Install Webpack Customizer

Install the npm as development

npm i -D @angular-builders/custom-webpack

Use Package

We need to change the angular.json to use the builder

-          "builder": "@angular-devkit/build-angular:browser",
+          "builder": "@angular-builders/custom-webpack:browser",
           "options": {
+            "customWebpackConfig": {
+              "path": "./custom-webpack.config.js"
+            },             
....
         "serve": {
-          "builder": "@angular-devkit/build-angular:dev-server",
+          "builder": "@angular-devkit/custom-webpack:dev-server",
           "options": {
+            "customWebpackConfig": {
+              "path": "./custom-webpack.config.js"
+            },                        
             "browserTarget": "bibbleWeb:build"

Custom-webpack.config.js

Add a custom-webpack.config.js to the root of the project.

"use strict";

const webpack = require("webpack");

/**
 * Custom webpack configuration
 */
module.exports = {
  ...
  module: {
    rules: [...
    ],
  },
  plugins: [
    // Filter out the moment locales to reduce bundle size
    // Locales that should be included MUST be added to the project, otherwise they won't be available for use)
    // References:
    // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  ],
};

OnPush Change Detection

Best Practice is not to touch this but if you have a case where it is expensive to refresh then maybe it is a worthwhile but know how it works.