SlideShare a Scribd company logo
PREDICTABLE WEB APPS WITH
ANGULAR AND REDUX
@giorgionatili
ABOUT ME
✴ Engineering Manager at
Akamai Technologies
✴ Google Developer Expert
✴ Organizer of DroidconBos
(www.droidcon-boston.com)
✴ Organizer of SwiftFest 2017
(www.swiftfest.io)
✴ Founder of Mobile Tea 

(www.mobiletea.net)
Droidcon Boston
bitly.com/dcbos18
Droidcon Boston
bitly.com/dcbos18
Mobile Tea
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
AGENDA
✴ Using AngularJS within Angular applications
✴ Integrating AngularJS components into Angular
✴ Redux in a nutshell
✴ Predictable states in an hybrid world
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
THE PROBLEMS
✴ Multiple components and services consume and fetch data
from multiple sources at a not given time
✴ Multidirectional and optimistic data propagation
✴ Application state out of control and not testable
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
TOOLS WE’LL USE
+ +
&
ANGULARJS & ANGULAR
Using AngularJS within Angular applications
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
WORKING WITH HYBRID ANGULAR APPS
✴ Modules built with different Angular versions should work in
the same app
✴ The routing system(s) should be aware of which routes it
should take care of
✴ Existing components should work in the NG4 world
CONFIGURING THE APP
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
ANGULAR STYLE
✴ Angular apps are built as a tree of components
✴ Components are encapsulated, use@Inputs and @Outputs
to pass the data in & out
✴ Angular compiles components ahead of time as part of our
build process
✴ Angular is built on top of TypeScript and clearly separates
the static parts of our applications (stored in decorators) from
the dynamic parts (components)
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
ANGULARJS 1.5.X+ STYLE
✴ Yo can write AngularJS applications in the Angular style
✴ AngularJS 1.5+ added angular.component and the
$onInit(), $onDestroy(), and $onChanges() life-cycle
events
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
NGUPGRADE
✴ NgUpgrade is a library to use in our applications to mix and
match AngularJS and Angular components
✴ It bridges the AngularJS and Angular dependency injection
systems
✴ With NgUpgrade it’s possible to bootstrap an existing
AngularJS application from an Angular one
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
USING NGUPGRADE
@NgModule({ declarations: [
AppComponent
],
imports: [
BrowserModule,
UpgradeModule
] })
export class AppModule {
constructor(private upgrade: UpgradeModule) {}
ngDoBootstrap() {
this.upgrade.bootstrap(document.body, ['AngularJsModule']);
}
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
UPGRADEMODULE.BOOTSTRAP
✴ It makes sure angular.bootstrap runs in the right zone
✴ It adds an extra module that sets up AngularJS to be visible in
Angular and vice versa
✴ It adapts the testability APIs to make Protractor work with
hybrid apps
@giorgionatili@DroidconBos
THAT’S IT…
@giorgionatili@DroidconBos
COMMON ISSUES
✴ It’s not possible to automatically upgrade components and
directives that use link or compile
✴ Components/directives that use scope can’t be upgraded too
✴ Also NgModel is not supported by NgUpgrade
@giorgionatili@DroidconBos
ANY OPTION?
@giorgionatili@DroidconBos
UPGRADE STRATEGIES
✴ Updating the app it route by route, screen by screen, or
feature by feature (vertical slicing)
✴ Start with upgrading reusable components like inputs and
date pickers (horizontal slicing)
@giorgionatili@DroidconBos
INTEGRATE THE TWO CODE BASES
ANGULARJS AND
ANGULAR
Integrating AngularJS components into Angular
@giorgionatili@DroidconBos
WEBPACK
✴ Most Angular 2/4 code seems to be using Webpack for
building
✴ Although it’s technically possible to use other solutions like
Browserify, it makes most sense to switch to Webpack to
avoid going "swimming upstream" as it were
@giorgionatili@DroidconBos
OTHER TASKS RUNNER
✴ Webpack creates essentially a single bundle which you then
use other plugins to split into separate files
✴ When there are tasks that didn't fit well into this model (unit
tests, merging of locale files, etc.) don’t migrate them but
keep them (i.e. Gulp)
@giorgionatili@DroidconBos
WEBPACK CONFIGURATION
✴ Create your configuration file by using $ eject from the
angular-cli tool
✴ Separate your configuration files for the development, testing
and production environments
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
WEBPACK CONFIG FILE
var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var commonConfig = require('./webpack.common.js');
//add HMR client to entries
let entries = {};
for (let entryName of Object.keys(commonConfig.entry)) {
entries[entryName] = commonConfig.entry[entryName].concat('webpack-hot-middleware/client?
reload=true');
}
let merge = webpackMerge.strategy({entry: 'replace'});
module.exports = merge(commonConfig, {
devtool: 'cheap-module-inline-source-map',
entry: entries,
plugins: [
new webpack.NoEmitOnErrorsPlugin(),
// enable HMR globally
new webpack.HotModuleReplacementPlugin(),
// prints more readable module names in the browser console on HMR updates
new webpack.NamedModulesPlugin()
],
devServer: {
historyApiFallback: true,
stats: 'minimal'
}
});
@giorgionatili@DroidconBos
HYBRID APP TEMPLATE
Create two directives in your html, one is the Angular (i.e. my-
app) app, the other is the AngularJS app (i.e. myangularjs-
app-root)
<myangularjs-app-root></myangularjs-app-root>
<my-app></my-app>
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
BOOTSTRAPPING ANGULAR 4
✴ Angular is bootstrapped first through:
platformBrowserDynamic().bootstrapModule(AppMo
dule);
✴ In the app module explicitly say to use your AppComponent
to bootstrap the main component bootstrap:
[AppComponent]
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
BOOTSTRAPPING ANGULAR 1
In the constructor of the app module bootstrap AngularJS to
prevent the $injector not found error
constructor(private upgrade: UpgradeModule) {

upgrade.bootstrap(document.documentElement, ['angular-js-app'],
{strictDi: true});
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
INJECTING THE SCOPE
To be sure the scope is injected into Angular, use a provider to
simply get the $scope into the AppModule
const angularjsScopeProvider: FactoryProvider = {
provide: '$scope',
useFactory: ($injector: any) => $injector.get('$rootScope'),
deps: ['$injector']
};
@giorgionatili@DroidconBos
DUAL ROUTING
✴ It’s possible to run simultaneously different routing systems
✴ To get ui-router and the Angular one working together, it’s
enough to tell each router to ignore the other's routes
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
TELLING UI-ROUTER TO IGNORE OUR ANGULAR4 ROUTE
export class YourAppRootController {
constructor($rootScope, $location) {
'ngInject';
Object.assign(this, {$rootScope, $location});
this.ng1Route = true;
$rootScope.$on('$locationChangeSuccess',
this.handleStateChange.bind(this));
}
handleStateChange() {
// If the url starts with /ng4-route this will be handled by NG4 

// and we should hide the ui-view
this.ng1Route = !this.$location.url().startsWith(‘/ng4-route‘);
}
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
HIDING THE UI-VIEW WHEN NOT ON A NG1 ROUTE
export class AngularJSRootController {
constructor($rootScope, $location) {
'ngInject';
Object.assign(this, {$rootScope, $location});
this.ng1Route = true;
$rootScope.$on('$locationChangeSuccess',
this.handleStateChange.bind(this));
}
handleStateChange() {
//if the url starts with /ng4-route it should hide the ui-view)
this.ng1Route = !this.$location.url().startsWith(‘/ng4-route');
}
}
@giorgionatili@DroidconBos
ALMOST THERE
UPGRADING COMPONENTS
@giorgionatili@DroidconBos
UPGRADING ISSUES
✴ There are a few things that will prevent a directive or
component from being upgraded:
✴ link function
✴ scope
✴ Requiring NgModel or any other controller on the directive
itself (or a parent)
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
COMPONENT WRAPPER
✴ There's no support for components that use NgModel in
upgradecomponent as of today
✴ To work around this, a possible solution, is implementing a
wrapper component
@giorgionatili@DroidconBos
WRAPPER RESPONSIBILITIES
✴ Acting as a bridge and using ng-model to put values into the
old component
✴ Listening to the ng-change events and bubbling up values
from the wrapper in an EventEitter
✴ Exposing and using the Angular methods as needed
@giorgionatili@DroidconBos
WRAPPER AND FORMS
✴ Angular offers a great variety of Forms validators
✴ When a component doesn’t have its own validation, using a
dummy form in the wrapper expose all the available Angular
validators
@giorgionatili@DroidconBos
WRAPPER CONTROLLER
✴ This is a class to assist in writing a controller for a AngularJS
component that wraps something using NgModel
✴ It handles passing through validation etc.
✴ The Component must be wrapped in a ng-form with name
$ctrl.model to work properly
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
WRAPPING AN EXISTING COMPONENT
import {IAngularStatic} from 'angular';
import {AbstractNgModelWrapperController} from './abstract-ng-model-wrapper.controller';
declare var angular: IAngularStatic;
class YourWrapperController extends AbstractNgModelWrapperController {
private collection: any[];
public writeValue(obj: any): void {
this.collection = obj;
}
public handleChange() {
this.onChange(this.collection);
this.onBlur();
this.onValidatorChange();
}
}
const yourWrapperComponent = {
bindings: {
collection: '<'
},
controller: YourWrapperController,
template: `<ng-form name="$ctrl.form">
<your-legcy-selector ng-model="collection">
</your-legacy-selector>
</ng-form>`
};
angular.module('your-app-name').component('yourComponenWrapper', yourWrapperComponent);
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
NGMODELWRAPPERCONTROLLER
import {ControlValueAccessor, Validator} from '@angular/forms';
export abstract class NgModelWrapperController implements ControlValueAccessor,
Validator {
public isDisabled: boolean = false;
protected onChange: any;
protected onBlur: any;
protected onValidatorChange: () => void;
public abstract writeValue(obj: any): void;
public registerOnChange(fn: any): void {
this.onChange = fn;
}
public registerOnTouched(fn: any): void {
this.onBlur = fn;
}
public registerOnValidatorChange(fn: () => void): void {
this.onValidatorChange = fn;
}
}
@giorgionatili@DroidconBos
UPGRADING THE COMPONENT
✴ Creating an Angular directive that wraps the upgraded
component
✴ Expose the @Input() and @Output() needed to
communicate with the Angular “world”
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
UPGRADING THE COMPONENT TO A DIRECTIVE
import { Directive, ElementRef, Injector, Input } from '@angular/core';
import { NgModelWrapperUpgradeComponent } from './ng-model-wrapper-upgrade.component';
@Directive({
selector: ‘your-directive-selector‘
})
export class YourDirective extends NgModelWrapperUpgradeComponent {


@Input() public collection: any[];
constructor(elementRef: ElementRef, injector: Injector) {
super('yourComponentWrapper', elementRef, injector);
}
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
NGMODELWRAPPERUPGRADECOMPONENT
import {UpgradeComponent} from '@angular/upgrade/static';
import {AbstractControl, ControlValueAccessor, ValidationErrors, Validator} from '@angular/forms';
export abstract class NgModelWrapperUpgradeComponent extends UpgradeComponent
implements ControlValueAccessor, Validator {
public writeValue(obj: any): void {
return (this as any).controllerInstance.writeValue(obj);
}
public registerOnChange(fn: any): void {
return (this as any).controllerInstance.registerOnChange(fn);
}
public registerOnTouched(fn: any): void {
return (this as any).controllerInstance.registerOnTouched(fn);
}
public setDisabledState(isDisabled: boolean): void {
return (this as any).controllerInstance.setDisabledState(isDisabled);
}
public validate(c: AbstractControl): ValidationErrors {
return (this as any).controllerInstance.validate(c);
}
public registerOnValidatorChange(fn: () => void): void {
return (this as any).controllerInstance.registerOnValidatorChange(fn);
}
}
@giorgionatili@DroidconBos
RELATIONSHIPS OVERVIEW
NgModelWrapper

Controller
NgModelWrapperUpgr
adeComponent
YourComponent

WrapperController
YourNG4Directive
@giorgionatili@DroidconBos
ALL SET!!!
REDUX IN A NUTSHELL
Predictably and Composability
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
DEFINITION
Redux holds all the state of your application.
It doesn't let you change that state directly, but instead forces you to
describe changes as plain objects called “actions".
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
CORE CONCEPTS
✴ A global immutable application state
✴ Unidirectional data-flow
✴ Changes to state are made in pure functions, or reducers
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
REDUX BUILDING BLOCKS
✴ Store, a centralized representation of the state of an app
✴ Reducers, pure functions that return a new representation of
the state
✴ Actions, POJOs that represent a change in the state of the app
✴ Middleware, a chain-able function used to extend Redux
@giorgionatili@DroidconBos
ACTIONS IN A NUTSHELL
✴ Actions are payloads of information that send data to the
application store
✴ By convention, they have a type attribute that indicates what
kind of action we are performing
✴ Flux standard actions also have a payload attribute used to
propagate the data needed to determine the next state
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
ACTIONS CREATORS
✴ Action creators are function that create actions
✴ They do not dispatch actions to the store, making them
portable and easier to test as they have no side-effects
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
HOW AN ACTION LOOKS LIKE
{
type: 'LOADED',
payload: {
text: 'Do something.'
}
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
ACTIONS SERVICES
✴ Encapsulating related-actions dispatchers in the same file
promote code reuse and organization
✴ Making them @Injectable() allow to use them within the
dependency injection system of Angular
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
ACTIONS SERVICES
import { Injectable } from '@angular/core';
import { NgRedux } from 'ng2-redux';
import { UserProfileState } from '../model/profile-state';
@Injectable()
export class ProfileLoadingActions {
static LOADED: string = 'PROFILE_LOADED';
constructor(private ngRedux: NgRedux<UserProfileState>) {}
loaded(): void {
this.ngRedux.dispatch({ type: ProfileLoadingActions.LOADED });
}
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
REDUCERS AS PURE FUNCTIONS
✴ Reducers are pure functions that, given an input, return
always the same output as a fresh representation of a slice of
the app state
✴ When the store needs to know how an action changes the
state, it asks the reducers
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
HOW A REDUCER LOOKS LIKE
import { ProfileLoadingActions } from '../../actions/loading-actions';
import { INITIAL_STATE } from './loading-initial-state';
export function isLoadingReducer(state: boolean = INITIAL_STATE,
action: any): boolean {
switch (action.type) {
case ProfileLoadingActions.LOADED:
return true;
case ProfileLoadingActions.UPDATED:
return true;
case ProfileLoadingActions.UPDATE:
return false;
case ProfileLoadingActions.NOT_LOADED:
return false;
default:
return state;
}
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
DESIGNING THE STORE
✴ The store is not a specular copy of the application model
✴ The store represents the information needed to properly
render the UI elements of a web application
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
COMBINE REDUCERS
✴ The most common state shape for a Redux app is a POJO
containing slices of the model at each top-level key
✴ combineReducers takes an object full of slice reducer
functions, and returns a new reducer function
✴ It is a utility function to simplify the most common use case
of combining multiple reducers to describe the app state
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
COMBINING REDUCERS IN ACTION
import { loadingReducer as isLoading }
from './loading/loading.reducer.ts';
import { errorReducer as error }
from './error/error.reducer.ts';
export const rootReducer =
combineReducers({
isLoading,
error,
});
MORE ON ACTIONS
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
COMBINE ACTIONS
✴ Combining actions together can be a nice way to simplify the
application state
✴ Executing in sequence or in parallel multiple actions can help
the semantic of your code
✴ There is a middleware (redux-combine-actions) to easy
combine async actions and dispatch them
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
INTEGRATION
import { createStore, combineReducers, applyMiddleware } from 'redux';
import combineActionsMiddleware from 'redux-combine-actions';
let createStoreWithMiddleware =
applyMiddleware(combineActionsMiddleware)(createStore);
let rootReducer = combineReducers(reducers); // Store initialization
let rootStore = createStoreWithMiddleware(rootReducer);
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
USING REDUX-COMBINE-ACTIONS
export function addTodo(text) {
return { type: 'ADD_TODO', text };
}
export function increment() {
return { type: 'INCREMENT_COUNTER' };
}
// Combine "addTodo" and "increment" actions
export function addTodoAndIncrement({text}) {
return {
types: [
'COMBINED_ACTION_START',
'COMBINED_ACTION_SUCCESS',
'COMBINED_ACTION_ERROR'
],
// Pass actions in array
payload: [addTodo.bind(null, text), increment]
};
}
// Dispatch action
store.dispatch(addTodoAndIncrement({text:'Dispatch combined action'}));
ASYNCHRONOUS CODE
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
ASYNCHRONOUS CODE
✴ Redux manages only synchronous actions
✴ The web is based on asynchronous calls
@giorgionatili@DroidconBos
AND NOW WHAT?!?
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
MANAGING ASYNC CODE WITH REDUX
import { Component, OnInit } from '@angular/core';
import { WebApiPromiseService } from './profile.service';
@Component({
selector: 'user-profile',
templateUrl: './user-profile.component.html'
})
export class UserProfileComponent implements OnInit {
constructor(private myPromiseService: WebApiPromiseService
private profileActions: ProfileLoadingActions) {}
ngOnInit() {
this.myPromiseService
.getService('v1/user/profile')
.then(result => this.profileActions.loaded())
.catch(error => console.log(error));
}
}
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
REDUX THUNK
✴ A thunk is a function that wraps an expression to delay its
evaluation
✴ Redux-Thunk middleware allows to write action creators that
return a function instead of an action
✴ The thunk can be used to delay the dispatch of an action, or
to dispatch it only if a certain condition is met
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
INTEGRATION
import { createStore, combineReducers, applyMiddleware } from 'redux';
import combineActionsMiddleware from 'redux-combine-actions';
import thunk from 'redux-thunk';
let createStoreWithMiddleware = applyMiddleware(thunk)
(combineActionsMiddleware)(createStore);
let rootReducer = combineReducers(reducers); // Store initialization
let rootStore = createStoreWithMiddleware(rootReducer);
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
REDUX THUNK AND PROMISES
function fetchSecretSauce(): Promise {
return fetch('https://www.google.com/search?q=secret+sauce');
}
function apologize(fromPerson: string, toPerson: string, error: any) {
return { type: 'APOLOGIZE', fromPerson, toPerson, error };
}
function makeSandwich(forPerson: string, secretSauce: string) {
return { type: 'MAKE_SANDWICH', forPerson, secretSauce };
}
function makeASandwichWithSecretSauce(forPerson: string) {
return function (dispatch: any) {
return fetchSecretSauce().then(
sauce => dispatch(makeSandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
};
}
// In your action service
store.dispatch(makeASandwichWithSecretSauce('Me'));
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
WHAT’S HAPPENED
✴ A function that accepts dispatch as argument get returned
so that dispatch can be called later
✴ The thunk middleware transformed thunk async actions into
actions
DETECT STATE CHANGES
@giorgionatili@DroidconBos
THE SELECT PATTERN
✴ The select pattern allows you to get slices of your state as
RxJS observables
✴ The @select decorator can be added to the property of any
class or Angular component/injectable
✴ It will turn the property into an observable which observes
the Redux Store value which is selected by the decorator's
parameter
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
SELECTORS
// Selects `counter` from the store and attaches it to this property
@select() counter;
// Selects `counter` from the store and attaches it to this property
@select('counter') counterSelectedWithString;
// Selects `pathDemo.foo.bar` from the store and attaches it the property
@select(['pathDemo', 'foo', 'bar']) pathSelection;
// This selects `counter` from the store and attaches it to this property
@select(state => state.counter) counterSelectedWithFunction;
// Selects `counter` from the store and multiples it by two
@select(state => state.counter * 2)
counterSelectedWithFuntionAndMultipliedByTwo: Observable<any>;
@giorgionatili@DroidconBos
REUSABLE SELECTORS
✴ A reusable selector is a function that return the selector
✴ By using reusable selectors it’s possible to minimize the
refactoring effort
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
FRACTAL STORES
✴ A subStore expose the same interface as the main Redux
store (dispatch, select, etc.)
✴ A subStore is rooted at a particular path in your global state
✴ A subStore is used in components that have instance-
specific access to Redux features
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
IMPLEMENTATION
export const userComponentReducer = (state, action) =>
action.type === 'ADD_LOCATION' ?
{ ...state, location: state.location + action.payload } : state;
export class UserComponent implements NgOnInit {
name: Observable<string>;
occupation: Observable<string>;
location: Observable<string>;
private subStore: ObservableStore<User>;
constructor(private ngRedux: NgRedux<UserProfileState>) {}
onInit() {
this.subStore = this.ngRedux.configureSubStore(
['users', userId],
userComponentReducer);
// Substore selectors are scoped to the base path used to configure the substore
this.name = this.subStore.select('name');
this.occupation = this.subStore.select('occupation');
this.location = this.subStore.select(s => s.location || 0);
}
@giorgionatili@DroidconBos
PREDICTABLE STATE CONTAINERS ARE SIMPLE
PREDICTABLE STATES
In an hybrid world
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
QUICK RECAP
✴ AngularJS and Angular can coexist in the same code base
✴ It’s possible to use ui-router and the Angular routing system
together
✴ Redux is a tool to support architectures with a centralized,
independent and decouple state
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
APPLICATION LAYER
Root app Bootstrap AngularJS and Angular
Initialize the store and the initial state
Angular
route
Actions and
creators
Define the DSL of the application
Decouple the creation of the actions as injectable services
Manipulate the application stateReducers
Propagate the state of the application as an immutable objectReducers
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
COMPONENTS
✴ Responsible of defining a node in your application three
✴ Responsible of fulfilling a single use case of the application
✴ Containing container components and legacy components
✴ Connecting legacy and container components with
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
CONTAINER COMPONENTS
✴ May contain both presentational and container components
✴ Markup is minimal and mostly contains presentation
components
✴ Are stateless because represent the datasource of presentation
components
✴ Use actions to communicate the intent of changing the state
✴ Listen to slices of the state updates with selectors
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
PRESENTATION COMPONENTS
✴ Have no dependencies on the rest of the app (i.e. actions)
✴ They don’t specify how the data is loaded or mutated
✴ Receive data and callbacks exclusively via @Input()
✴ Are written as functional components with no lifecycle hooks
✴ Communicate with parents through @Output()
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
STATE CHANGE
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
FIVE THINGS YOU SHOULD DO WHEN USING REDUX
✴ Design the application state before starting to code
✴ Build independent and self-contained modules using fractal
stores
✴ Implement action creators to don't repeat yourself
✴ Use filters and selectors to listen to changes of specific slices
✴ Remove as much as possible the logic from the components
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
FIVE THINGS YOU SHOULDN’T DO WHEN USING REDUX
✴ Store in the application state redundant information
✴ Pollute and make the DSL ambiguous with not needed actions
✴ Use Redux as an event bus system
✴ Apply optimistic changes when interacting with external API
✴ Create complex reducers instead of splitting the state
@giorgionatili | @theSwiftFest | @DroidconBos#webu17
PERFORMANCES HINT: RESELECT
✴ Selectors can compute derived data, allowing Redux to store
the minimal possible state
✴ A selector is not recomputed unless one of its arguments
change
✴ Selectors are composable can be used as input to other
selectors
QUESTIONS
and potentially answers :)
““The cleaner and nicer the program, the
faster it's going to run. And if it doesn't,
it'll be easy to make it fast.”
- Joshua Bloch
THANKS!
@giorgionatili



Ad

Recommended

A gently introduction to AngularJS
A gently introduction to AngularJS
Gregor Woiwode
 
GlobalLogic Test Automation Online TechTalk “Playwright — A New Hope”
GlobalLogic Test Automation Online TechTalk “Playwright — A New Hope”
GlobalLogic Ukraine
 
AngularJS Project Setup step-by- step guide - RapidValue Solutions
AngularJS Project Setup step-by- step guide - RapidValue Solutions
RapidValue
 
How to Build ToDo App with Vue 3 + TypeScript
How to Build ToDo App with Vue 3 + TypeScript
Katy Slemon
 
Using ReactJS in AngularJS
Using ReactJS in AngularJS
Boris Dinkevich
 
jQuery plugin & testing with Jasmine
jQuery plugin & testing with Jasmine
Miguel Parramón Teixidó
 
Top 7 Angular Best Practices to Organize Your Angular App
Top 7 Angular Best Practices to Organize Your Angular App
Katy Slemon
 
React Native
React Native
Craig Jolicoeur
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java side
Mek Srunyu Stittri
 
React Nativeの光と闇
React Nativeの光と闇
Yukiya Nakagawa
 
Angular js
Angular js
Knoldus Inc.
 
Spring MVC Intro / Gore - Nov NHJUG
Spring MVC Intro / Gore - Nov NHJUG
Ted Pennings
 
Modularisation avec Gradle et Dagger
Modularisation avec Gradle et Dagger
Martin Devillers
 
Introduction to React for Frontend Developers
Introduction to React for Frontend Developers
Sergio Nakamura
 
BlackBerry DevCon 2011 - PhoneGap and WebWorks
BlackBerry DevCon 2011 - PhoneGap and WebWorks
mwbrooks
 
Angular App Presentation
Angular App Presentation
Elizabeth Long
 
How to React Native
How to React Native
Dmitry Ulyanov
 
AngularJs Crash Course
AngularJs Crash Course
Keith Bloomfield
 
React Native Androidはなぜ動くのか
React Native Androidはなぜ動くのか
Yukiya Nakagawa
 
Fullstack End-to-end test automation with Node.js, one year later
Fullstack End-to-end test automation with Node.js, one year later
Mek Srunyu Stittri
 
Introduction of React.js
Introduction of React.js
Jyaasa Technologies
 
Angular 5
Angular 5
Bartłomiej Narożnik
 
Vaadin Components
Vaadin Components
Joonas Lehtinen
 
Overview of the AngularJS framework
Overview of the AngularJS framework
Yakov Fain
 
Introducing PhoneGap to SproutCore 2
Introducing PhoneGap to SproutCore 2
mwbrooks
 
125 고성능 web view-deview 2013 발표 자료_공유용
125 고성능 web view-deview 2013 발표 자료_공유용
NAVER D2
 
AngularJS Basics and Best Practices - CC FE &UX
AngularJS Basics and Best Practices - CC FE &UX
JWORKS powered by Ordina
 
GWTcon 2015 - Beyond GWT 3.0 Panic
GWTcon 2015 - Beyond GWT 3.0 Panic
Cristiano Costantini
 
AngularJS with TypeScript and Windows Azure Mobile Services
AngularJS with TypeScript and Windows Azure Mobile Services
Rainer Stropek
 
Angular2 workshop
Angular2 workshop
Nir Kaufman
 

More Related Content

What's hot (20)

Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java side
Mek Srunyu Stittri
 
React Nativeの光と闇
React Nativeの光と闇
Yukiya Nakagawa
 
Angular js
Angular js
Knoldus Inc.
 
Spring MVC Intro / Gore - Nov NHJUG
Spring MVC Intro / Gore - Nov NHJUG
Ted Pennings
 
Modularisation avec Gradle et Dagger
Modularisation avec Gradle et Dagger
Martin Devillers
 
Introduction to React for Frontend Developers
Introduction to React for Frontend Developers
Sergio Nakamura
 
BlackBerry DevCon 2011 - PhoneGap and WebWorks
BlackBerry DevCon 2011 - PhoneGap and WebWorks
mwbrooks
 
Angular App Presentation
Angular App Presentation
Elizabeth Long
 
How to React Native
How to React Native
Dmitry Ulyanov
 
AngularJs Crash Course
AngularJs Crash Course
Keith Bloomfield
 
React Native Androidはなぜ動くのか
React Native Androidはなぜ動くのか
Yukiya Nakagawa
 
Fullstack End-to-end test automation with Node.js, one year later
Fullstack End-to-end test automation with Node.js, one year later
Mek Srunyu Stittri
 
Introduction of React.js
Introduction of React.js
Jyaasa Technologies
 
Angular 5
Angular 5
Bartłomiej Narożnik
 
Vaadin Components
Vaadin Components
Joonas Lehtinen
 
Overview of the AngularJS framework
Overview of the AngularJS framework
Yakov Fain
 
Introducing PhoneGap to SproutCore 2
Introducing PhoneGap to SproutCore 2
mwbrooks
 
125 고성능 web view-deview 2013 발표 자료_공유용
125 고성능 web view-deview 2013 발표 자료_공유용
NAVER D2
 
AngularJS Basics and Best Practices - CC FE &UX
AngularJS Basics and Best Practices - CC FE &UX
JWORKS powered by Ordina
 
GWTcon 2015 - Beyond GWT 3.0 Panic
GWTcon 2015 - Beyond GWT 3.0 Panic
Cristiano Costantini
 
Node.js and Selenium Webdriver, a journey from the Java side
Node.js and Selenium Webdriver, a journey from the Java side
Mek Srunyu Stittri
 
React Nativeの光と闇
React Nativeの光と闇
Yukiya Nakagawa
 
Spring MVC Intro / Gore - Nov NHJUG
Spring MVC Intro / Gore - Nov NHJUG
Ted Pennings
 
Modularisation avec Gradle et Dagger
Modularisation avec Gradle et Dagger
Martin Devillers
 
Introduction to React for Frontend Developers
Introduction to React for Frontend Developers
Sergio Nakamura
 
BlackBerry DevCon 2011 - PhoneGap and WebWorks
BlackBerry DevCon 2011 - PhoneGap and WebWorks
mwbrooks
 
Angular App Presentation
Angular App Presentation
Elizabeth Long
 
React Native Androidはなぜ動くのか
React Native Androidはなぜ動くのか
Yukiya Nakagawa
 
Fullstack End-to-end test automation with Node.js, one year later
Fullstack End-to-end test automation with Node.js, one year later
Mek Srunyu Stittri
 
Overview of the AngularJS framework
Overview of the AngularJS framework
Yakov Fain
 
Introducing PhoneGap to SproutCore 2
Introducing PhoneGap to SproutCore 2
mwbrooks
 
125 고성능 web view-deview 2013 발표 자료_공유용
125 고성능 web view-deview 2013 발표 자료_공유용
NAVER D2
 
AngularJS Basics and Best Practices - CC FE &UX
AngularJS Basics and Best Practices - CC FE &UX
JWORKS powered by Ordina
 
GWTcon 2015 - Beyond GWT 3.0 Panic
GWTcon 2015 - Beyond GWT 3.0 Panic
Cristiano Costantini
 

Similar to Predictable Web Apps with Angular and Redux (20)

AngularJS with TypeScript and Windows Azure Mobile Services
AngularJS with TypeScript and Windows Azure Mobile Services
Rainer Stropek
 
Angular2 workshop
Angular2 workshop
Nir Kaufman
 
Angular js
Angular js
Felixits
 
Angular js
Angular js
Felixits
 
Myths of Angular 2: What Angular Really Is
Myths of Angular 2: What Angular Really Is
DevFest DC
 
Building scalable applications with angular js
Building scalable applications with angular js
Andrew Alpert
 
Migrating from AngularJS when you can't use the word "Big Bang@
Migrating from AngularJS when you can't use the word "Big Bang@
Asim Hussain
 
AngularJS 1.x - your first application (problems and solutions)
AngularJS 1.x - your first application (problems and solutions)
Igor Talevski
 
Angular, the New Angular JS
Angular, the New Angular JS
Kenzan
 
AngularJS best-practices
AngularJS best-practices
Henry Tao
 
Angular js workshop
Angular js workshop
Rolands Krumbergs
 
Migrating From Angular 1.x to Angular 2+
Migrating From Angular 1.x to Angular 2+
Asim Hussain
 
Angular%201%20to%20angular%202
Angular%201%20to%20angular%202
Ran Wahle
 
AngularJS for Legacy Apps
AngularJS for Legacy Apps
Peter Drinnan
 
Walk in the shoe of angular
Walk in the shoe of angular
Fiyaz Hasan
 
The Angular road from 1.x to 2.0
The Angular road from 1.x to 2.0
Vassilis Pitsounis
 
Angular TS(typescript)
Angular TS(typescript)
Ivan Stepić
 
AngularJS Introduction (Talk given on Aug 5 2013)
AngularJS Introduction (Talk given on Aug 5 2013)
Abhishek Anand
 
Angular JS 2_0 BCS CTO_in_Res V3
Angular JS 2_0 BCS CTO_in_Res V3
Bruce Pentreath
 
AngularJS One Day Workshop
AngularJS One Day Workshop
Shyam Seshadri
 
AngularJS with TypeScript and Windows Azure Mobile Services
AngularJS with TypeScript and Windows Azure Mobile Services
Rainer Stropek
 
Angular2 workshop
Angular2 workshop
Nir Kaufman
 
Angular js
Angular js
Felixits
 
Angular js
Angular js
Felixits
 
Myths of Angular 2: What Angular Really Is
Myths of Angular 2: What Angular Really Is
DevFest DC
 
Building scalable applications with angular js
Building scalable applications with angular js
Andrew Alpert
 
Migrating from AngularJS when you can't use the word "Big Bang@
Migrating from AngularJS when you can't use the word "Big Bang@
Asim Hussain
 
AngularJS 1.x - your first application (problems and solutions)
AngularJS 1.x - your first application (problems and solutions)
Igor Talevski
 
Angular, the New Angular JS
Angular, the New Angular JS
Kenzan
 
AngularJS best-practices
AngularJS best-practices
Henry Tao
 
Migrating From Angular 1.x to Angular 2+
Migrating From Angular 1.x to Angular 2+
Asim Hussain
 
Angular%201%20to%20angular%202
Angular%201%20to%20angular%202
Ran Wahle
 
AngularJS for Legacy Apps
AngularJS for Legacy Apps
Peter Drinnan
 
Walk in the shoe of angular
Walk in the shoe of angular
Fiyaz Hasan
 
The Angular road from 1.x to 2.0
The Angular road from 1.x to 2.0
Vassilis Pitsounis
 
Angular TS(typescript)
Angular TS(typescript)
Ivan Stepić
 
AngularJS Introduction (Talk given on Aug 5 2013)
AngularJS Introduction (Talk given on Aug 5 2013)
Abhishek Anand
 
Angular JS 2_0 BCS CTO_in_Res V3
Angular JS 2_0 BCS CTO_in_Res V3
Bruce Pentreath
 
AngularJS One Day Workshop
AngularJS One Day Workshop
Shyam Seshadri
 
Ad

More from FITC (20)

Cut it up
Cut it up
FITC
 
Designing for Digital Health
Designing for Digital Health
FITC
 
Profiling JavaScript Performance
Profiling JavaScript Performance
FITC
 
Surviving Your Tech Stack
Surviving Your Tech Stack
FITC
 
How to Pitch Your First AR Project
How to Pitch Your First AR Project
FITC
 
Start by Understanding the Problem, Not by Delivering the Answer
Start by Understanding the Problem, Not by Delivering the Answer
FITC
 
Cocaine to Carrots: The Art of Telling Someone Else’s Story
Cocaine to Carrots: The Art of Telling Someone Else’s Story
FITC
 
Everyday Innovation
Everyday Innovation
FITC
 
HyperLight Websites
HyperLight Websites
FITC
 
Everything is Terrifying
Everything is Terrifying
FITC
 
Post-Earth Visions: Designing for Space and the Future Human
Post-Earth Visions: Designing for Space and the Future Human
FITC
 
The Rise of the Creative Social Influencer (and How to Become One)
The Rise of the Creative Social Influencer (and How to Become One)
FITC
 
East of the Rockies: Developing an AR Game
East of the Rockies: Developing an AR Game
FITC
 
Creating a Proactive Healthcare System
Creating a Proactive Healthcare System
FITC
 
World Transformation: The Secret Agenda of Product Design
World Transformation: The Secret Agenda of Product Design
FITC
 
The Power of Now
The Power of Now
FITC
 
High Performance PWAs
High Performance PWAs
FITC
 
Rise of the JAMstack
Rise of the JAMstack
FITC
 
From Closed to Open: A Journey of Self Discovery
From Closed to Open: A Journey of Self Discovery
FITC
 
Projects Ain’t Nobody Got Time For
Projects Ain’t Nobody Got Time For
FITC
 
Cut it up
Cut it up
FITC
 
Designing for Digital Health
Designing for Digital Health
FITC
 
Profiling JavaScript Performance
Profiling JavaScript Performance
FITC
 
Surviving Your Tech Stack
Surviving Your Tech Stack
FITC
 
How to Pitch Your First AR Project
How to Pitch Your First AR Project
FITC
 
Start by Understanding the Problem, Not by Delivering the Answer
Start by Understanding the Problem, Not by Delivering the Answer
FITC
 
Cocaine to Carrots: The Art of Telling Someone Else’s Story
Cocaine to Carrots: The Art of Telling Someone Else’s Story
FITC
 
Everyday Innovation
Everyday Innovation
FITC
 
HyperLight Websites
HyperLight Websites
FITC
 
Everything is Terrifying
Everything is Terrifying
FITC
 
Post-Earth Visions: Designing for Space and the Future Human
Post-Earth Visions: Designing for Space and the Future Human
FITC
 
The Rise of the Creative Social Influencer (and How to Become One)
The Rise of the Creative Social Influencer (and How to Become One)
FITC
 
East of the Rockies: Developing an AR Game
East of the Rockies: Developing an AR Game
FITC
 
Creating a Proactive Healthcare System
Creating a Proactive Healthcare System
FITC
 
World Transformation: The Secret Agenda of Product Design
World Transformation: The Secret Agenda of Product Design
FITC
 
The Power of Now
The Power of Now
FITC
 
High Performance PWAs
High Performance PWAs
FITC
 
Rise of the JAMstack
Rise of the JAMstack
FITC
 
From Closed to Open: A Journey of Self Discovery
From Closed to Open: A Journey of Self Discovery
FITC
 
Projects Ain’t Nobody Got Time For
Projects Ain’t Nobody Got Time For
FITC
 
Ad

Recently uploaded (20)

Logging and Automated Alerting Webinar.pdf
Logging and Automated Alerting Webinar.pdf
ControlCase
 
IAREUOUSTPIDWHY$)CHARACTERARERWUEEJJSKWNSND
IAREUOUSTPIDWHY$)CHARACTERARERWUEEJJSKWNSND
notgachabite123
 
BitRecover OST to PST Converter Software
BitRecover OST to PST Converter Software
antoniogosling01
 
最新版加拿大奎斯特大学毕业证(QUC毕业证书)原版定制
最新版加拿大奎斯特大学毕业证(QUC毕业证书)原版定制
taqyed
 
Lecture 3.1 Analysing the Global Business Environment .pptx
Lecture 3.1 Analysing the Global Business Environment .pptx
shofalbsb
 
Clive Dickens RedTech Public Copy - Collaborate or Die
Clive Dickens RedTech Public Copy - Collaborate or Die
Clive Dickens
 
TCP/IP presentation SET2- Information Systems
TCP/IP presentation SET2- Information Systems
agnesegtcagliero
 
Topic 2 - Cloud Computing Basics,,,.pptx
Topic 2 - Cloud Computing Basics,,,.pptx
oneillp100
 
Transmission Control Protocol (TCP) and Starlink
Transmission Control Protocol (TCP) and Starlink
APNIC
 
history of internet in nepal Class-8 (sparsha).pptx
history of internet in nepal Class-8 (sparsha).pptx
SPARSH508080
 
原版一样(ISM毕业证书)德国多特蒙德国际管理学院毕业证多少钱
原版一样(ISM毕业证书)德国多特蒙德国际管理学院毕业证多少钱
taqyed
 
ChatGPT A.I. Powered Chatbot and Popularization.pdf
ChatGPT A.I. Powered Chatbot and Popularization.pdf
StanleySamson1
 
Pitch PitchPitchPitchPitchPitchPitch.pptx
Pitch PitchPitchPitchPitchPitchPitch.pptx
157551
 
原版澳洲斯文本科技大学毕业证(SUT毕业证书)如何办理
原版澳洲斯文本科技大学毕业证(SUT毕业证书)如何办理
taqyed
 
B M Mostofa Kamal Al-Azad [Document & Localization Expert]
B M Mostofa Kamal Al-Azad [Document & Localization Expert]
Mostofa Kamal Al-Azad
 
PROCESS FOR CREATION OF BUSINESS PARTNER IN SAP
PROCESS FOR CREATION OF BUSINESS PARTNER IN SAP
AhmadAli716831
 
Paper: The World Game (s) Great Redesign.pdf
Paper: The World Game (s) Great Redesign.pdf
Steven McGee
 
Almos Entirely Correct Mixing with Apps to Voting
Almos Entirely Correct Mixing with Apps to Voting
gapati2964
 
Make DDoS expensive for the threat actors
Make DDoS expensive for the threat actors
APNIC
 
ChatGPT_and_Its_Uses_Presentationss.pptx
ChatGPT_and_Its_Uses_Presentationss.pptx
Neha Prakash
 
Logging and Automated Alerting Webinar.pdf
Logging and Automated Alerting Webinar.pdf
ControlCase
 
IAREUOUSTPIDWHY$)CHARACTERARERWUEEJJSKWNSND
IAREUOUSTPIDWHY$)CHARACTERARERWUEEJJSKWNSND
notgachabite123
 
BitRecover OST to PST Converter Software
BitRecover OST to PST Converter Software
antoniogosling01
 
最新版加拿大奎斯特大学毕业证(QUC毕业证书)原版定制
最新版加拿大奎斯特大学毕业证(QUC毕业证书)原版定制
taqyed
 
Lecture 3.1 Analysing the Global Business Environment .pptx
Lecture 3.1 Analysing the Global Business Environment .pptx
shofalbsb
 
Clive Dickens RedTech Public Copy - Collaborate or Die
Clive Dickens RedTech Public Copy - Collaborate or Die
Clive Dickens
 
TCP/IP presentation SET2- Information Systems
TCP/IP presentation SET2- Information Systems
agnesegtcagliero
 
Topic 2 - Cloud Computing Basics,,,.pptx
Topic 2 - Cloud Computing Basics,,,.pptx
oneillp100
 
Transmission Control Protocol (TCP) and Starlink
Transmission Control Protocol (TCP) and Starlink
APNIC
 
history of internet in nepal Class-8 (sparsha).pptx
history of internet in nepal Class-8 (sparsha).pptx
SPARSH508080
 
原版一样(ISM毕业证书)德国多特蒙德国际管理学院毕业证多少钱
原版一样(ISM毕业证书)德国多特蒙德国际管理学院毕业证多少钱
taqyed
 
ChatGPT A.I. Powered Chatbot and Popularization.pdf
ChatGPT A.I. Powered Chatbot and Popularization.pdf
StanleySamson1
 
Pitch PitchPitchPitchPitchPitchPitch.pptx
Pitch PitchPitchPitchPitchPitchPitch.pptx
157551
 
原版澳洲斯文本科技大学毕业证(SUT毕业证书)如何办理
原版澳洲斯文本科技大学毕业证(SUT毕业证书)如何办理
taqyed
 
B M Mostofa Kamal Al-Azad [Document & Localization Expert]
B M Mostofa Kamal Al-Azad [Document & Localization Expert]
Mostofa Kamal Al-Azad
 
PROCESS FOR CREATION OF BUSINESS PARTNER IN SAP
PROCESS FOR CREATION OF BUSINESS PARTNER IN SAP
AhmadAli716831
 
Paper: The World Game (s) Great Redesign.pdf
Paper: The World Game (s) Great Redesign.pdf
Steven McGee
 
Almos Entirely Correct Mixing with Apps to Voting
Almos Entirely Correct Mixing with Apps to Voting
gapati2964
 
Make DDoS expensive for the threat actors
Make DDoS expensive for the threat actors
APNIC
 
ChatGPT_and_Its_Uses_Presentationss.pptx
ChatGPT_and_Its_Uses_Presentationss.pptx
Neha Prakash
 

Predictable Web Apps with Angular and Redux

  • 1. PREDICTABLE WEB APPS WITH ANGULAR AND REDUX @giorgionatili
  • 2. ABOUT ME ✴ Engineering Manager at Akamai Technologies ✴ Google Developer Expert ✴ Organizer of DroidconBos (www.droidcon-boston.com) ✴ Organizer of SwiftFest 2017 (www.swiftfest.io) ✴ Founder of Mobile Tea 
 (www.mobiletea.net)
  • 6. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 AGENDA ✴ Using AngularJS within Angular applications ✴ Integrating AngularJS components into Angular ✴ Redux in a nutshell ✴ Predictable states in an hybrid world
  • 7. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 THE PROBLEMS ✴ Multiple components and services consume and fetch data from multiple sources at a not given time ✴ Multidirectional and optimistic data propagation ✴ Application state out of control and not testable
  • 8. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 TOOLS WE’LL USE + + &
  • 9. ANGULARJS & ANGULAR Using AngularJS within Angular applications
  • 10. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 WORKING WITH HYBRID ANGULAR APPS ✴ Modules built with different Angular versions should work in the same app ✴ The routing system(s) should be aware of which routes it should take care of ✴ Existing components should work in the NG4 world
  • 12. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 ANGULAR STYLE ✴ Angular apps are built as a tree of components ✴ Components are encapsulated, use@Inputs and @Outputs to pass the data in & out ✴ Angular compiles components ahead of time as part of our build process ✴ Angular is built on top of TypeScript and clearly separates the static parts of our applications (stored in decorators) from the dynamic parts (components)
  • 13. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 ANGULARJS 1.5.X+ STYLE ✴ Yo can write AngularJS applications in the Angular style ✴ AngularJS 1.5+ added angular.component and the $onInit(), $onDestroy(), and $onChanges() life-cycle events
  • 14. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 NGUPGRADE ✴ NgUpgrade is a library to use in our applications to mix and match AngularJS and Angular components ✴ It bridges the AngularJS and Angular dependency injection systems ✴ With NgUpgrade it’s possible to bootstrap an existing AngularJS application from an Angular one
  • 15. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 USING NGUPGRADE @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, UpgradeModule ] }) export class AppModule { constructor(private upgrade: UpgradeModule) {} ngDoBootstrap() { this.upgrade.bootstrap(document.body, ['AngularJsModule']); } }
  • 16. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 UPGRADEMODULE.BOOTSTRAP ✴ It makes sure angular.bootstrap runs in the right zone ✴ It adds an extra module that sets up AngularJS to be visible in Angular and vice versa ✴ It adapts the testability APIs to make Protractor work with hybrid apps
  • 18. @giorgionatili@DroidconBos COMMON ISSUES ✴ It’s not possible to automatically upgrade components and directives that use link or compile ✴ Components/directives that use scope can’t be upgraded too ✴ Also NgModel is not supported by NgUpgrade
  • 20. @giorgionatili@DroidconBos UPGRADE STRATEGIES ✴ Updating the app it route by route, screen by screen, or feature by feature (vertical slicing) ✴ Start with upgrading reusable components like inputs and date pickers (horizontal slicing)
  • 23. @giorgionatili@DroidconBos WEBPACK ✴ Most Angular 2/4 code seems to be using Webpack for building ✴ Although it’s technically possible to use other solutions like Browserify, it makes most sense to switch to Webpack to avoid going "swimming upstream" as it were
  • 24. @giorgionatili@DroidconBos OTHER TASKS RUNNER ✴ Webpack creates essentially a single bundle which you then use other plugins to split into separate files ✴ When there are tasks that didn't fit well into this model (unit tests, merging of locale files, etc.) don’t migrate them but keep them (i.e. Gulp)
  • 25. @giorgionatili@DroidconBos WEBPACK CONFIGURATION ✴ Create your configuration file by using $ eject from the angular-cli tool ✴ Separate your configuration files for the development, testing and production environments
  • 26. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 WEBPACK CONFIG FILE var webpack = require('webpack'); var webpackMerge = require('webpack-merge'); var commonConfig = require('./webpack.common.js'); //add HMR client to entries let entries = {}; for (let entryName of Object.keys(commonConfig.entry)) { entries[entryName] = commonConfig.entry[entryName].concat('webpack-hot-middleware/client? reload=true'); } let merge = webpackMerge.strategy({entry: 'replace'}); module.exports = merge(commonConfig, { devtool: 'cheap-module-inline-source-map', entry: entries, plugins: [ new webpack.NoEmitOnErrorsPlugin(), // enable HMR globally new webpack.HotModuleReplacementPlugin(), // prints more readable module names in the browser console on HMR updates new webpack.NamedModulesPlugin() ], devServer: { historyApiFallback: true, stats: 'minimal' } });
  • 27. @giorgionatili@DroidconBos HYBRID APP TEMPLATE Create two directives in your html, one is the Angular (i.e. my- app) app, the other is the AngularJS app (i.e. myangularjs- app-root) <myangularjs-app-root></myangularjs-app-root> <my-app></my-app>
  • 28. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 BOOTSTRAPPING ANGULAR 4 ✴ Angular is bootstrapped first through: platformBrowserDynamic().bootstrapModule(AppMo dule); ✴ In the app module explicitly say to use your AppComponent to bootstrap the main component bootstrap: [AppComponent]
  • 29. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 BOOTSTRAPPING ANGULAR 1 In the constructor of the app module bootstrap AngularJS to prevent the $injector not found error constructor(private upgrade: UpgradeModule) {
 upgrade.bootstrap(document.documentElement, ['angular-js-app'], {strictDi: true}); }
  • 30. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 INJECTING THE SCOPE To be sure the scope is injected into Angular, use a provider to simply get the $scope into the AppModule const angularjsScopeProvider: FactoryProvider = { provide: '$scope', useFactory: ($injector: any) => $injector.get('$rootScope'), deps: ['$injector'] };
  • 31. @giorgionatili@DroidconBos DUAL ROUTING ✴ It’s possible to run simultaneously different routing systems ✴ To get ui-router and the Angular one working together, it’s enough to tell each router to ignore the other's routes
  • 32. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 TELLING UI-ROUTER TO IGNORE OUR ANGULAR4 ROUTE export class YourAppRootController { constructor($rootScope, $location) { 'ngInject'; Object.assign(this, {$rootScope, $location}); this.ng1Route = true; $rootScope.$on('$locationChangeSuccess', this.handleStateChange.bind(this)); } handleStateChange() { // If the url starts with /ng4-route this will be handled by NG4 
 // and we should hide the ui-view this.ng1Route = !this.$location.url().startsWith(‘/ng4-route‘); } }
  • 33. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 HIDING THE UI-VIEW WHEN NOT ON A NG1 ROUTE export class AngularJSRootController { constructor($rootScope, $location) { 'ngInject'; Object.assign(this, {$rootScope, $location}); this.ng1Route = true; $rootScope.$on('$locationChangeSuccess', this.handleStateChange.bind(this)); } handleStateChange() { //if the url starts with /ng4-route it should hide the ui-view) this.ng1Route = !this.$location.url().startsWith(‘/ng4-route'); } }
  • 36. @giorgionatili@DroidconBos UPGRADING ISSUES ✴ There are a few things that will prevent a directive or component from being upgraded: ✴ link function ✴ scope ✴ Requiring NgModel or any other controller on the directive itself (or a parent)
  • 37. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 COMPONENT WRAPPER ✴ There's no support for components that use NgModel in upgradecomponent as of today ✴ To work around this, a possible solution, is implementing a wrapper component
  • 38. @giorgionatili@DroidconBos WRAPPER RESPONSIBILITIES ✴ Acting as a bridge and using ng-model to put values into the old component ✴ Listening to the ng-change events and bubbling up values from the wrapper in an EventEitter ✴ Exposing and using the Angular methods as needed
  • 39. @giorgionatili@DroidconBos WRAPPER AND FORMS ✴ Angular offers a great variety of Forms validators ✴ When a component doesn’t have its own validation, using a dummy form in the wrapper expose all the available Angular validators
  • 40. @giorgionatili@DroidconBos WRAPPER CONTROLLER ✴ This is a class to assist in writing a controller for a AngularJS component that wraps something using NgModel ✴ It handles passing through validation etc. ✴ The Component must be wrapped in a ng-form with name $ctrl.model to work properly
  • 41. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 WRAPPING AN EXISTING COMPONENT import {IAngularStatic} from 'angular'; import {AbstractNgModelWrapperController} from './abstract-ng-model-wrapper.controller'; declare var angular: IAngularStatic; class YourWrapperController extends AbstractNgModelWrapperController { private collection: any[]; public writeValue(obj: any): void { this.collection = obj; } public handleChange() { this.onChange(this.collection); this.onBlur(); this.onValidatorChange(); } } const yourWrapperComponent = { bindings: { collection: '<' }, controller: YourWrapperController, template: `<ng-form name="$ctrl.form"> <your-legcy-selector ng-model="collection"> </your-legacy-selector> </ng-form>` }; angular.module('your-app-name').component('yourComponenWrapper', yourWrapperComponent);
  • 42. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 NGMODELWRAPPERCONTROLLER import {ControlValueAccessor, Validator} from '@angular/forms'; export abstract class NgModelWrapperController implements ControlValueAccessor, Validator { public isDisabled: boolean = false; protected onChange: any; protected onBlur: any; protected onValidatorChange: () => void; public abstract writeValue(obj: any): void; public registerOnChange(fn: any): void { this.onChange = fn; } public registerOnTouched(fn: any): void { this.onBlur = fn; } public registerOnValidatorChange(fn: () => void): void { this.onValidatorChange = fn; } }
  • 43. @giorgionatili@DroidconBos UPGRADING THE COMPONENT ✴ Creating an Angular directive that wraps the upgraded component ✴ Expose the @Input() and @Output() needed to communicate with the Angular “world”
  • 44. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 UPGRADING THE COMPONENT TO A DIRECTIVE import { Directive, ElementRef, Injector, Input } from '@angular/core'; import { NgModelWrapperUpgradeComponent } from './ng-model-wrapper-upgrade.component'; @Directive({ selector: ‘your-directive-selector‘ }) export class YourDirective extends NgModelWrapperUpgradeComponent { 
 @Input() public collection: any[]; constructor(elementRef: ElementRef, injector: Injector) { super('yourComponentWrapper', elementRef, injector); } }
  • 45. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 NGMODELWRAPPERUPGRADECOMPONENT import {UpgradeComponent} from '@angular/upgrade/static'; import {AbstractControl, ControlValueAccessor, ValidationErrors, Validator} from '@angular/forms'; export abstract class NgModelWrapperUpgradeComponent extends UpgradeComponent implements ControlValueAccessor, Validator { public writeValue(obj: any): void { return (this as any).controllerInstance.writeValue(obj); } public registerOnChange(fn: any): void { return (this as any).controllerInstance.registerOnChange(fn); } public registerOnTouched(fn: any): void { return (this as any).controllerInstance.registerOnTouched(fn); } public setDisabledState(isDisabled: boolean): void { return (this as any).controllerInstance.setDisabledState(isDisabled); } public validate(c: AbstractControl): ValidationErrors { return (this as any).controllerInstance.validate(c); } public registerOnValidatorChange(fn: () => void): void { return (this as any).controllerInstance.registerOnValidatorChange(fn); } }
  • 48. REDUX IN A NUTSHELL Predictably and Composability
  • 49. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 DEFINITION Redux holds all the state of your application. It doesn't let you change that state directly, but instead forces you to describe changes as plain objects called “actions".
  • 50. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 CORE CONCEPTS ✴ A global immutable application state ✴ Unidirectional data-flow ✴ Changes to state are made in pure functions, or reducers
  • 51. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 REDUX BUILDING BLOCKS ✴ Store, a centralized representation of the state of an app ✴ Reducers, pure functions that return a new representation of the state ✴ Actions, POJOs that represent a change in the state of the app ✴ Middleware, a chain-able function used to extend Redux
  • 52. @giorgionatili@DroidconBos ACTIONS IN A NUTSHELL ✴ Actions are payloads of information that send data to the application store ✴ By convention, they have a type attribute that indicates what kind of action we are performing ✴ Flux standard actions also have a payload attribute used to propagate the data needed to determine the next state
  • 53. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 ACTIONS CREATORS ✴ Action creators are function that create actions ✴ They do not dispatch actions to the store, making them portable and easier to test as they have no side-effects
  • 54. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 HOW AN ACTION LOOKS LIKE { type: 'LOADED', payload: { text: 'Do something.' } }
  • 55. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 ACTIONS SERVICES ✴ Encapsulating related-actions dispatchers in the same file promote code reuse and organization ✴ Making them @Injectable() allow to use them within the dependency injection system of Angular
  • 56. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 ACTIONS SERVICES import { Injectable } from '@angular/core'; import { NgRedux } from 'ng2-redux'; import { UserProfileState } from '../model/profile-state'; @Injectable() export class ProfileLoadingActions { static LOADED: string = 'PROFILE_LOADED'; constructor(private ngRedux: NgRedux<UserProfileState>) {} loaded(): void { this.ngRedux.dispatch({ type: ProfileLoadingActions.LOADED }); } }
  • 57. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 REDUCERS AS PURE FUNCTIONS ✴ Reducers are pure functions that, given an input, return always the same output as a fresh representation of a slice of the app state ✴ When the store needs to know how an action changes the state, it asks the reducers
  • 58. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 HOW A REDUCER LOOKS LIKE import { ProfileLoadingActions } from '../../actions/loading-actions'; import { INITIAL_STATE } from './loading-initial-state'; export function isLoadingReducer(state: boolean = INITIAL_STATE, action: any): boolean { switch (action.type) { case ProfileLoadingActions.LOADED: return true; case ProfileLoadingActions.UPDATED: return true; case ProfileLoadingActions.UPDATE: return false; case ProfileLoadingActions.NOT_LOADED: return false; default: return state; } }
  • 59. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 DESIGNING THE STORE ✴ The store is not a specular copy of the application model ✴ The store represents the information needed to properly render the UI elements of a web application
  • 60. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 COMBINE REDUCERS ✴ The most common state shape for a Redux app is a POJO containing slices of the model at each top-level key ✴ combineReducers takes an object full of slice reducer functions, and returns a new reducer function ✴ It is a utility function to simplify the most common use case of combining multiple reducers to describe the app state
  • 61. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 COMBINING REDUCERS IN ACTION import { loadingReducer as isLoading } from './loading/loading.reducer.ts'; import { errorReducer as error } from './error/error.reducer.ts'; export const rootReducer = combineReducers({ isLoading, error, });
  • 63. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 COMBINE ACTIONS ✴ Combining actions together can be a nice way to simplify the application state ✴ Executing in sequence or in parallel multiple actions can help the semantic of your code ✴ There is a middleware (redux-combine-actions) to easy combine async actions and dispatch them
  • 64. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 INTEGRATION import { createStore, combineReducers, applyMiddleware } from 'redux'; import combineActionsMiddleware from 'redux-combine-actions'; let createStoreWithMiddleware = applyMiddleware(combineActionsMiddleware)(createStore); let rootReducer = combineReducers(reducers); // Store initialization let rootStore = createStoreWithMiddleware(rootReducer);
  • 65. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 USING REDUX-COMBINE-ACTIONS export function addTodo(text) { return { type: 'ADD_TODO', text }; } export function increment() { return { type: 'INCREMENT_COUNTER' }; } // Combine "addTodo" and "increment" actions export function addTodoAndIncrement({text}) { return { types: [ 'COMBINED_ACTION_START', 'COMBINED_ACTION_SUCCESS', 'COMBINED_ACTION_ERROR' ], // Pass actions in array payload: [addTodo.bind(null, text), increment] }; } // Dispatch action store.dispatch(addTodoAndIncrement({text:'Dispatch combined action'}));
  • 67. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 ASYNCHRONOUS CODE ✴ Redux manages only synchronous actions ✴ The web is based on asynchronous calls
  • 69. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 MANAGING ASYNC CODE WITH REDUX import { Component, OnInit } from '@angular/core'; import { WebApiPromiseService } from './profile.service'; @Component({ selector: 'user-profile', templateUrl: './user-profile.component.html' }) export class UserProfileComponent implements OnInit { constructor(private myPromiseService: WebApiPromiseService private profileActions: ProfileLoadingActions) {} ngOnInit() { this.myPromiseService .getService('v1/user/profile') .then(result => this.profileActions.loaded()) .catch(error => console.log(error)); } }
  • 70. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 REDUX THUNK ✴ A thunk is a function that wraps an expression to delay its evaluation ✴ Redux-Thunk middleware allows to write action creators that return a function instead of an action ✴ The thunk can be used to delay the dispatch of an action, or to dispatch it only if a certain condition is met
  • 71. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 INTEGRATION import { createStore, combineReducers, applyMiddleware } from 'redux'; import combineActionsMiddleware from 'redux-combine-actions'; import thunk from 'redux-thunk'; let createStoreWithMiddleware = applyMiddleware(thunk) (combineActionsMiddleware)(createStore); let rootReducer = combineReducers(reducers); // Store initialization let rootStore = createStoreWithMiddleware(rootReducer);
  • 72. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 REDUX THUNK AND PROMISES function fetchSecretSauce(): Promise { return fetch('https://www.google.com/search?q=secret+sauce'); } function apologize(fromPerson: string, toPerson: string, error: any) { return { type: 'APOLOGIZE', fromPerson, toPerson, error }; } function makeSandwich(forPerson: string, secretSauce: string) { return { type: 'MAKE_SANDWICH', forPerson, secretSauce }; } function makeASandwichWithSecretSauce(forPerson: string) { return function (dispatch: any) { return fetchSecretSauce().then( sauce => dispatch(makeSandwich(forPerson, sauce)), error => dispatch(apologize('The Sandwich Shop', forPerson, error)) ); }; } // In your action service store.dispatch(makeASandwichWithSecretSauce('Me'));
  • 73. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 WHAT’S HAPPENED ✴ A function that accepts dispatch as argument get returned so that dispatch can be called later ✴ The thunk middleware transformed thunk async actions into actions
  • 75. @giorgionatili@DroidconBos THE SELECT PATTERN ✴ The select pattern allows you to get slices of your state as RxJS observables ✴ The @select decorator can be added to the property of any class or Angular component/injectable ✴ It will turn the property into an observable which observes the Redux Store value which is selected by the decorator's parameter
  • 76. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 SELECTORS // Selects `counter` from the store and attaches it to this property @select() counter; // Selects `counter` from the store and attaches it to this property @select('counter') counterSelectedWithString; // Selects `pathDemo.foo.bar` from the store and attaches it the property @select(['pathDemo', 'foo', 'bar']) pathSelection; // This selects `counter` from the store and attaches it to this property @select(state => state.counter) counterSelectedWithFunction; // Selects `counter` from the store and multiples it by two @select(state => state.counter * 2) counterSelectedWithFuntionAndMultipliedByTwo: Observable<any>;
  • 77. @giorgionatili@DroidconBos REUSABLE SELECTORS ✴ A reusable selector is a function that return the selector ✴ By using reusable selectors it’s possible to minimize the refactoring effort
  • 78. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 FRACTAL STORES ✴ A subStore expose the same interface as the main Redux store (dispatch, select, etc.) ✴ A subStore is rooted at a particular path in your global state ✴ A subStore is used in components that have instance- specific access to Redux features
  • 79. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 IMPLEMENTATION export const userComponentReducer = (state, action) => action.type === 'ADD_LOCATION' ? { ...state, location: state.location + action.payload } : state; export class UserComponent implements NgOnInit { name: Observable<string>; occupation: Observable<string>; location: Observable<string>; private subStore: ObservableStore<User>; constructor(private ngRedux: NgRedux<UserProfileState>) {} onInit() { this.subStore = this.ngRedux.configureSubStore( ['users', userId], userComponentReducer); // Substore selectors are scoped to the base path used to configure the substore this.name = this.subStore.select('name'); this.occupation = this.subStore.select('occupation'); this.location = this.subStore.select(s => s.location || 0); }
  • 81. PREDICTABLE STATES In an hybrid world
  • 82. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 QUICK RECAP ✴ AngularJS and Angular can coexist in the same code base ✴ It’s possible to use ui-router and the Angular routing system together ✴ Redux is a tool to support architectures with a centralized, independent and decouple state
  • 83. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 APPLICATION LAYER Root app Bootstrap AngularJS and Angular Initialize the store and the initial state Angular route Actions and creators Define the DSL of the application Decouple the creation of the actions as injectable services Manipulate the application stateReducers Propagate the state of the application as an immutable objectReducers
  • 84. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 COMPONENTS ✴ Responsible of defining a node in your application three ✴ Responsible of fulfilling a single use case of the application ✴ Containing container components and legacy components ✴ Connecting legacy and container components with
  • 85. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 CONTAINER COMPONENTS ✴ May contain both presentational and container components ✴ Markup is minimal and mostly contains presentation components ✴ Are stateless because represent the datasource of presentation components ✴ Use actions to communicate the intent of changing the state ✴ Listen to slices of the state updates with selectors
  • 86. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 PRESENTATION COMPONENTS ✴ Have no dependencies on the rest of the app (i.e. actions) ✴ They don’t specify how the data is loaded or mutated ✴ Receive data and callbacks exclusively via @Input() ✴ Are written as functional components with no lifecycle hooks ✴ Communicate with parents through @Output()
  • 87. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 STATE CHANGE
  • 88. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 FIVE THINGS YOU SHOULD DO WHEN USING REDUX ✴ Design the application state before starting to code ✴ Build independent and self-contained modules using fractal stores ✴ Implement action creators to don't repeat yourself ✴ Use filters and selectors to listen to changes of specific slices ✴ Remove as much as possible the logic from the components
  • 89. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 FIVE THINGS YOU SHOULDN’T DO WHEN USING REDUX ✴ Store in the application state redundant information ✴ Pollute and make the DSL ambiguous with not needed actions ✴ Use Redux as an event bus system ✴ Apply optimistic changes when interacting with external API ✴ Create complex reducers instead of splitting the state
  • 90. @giorgionatili | @theSwiftFest | @DroidconBos#webu17 PERFORMANCES HINT: RESELECT ✴ Selectors can compute derived data, allowing Redux to store the minimal possible state ✴ A selector is not recomputed unless one of its arguments change ✴ Selectors are composable can be used as input to other selectors
  • 92. ““The cleaner and nicer the program, the faster it's going to run. And if it doesn't, it'll be easy to make it fast.” - Joshua Bloch