Upgrade to Pro — share decks privately, control downloads, hide ads and more …

STORE Trek: The Voyage to Angular (ngVikings 2019)

STORE Trek: The Voyage to Angular (ngVikings 2019)

You know that the journey from AngularJS to Angular can be tricky. You may also know that NgRx can help make your Angular applications be more scalable and testable. But did you know that NgRx and ngUpgrade can work together to take your upgrade to warp factor 10 ?

In this talk, Mike Ryan and Sam Julien — the NgRx guy and the ngUpgrade guy — will teach you how to inject your NgRx store into your AngularJS code. You can then dispatch actions and trigger effects from AngularJS! You’ll learn how to set up this strategy and how to use it to migrate step-by-step. You’ll also learn how NgRx + ngUpgrade can give you greater flexibility in your upgrade by decoupling your business logic, how you can call AngularJS services with effects (even when they’re returning promises), and how to gracefully transition a feature from AngularJS all the way to Angular with NgRx.

Sam Julien

May 27, 2019
Tweet

More Decks by Sam Julien

Other Decks in Technology

Transcript

  1. STATE Warp speed is set to a number between 3

    and 9.95 The weapons are “firing” or “online” The shields are “raised” or “lowered”
  2. export class DashboardService { fireTorpedoes() { this.$rootScope.$broadcast( this.FIRE_TORPEDOES, "Firing torpedoes!"

    ); } onFireTorpedoes($scope, handler) { $scope.$on(this.FIRE_TORPEDOES, (event, message) => { handler(message); }); } }
  3. export class DashboardService { fireTorpedoes() { this.$rootScope.$broadcast( this.FIRE_TORPEDOES, "Firing torpedoes!"

    ); } onFireTorpedoes($scope, handler) { $scope.$on(this.FIRE_TORPEDOES, (event, message) => { handler(message); }); } }
  4. export class DashboardService { fireTorpedoes() { this.$rootScope.$broadcast( this.FIRE_TORPEDOES, "Firing torpedoes!"

    ); } onFireTorpedoes($scope, handler) { $scope.$on(this.FIRE_TORPEDOES, (event, message) => { handler(message); }); } }
  5. WHAT IF… The warp drive algorithm changes? The weapons system

    changes? We need to use this system for multiple ships?
  6. APPLICATION STATE THAT CHANGES FREQUENTLY APPLICATIONS WITH MANY SOURCES OF

    CHANGE LARGE-SCALE ENTERPRISE APPLICATIONS YOU MAY NEED NGRX IF…
  7. ' )

  8. class ShieldsComponent { @Input() status: "up" | "down"; @Output() raiseShields:

    EventEmitter; } DOES THIS COMPONENT KNOW WHO IS BINDING TO ITS INPUT?
  9. class ShieldsComponent { @Input() status: "up" | "down"; @Output() raiseShields:

    EventEmitter; } DOES THIS COMPONENT KNOW WHO IS LISTENING TO ITS OUTPUT?
  10. export enum Types { RaiseShields = "[Shields] Raise Shields", LowerShields

    = "[Shields] Lower Shields" } export interface RaiseShieldsAction { type: Types.RaiseShields; } export interface LowerShieldsAction { type: Types.LowerShields; }
  11. export interface State { shieldsRaised: boolean; warpSpeed: number; firingTorpedoes: boolean;

    } export const initialState: State = { shieldsRaised: false, warpSpeed: 3, firingTorpedoes: false };
  12. export function reducer( state = initialState, action: ShieldsActions.Union ): State

    { switch (action.type) { case ShieldsActions.Types.RaiseShields: { return { ...state, shieldsRaised: true }; } case ShieldsActions.Types.LowerShields: { return { ...state, shieldsRaised: false }; } } }
  13. export function reducer( state = initialState, action: ShieldsActions.Union ): State

    { switch (action.type) { case ShieldsActions.Types.RaiseShields: { return { ...state, shieldsRaised: true }; } case ShieldsActions.Types.LowerShields: { return { ...state, shieldsRaised: false }; } } }
  14. export function reducer( state = initialState, action: ShieldsActions.Union ): State

    { switch (action.type) { case ShieldsActions.Types.RaiseShields: { return { ...state, shieldsRaised: true }; } case ShieldsActions.Types.LowerShields: { return { ...state, shieldsRaised: false }; } } }
  15. weaponsController.$inject = ["Store"]; function weaponsController(store: Store<any>) { const vm =

    this; vm.fireWeapons = () => store.dispatch(createFireTorpedoesAction()); }
  16. weaponsController.$inject = ["Store"]; function weaponsController(store: Store<any>) { const vm =

    this; vm.fireWeapons = () => store.dispatch(createFireTorpedoesAction()); }
  17. @Effect() fireTorpedoes$ = this.actions$.pipe( ofType(WeaponsActions.Types.FireTorpedoes), concatMap(async () => { await

    this.upgrade.$injector .get("weaponsService") .fireTorpedoes(); return WeaponsActions.createFireTorpedoesSuccessAction(); }) );
  18. @Effect() fireTorpedoes$ = this.actions$.pipe( ofType(WeaponsActions.Types.FireTorpedoes), concatMap(async () => { await

    this.upgrade.$injector .get("weaponsService") .fireTorpedoes(); return WeaponsActions.createFireTorpedoesSuccessAction(); }) );
  19. @Effect() fireTorpedoes$ = this.actions$.pipe( ofType(WeaponsActions.Types.FireTorpedoes), concatMap(async () => { await

    this.upgrade.$injector .get("weaponsService") .fireTorpedoes(); return WeaponsActions.createFireTorpedoesSuccessAction(); }) );
  20. @Effect() fireTorpedoes$ = this.actions$.pipe( ofType(WeaponsActions.Types.FireTorpedoes), concatMap(async () => { await

    this.upgrade.$injector .get("weaponsService") .fireTorpedoes(); return WeaponsActions.createFireTorpedoesSuccessAction(); }) );
  21. statusController.$inject = ["Store"]; function statusController(store: Store<fromRoot.AppState>) { const torpedoesSubscription =

    store .select(fromRoot.selectShipIsFiringTorpedoes) .subscribe(isFiringTorpedoes => { vm.weaponsStatus = isFiringTorpedoes ? "Firing torpedoes!" : "Weapons online."; }); vm.$onDestroy = () => torpedoesSubscription.unsubscribe(); }
  22. statusController.$inject = ["Store"]; function statusController(store: Store<fromRoot.AppState>) { const torpedoesSubscription =

    store .select(fromRoot.selectShipIsFiringTorpedoes) .subscribe(isFiringTorpedoes => { vm.weaponsStatus = isFiringTorpedoes ? "Firing torpedoes!" : "Weapons online."; }); vm.$onDestroy = () => torpedoesSubscription.unsubscribe(); }
  23. statusController.$inject = ["Store"]; function statusController(store: Store<fromRoot.AppState>) { const torpedoesSubscription =

    store .select(fromRoot.selectShipIsFiringTorpedoes) .subscribe(isFiringTorpedoes => { vm.weaponsStatus = isFiringTorpedoes ? "Firing torpedoes!" : "Weapons online."; }); vm.$onDestroy = () => torpedoesSubscription.unsubscribe(); }
  24. export const shieldsComponent = { template: ` <button ng-click="$ctrl.raiseShields()">Raise</button> <button

    ng-click="$ctrl.lowerShields()">Lower</button> `, controller: shieldsController }; shieldsController.$inject = ["Store"]; function shieldsController(store: Store<any>) { const vm = this; vm.raiseShields = () => store.dispatch(createRaiseShieldsAction()); vm.lowerShields = () => store.dispatch(createLowerShieldsAction()); }
  25. @Component({ template: ` <button (click)="raiseShields()">Raise</button> <button (click)="lowerShields()">Lower</button> ` }) export

    class ShieldComponent { constructor(private store: Store<any>) {} raiseShields = () => this.store.dispatch(createRaiseShieldsAction()); lowerShields = () => this.store.dispatch(createLowerShieldsAction()); }
  26. CREATE ACTIONS TO CAPTURE APP EVENTS WRITE REDUCERS TO MANAGE

    STATE TRANSITIONS BUILD EFFECTS TO COMMUNICATE WITH SERVICES REARCHITECT THE APPLICATION TO NGRX STEP ONE DOWNGRADE STORE USING ANGULAR UPGRADE SUBSCRIBE TO SELECTORS IN YOUR COMPONENTS
  27. PICK A VIEW LAYER UPGRADE STRATEGY STEP TWO COMPONENTS ROUTES

    </> </> </> </> </> </> </> </> </> </> </> </> </> </>