Angular Revisit

From Training Material
Jump to navigation Jump to search
title
Angular2 Revisit
author
Hao Wang

Setup ⌘

git clone https://github.com/jusfeel/ng-demo.git
git clone https://github.com/jusfeel/ngob.git
git clone https://github.com/jusfeel/ng-child-routes-demo.git
git clone https://github.com/jusfeel/ng-route-guards-demo.git
git clone https://github.com/jusfeel/ng-testing-demo
git clone https://github.com/jusfeel/ngrx-demo

Setup - packages ⌘

 npm i jquery@1.9.1 	
 npm i bootstrap@3.3.7
 npm i font-awesome
 npm i json-server
 more package.json
"styles": [
        "../node_modules/bootstrap/dist/css/bootstrap.min.css",
        "../node_modules/font-awesome/css/font-awesome.min.css",
        "styles.css"
      ],
"scripts": [
        "../node_modules/jquery/jquery.min.js",
        "../node_modules/bootstrap/dist/js/bootstrap.min.js"
      ],

Form ⌘

  • Briefly review the concept, some reasons for using certain annotations were unclear
    • Forms-Demo
  • A custom validator with reactive forms
    • lettersNumbersOnly, usernameNotTaken
  • A custom validator with template-driven form
    • lettersNumbersOnly, usernameNotTaken
https://angular.io/api/forms/Validators

Rxjs/Observables ⌘

http://github.com/jusfeel/ngob.git

Rxjs/Observables - good demo ⌘

Event Emitter ⌘

  • Explain concept (we see it as an Angular design pattern)
    • Component can receiving data and giving out data. This is the way component communicates. [IN] (Out) [(ngModel)] too-way binding
    • You can have a component to manage list of items and another component to collect item detail by a form. let the form get the data and emit “I finished” event, parent component observe the change(output) and act upon it(save the item).
  • Use case: when should we use it
    • Anywhere components have data flowing between them
  • Should we use it in our project
    • Yes
  • Why do we need to re-subscribe to an observable every time, during “ngOnChanges” (was not clarified in training)
    • If you mean http observables, then when input data changed, the result set might need to change with it. Unsubscribe simply fire another request to get the update data. Say you have are doing searching, the ngOnChanges is observing the input keyword.


State Management ⌘

  • Explaining/review the concept with Redux and reducers with actions
  • Use case: when to switch to state management.
  • localStorage: not supported on Apple browsers, what is the alternative?

Redux - core concept 1 ⌘

Redux - core concept 2 ⌘

    • State of app is plain object like this:
{
  todos: [{
    text: 'Eat food',
    completed: true
  }, {
    text: 'Exercise',
    completed: false
  }],
  visibilityFilter: 'SHOW_COMPLETED'
}

Redux - core concept 3 ⌘

  • To change something in the state, you need to dispatch an action. An action is a plain JavaScript object:
{ type: 'ADD_TODO', text: 'Go to swimming pool' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
  • Enforcing that every change is described as an action lets us have a clear understanding of what’s going on in the app. If something changed, we know why it changed. Actions are like breadcrumbs of what has happened.

Redux - core concept 4 ⌘

  • Finally, to tie state and actions together, we write a function called a reducer —it’s just a function that takes state and action as arguments, and returns the next state of the app. It would be hard to write such a function for a big app, so we write smaller functions managing parts of the state:
function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter
  } else {
    return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat([{ text: action.text, completed: false }])
    case 'TOGGLE_TODO':
      return state.map(
        (todo, index) =>
          action.index === index
            ? { text: todo.text, completed: !todo.completed }
            : todo
      )
    default:
      return state
  }
}

Redux - core concept 4 ⌘

  • And we write another reducer that manages the complete state of our app by calling those two reducers for the corresponding state keys:
function todoApp(state = {}, action) {
  return {
    todos: todos(state.todos, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  }
}

Three principles - Single source of truth ⌘

  • The state of your whole application is stored in an object tree within a single store.
console.log(store.getState())

/* Prints
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Consider using Redux',
      completed: true,
    },
    {
      text: 'Keep all state in a single tree',
      completed: false
    }
  ]
}
*/

Three principles - State is read-only ⌘

  • The only way to change the state is to emit an action, an object describing what happened.
store.dispatch({
  type: 'COMPLETE_TODO',
  index: 1
})

store.dispatch({
  type: 'SET_VISIBILITY_FILTER',
  filter: 'SHOW_COMPLETED'
})

Three principles - Changes are made with pure function ⌘

  • To specify how the state tree is transformed by actions, you write pure reducers.
function visibilityFilter(state = 'SHOW_ALL', action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: true
          })
        }
        return todo
      })
    default:
      return state
  }
}

import { combineReducers, createStore } from 'redux'
const reducer = combineReducers({ visibilityFilter, todos })
const store = createStore(reducer)

Ngrx ⌘

  • For a simple app you're fine to just have one reducer, but for a larger more complex app it's a good idea to break down your state into pieces and then compose them together. So each reducer is just responsible for it's piece of state, and then there's a function that composes the reducers together.
// Part One

export interface PartOneState {
    a: string,
};

export const partOneInitialState: PartOneState = {
    a: '',
};

export const SAY_HI = 'SAY_HI';
export class SayHi implements Action {
    readonly type = SAY_HI;
}

export const SAY_BYE = 'SAY_BYE';
export class SayBye implements Action {
    readonly type = SAY_BYE;
}

export const partOneReducer = (state: PartOneState = partOneInitialState,
                               action): PartOneState => {
    switch (action.type) {
        case SAY_HI:
            return {  a: 'hi' };
        case SAY_BYE:
            return {  a: 'bye' };

        default:
            return state;
    }
}

// Part Two

export interface PartTwoState {
    b: number;
};

export const partTwoInitialState: PartTwoState = {
    b: 0,
};

export const ADD = 'ADD';
export class Add implements Action {
    readonly type = ADD;
}

export const SUBTRACT = 'SUBTRACT';
export class Subtract implements Action {
    readonly type = SUBTRACT;
}

export const partTwoReducer = (state: PartTwoState = partTwoInitialState,
                               action): PartTwoState => {
    switch (action.type) {
        case ADD:
            return {  b: state.b + 1 };
        case SUBTRACT:
            return {  b: state.b - 1 };

        default:
            return state;
    }
}

// Compose them together


export interface AppState {
    partOne: PartOneState,
    partTwo: PartTwoState
}

export const reducer: ActionReducerMap<AppState> = {
    partOne: partOneReducer,
    partTwo: partTwoReducer
};



// In your app.module.ts file...
{
    // ...
    imports: [
        // ...
        StoreModule.forRoot(reducer),
        // ...
    ]
}

When to use ⌘

Routing ⌘

Style guide ⌘

  • Resources outside the ‘asset’ folder
    • If not included in the bundle but used like png, fonts etc
    • edit .angular-cli.json to include
  • File structure: core, shared and feature module organization in our project
    • Core module loading shared and a few feature modules
    • Other feature Modules loading core and shared module mostly
    • Shared Module for shared pipe, directive etc
    • Models folder shared across app
    • Util folder shared across app

Testing ⌘

  • How do we test our services with observables?
    • mocking http
  • How do we test/mock our methods where httpClient is used. Note: we created such methods during our training workshops
    • mocking service
  • https://github.com/jusfeel/ng-testing-demo.git