The first things we need to do is create a reducer:
/** * CONSTANT * @type {string} */ export const GET_CATEGORIES = "GET_CATEGORIES"; /** * INIT VALUE */ export const initialCategories = [ {id: 0, name: ‘Development‘}, {id: 1, name: ‘Design‘}, {id: 2, name: ‘Exercise‘}, {id: 3, name: ‘Humor‘} ]; /** * REDUCERS * @type {string} */ export const categories = (state = initialCategories, {type, payload}) => { switch(type) { case GET_CATEGORIES: return payload || state; default: return state; } };
It has some default initialize data. What it does is just simply return the state.
Then let‘s create a gloable store for the app, which has two methods, getState, dispatch. Two props: reducer, state.
class Store { constructor(reducer, initialState) { this.reducer = reducer; this.state= initialState; } getState() { return this.state } dispatch() { this.state = this.reducer(this.state, action); } }
Once we got that, we are going to init our store:
import {categories, initialCategories} from ‘./components/categories/category.state‘; import Store from ‘./app.store‘; const store = new Store(categories, initialCategories);
We passed in categoreis reudcer and the initialCategories state.
To make it available to Angular APP. we need to make it injectable:
let appModule = angular.module(‘app‘, [ CommonModule.name, ComponentsModule.name ]) .value(‘store‘, store)
Then we can use it in our app:
class CategoriesController { constructor(store) { ‘ngInject‘; angular.extend(this, { store }); } $onInit() { this.store.dispatch({type: GET_CATEGORIES}); this.categories = this.store.getState(); } }
Now we are going to simply the code a little bit, we going to make a subscribe method so that we don‘t need to call getState() method everytime after we dispatch an action.
You can think that the subscribe method is a just callback function which each time we dispatch an action, it will be called. And inside the callback function, we will call this.store.getState() to get the value.
class Store { constructor(reducer, initialState) { this.reducer = reducer; this.state = initialState; this.listeners = []; } getState() { return this.state; } dispatch(action) { this.state = this.reducer(this.state, action); this.listeners.forEach((l) => l()); } subscribe(listener) { this.listeners = [ ...this.listeners, listener ]; // return an unsubscribe function return () => { this.listeners = this.listeners.filter(l => l !== listener); } } } export default Store;
class CategoriesController { constructor($timeout, store) { ‘ngInject‘; angular.extend(this, { $timeout, store }); } $onInit() { this.unsubscribe = this.store.subscribe(() => { this.categories = this.store.getState(); }); this.store.dispatch({type: GET_CATEGORIES}); } }
Currently inside the dispatch() metod, we pass in an object with type and payload. It would be better if we can manage those action in a single place. There is where Action creator comes in to play.
/** * ACTIONS CREATOR */ export const CategoriesActions = () => { const getCategoreis = (categories) => { return {type: GET_CATEGORIES, payload: categories} }; const getCurrentCategory = (currentCategory) => { return {type: GET_CURRENT_CATEGORY, payload: currentCategory} }; return { getCategoreis, getCurrentCategory }; };
To make it avaiable to Angular App, we can create a factory for this:
let appModule = angular.module(‘app‘, [ CommonModule.name, ComponentsModule.name ]) .value(‘store‘, store) .factory(‘CategoriesActions‘, CategoriesActions) .component(‘app‘, AppComponent)
Then we can use it inside the controller:
constructor($timeout, store, CategoriesActions) { ‘ngInject‘; angular.extend(this, { $timeout, store, CategoriesActions }); }
$onInit() { this.unsubscribe = this.store.subscribe(() => { this.categories = this.store.getState(); }); this.store.dispatch(this.CategoriesActions.getCategoreis()); }
onCategorySelected(currentCategory) { this.currentCategory = category(this.currentCategory, this.CategoriesActions.getCurrentCategory(currentCategory)); }