solid angular

56
HOW ANGULAR CAN SOLVE ALL YOUR PROBLEMS Nir Kaufman

Upload: nir-kaufman

Post on 12-Jan-2017

1.850 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Solid angular

HOW ANGULAR CAN SOLVE ALL YOUR PROBLEMS

Nir Kaufman

Page 2: Solid angular

IT CAN’T

Page 3: Solid angular

Nir Kaufman

- I don’t really need glasses to see - This photo is a photoshop - In reality I’m in color (and fatter)

Head of AngularJS Development @ 500Tech

Page 4: Solid angular

INTRODUCTION

Page 5: Solid angular

“We are not happy with our app. it should be modular, easy to extend and maintain. It’s hard to understand the flow, feels like a spaghetti of presentation and business logic.

- frontend team at {{ company.name }}

Page 6: Solid angular

WE NEED A BETTER FRAMEWORK

Page 7: Solid angular
Page 8: Solid angular
Page 9: Solid angular

WHAT DO WE NEED?

Page 10: Solid angular

COMPONENTS

Page 11: Solid angular

A clean way of organizing your UI code into self-contained, reusable chunks

Page 12: Solid angular

// component controller class likeBoxController { constructor(params) { this.chosenValue = params.value; } like() { this.chosenValue('like'); } dislike() { this.chosenValue('dislike'); } } // component definition function likeBoxComponent() { return { viewModel: likeBoxController, template: likeBoxTemplate } } // component registration ko.components.register('like-widget', likeBoxComponent());

Page 13: Solid angular

// component controller class likeBoxController { constructor() { this.chosenValue = null; } like() { this.chosenValue = 'like'; } dislike() { this.chosenValue = 'dislike'; } } // component definition function likeBoxComponent() { return { controller: likeBoxController, scope: { params: ‘=chosenValue' }, controllerAs: 'LikeBox', bindToController: true, template: likeBoxTemplate } } angular.module('app', []) .directive('likeWidget', likeBoxComponent);

Page 14: Solid angular

<div class="like-or-dislike" data-bind="visible: !chosenValue()"> <button data-bind="click: like">Like it</button> <button data-bind="click: dislike">Dislike it</button> </div> <div class="result" data-bind="visible: chosenValue"> You <strong data-bind="text: chosenValue"></strong> it </div>

<div class="like-or-dislike" ng-hide="LikeBox.chosenValue"> <button ng-click="LikeBox.like()">Like it</button> <button ng-click="LikeBox.dislike()">Dislike it</button> </div> <div class="result" ng-show="LikeBox.chosenValue"> You <strong ng-bind="LikeBox.chosenValue"></strong> it </div>

Page 15: Solid angular

class LikeWidget extends React.Component { constructor(props) { super(props); this.state = { chosenValue: null }; this.like = this.like.bind(this); this.dislike = this.dislike.bind(this); this._likeButtons = this._likeButtons.bind(this) } like() { this.setState({ chosenValue: 'like' }) } dislike() { this.setState({ chosenValue: 'dislike' }) } _likeButtons() { if (this.state.chosenValue) { return null } return ( <div> <button onClick={this.like}>Like it</button> <button onClick={this.dislike}>Dislike it</button> </div> ) } render() { return ( <div> { this._likeButtons() } { this.state.chosenValue ? <div>You <strong>{ this.state.chosenValue }</strong> it </div> : null} </div> ) } } React.render(<LikeWidget/>, document.getElementById('app'));

Page 16: Solid angular

MVW PATTERN

Page 17: Solid angular

Keep your business logic separate from your user interface

Page 18: Solid angular

class MyViewModel { constructor() { this.products = [ new Product('Garlic bread'), new Product('Pain au chocolat'), new Product('Seagull spaghetti', 'like') ]; } } ko.applyBindings(new MyViewModel());

class MyViewModel { constructor() { this.products = [ new Product('Garlic bread'), new Product('Pain au chocolat'), new Product('Seagull spaghetti', 'like') ]; } } angular.module('app', []) .controller('MyViewModel', MyViewModel);

Page 19: Solid angular

Backbone.Model.extend({ defaults: { coverImage: 'img/placeholder.png', title: 'No title', author: 'Unknown', releaseDate: 'Unknown', keywords: 'None' } });

DS.Model.extend({ title: DS.attr('No title'), author: DS.attr('Unknown'), releaseDate: DS.attr('Unknown'), keywords: DS.attr('None') });

class Book { constructor() { this.coverImage = 'img/placeholder.png'; this.title = 'No title'; this.author = 'Unknown'; this.releaseDate = 'Unknown'; this.keywords = 'None'; } }

Page 20: Solid angular

LETS GET TO THE POINT

Page 21: Solid angular

All major frameworks introduce the same concepts.

Don’t make a switch for the wrong reasons. Switching to another framework won’t solve your design problems.

Page 22: Solid angular

OBJECT ORIENTED PROGRAMMING

Page 23: Solid angular

CONSIDER TYPESCRIPT

I used to hate it…

Page 24: Solid angular

SOLID PRINCIPLESSingle Responsibility

Open / Closed

Liskov Substitution

Interface Segregation

Dependency Inversion

Page 25: Solid angular

S.O.L.I.DSingle Responsibility Principle

Page 26: Solid angular

A module should have one, and only one reason to change

Page 27: Solid angular

// module declarations angular.module('app', [ 'ui.router', 'LocalStorageModule', 'app.states' ]) .config(($stateProvider, $httpProvider, localStorageServiceProvider) => { // start routing $stateProvider .state('dashboard', { url: '/dashboard', templateUrl: 'states/dashboard/dashboard.html', controller: 'DashboardController' }) .state('users', { url: '/users', templateUrl: 'states/users/users.html', controller: 'UsersController' }); // http configuration $httpProvider.useApplyAsync(true); $httpProvider.useLegacyPromiseExtensions(false); $httpProvider.interceptors.push(($log) => { return { 'request': function (config) { $log.debug(config); return config; } }; }); // storage configurations localStorageServiceProvider .setPrefix('myApp') .setStorageType('sessionStorage') }); // start engines angular.bootstrap(document, ['app']);

4 reasons to change this module:

add dependency add new state configure http service configure storage service

Page 28: Solid angular

// module declarations angular.module('app', [ 'ui.router', 'LocalStorageModule', 'app.states' ]) .config(routes) .config(http) .config(storage) // start engines angular.bootstrap(document, ['app']);

export function routes($stateProvider) { $stateProvider .state('dashboard', { url: '/dashboard', templateUrl: 'states/dashboard/dashboard.html', controller: 'DashboardController' }) .state('users', { url: '/users', templateUrl: 'states/users/users.html', controller: 'UsersController' }); }

routes.ts

app.ts

export function http ($httpProvider) { $httpProvider.useApplyAsync(true); $httpProvider.useLegacyPromiseExtensions(false); }

http.ts

export function storage(localStorageServiceProvider) { localStorageServiceProvider .setPrefix('myApp') .setStorageType('sessionStorage') }

storage.ts

Page 29: Solid angular

// module declarations angular.module('app', [ 'ui.router', 'LocalStorageModule', 'app.states' ]) .config(routes) .config(http) .config(storage) // start engines angular.bootstrap(document, ['app']);

Are we there yet?

2 reasons to change

// module declarations angular.module('app', [ 'ui.router', 'LocalStorageModule', 'app.states' ]) // start engines angular.bootstrap(document, ['app']);

1 responsibility

Page 30: Solid angular

S.O.L.I.DOpen / Closed Principle

Page 31: Solid angular

A module should be open for extension, but closed for modification.

Page 32: Solid angular

class Modal { constructor($modal) { this.modal = $modal; } show(type) { switch (type) { case 'login': this.showLoginModal(); break; case 'info': this.showInfoModal(); break; } } showLoginModal() { this.modal.open({ template: 'loginModal.html', controller: ‘loginModalController’ }) } showInfoModal() { this.modal.open({ template: 'infoModal.html', controller: 'infoModalController' }) } }

class Controller { constructor(Modal) { this.Modal = Modal; } showLogin(){ this.Modal.show('login'); } }

We need to add new Modals to our system

Page 33: Solid angular

class Modal { constructor($modal) { this.modal = $modal; this.modals = new Map(); } register(type, config) { this.modals.set(type, config) } $get() { return { show: this.show } } show(type) { if(this.modals.has(type)){ this.modal.open(this.modals.get(type)) } } }

angular.module('app', []) .config(ModalProvider => { ModalProvider.register('lostPassword', { template: 'lostPassword.html', controller: 'lostPasswordController' }) });

class Controller { constructor(Modal) { this.Modal = Modal; } showLogin(){ this.Modal.show('lostPassword'); } }

Write code that can be extended

Page 34: Solid angular

S.O.L.I.DLiskov Substitution Principle

Page 35: Solid angular

Child classes should never break the parent class type definitions

Page 36: Solid angular

IT’S ABOUT INHERITANCE

Page 37: Solid angular

DON’T DO IT

Page 38: Solid angular

S.O.L.I.DInterface Segregation Principle

Page 39: Solid angular

Many specific interfaces are better than one generic interface

Page 40: Solid angular

I just want to make a copy

Page 41: Solid angular

class LocalStorage { private storage; private $window; constructor($window, $q) { this.$window = $window; } setStorageType(type:string) { if (type === 'local') { return this.storage = this.$window.localStorage; } if (type === 'db') { return this.storage = new PouchDB('DB'); } } setLocalItem(key:string, data) { if (this.db) { return this.db.put(JSON.parse(data)) } return this.storage.setItem(key, JSON.stringify(data)); }

put(data) { this.storage.put(JSON.parse(data)) }

getLocalItem(key:string):string { let deferred = this.$q.defer(); if (this.db) { this.db.get(key).then( result => deferred.resolve() ) } deferred.resolve(this.storage.getItem()); return deferred.promise; } }

No client should be forced to depend on methods it doesn’t use

Page 42: Solid angular

class LocalStorage { private storage; constructor($window) { this.storage = $window.localStorage; } setItem(key:string, data) { return this.storage.setItem(key, JSON.stringify(data)); } getItem(key:string):string { return this.storage.getItem(); } }

class SessionStorage { private storage; constructor($window, $q) { this.storage = $window.sessionStorage; } setItem(key:string, data) { return this.storage.setItem(key, JSON.stringify(data)); } getItem(key:string):string { return this.storage.getItem(); } }

localStorage.ts

sessionStorage.ts

class DBStorage { private db; constructor(PouchDB) { this.db = new PouchDB('DB'); } put(data) { this.db.put(data) } get(id){ return this.db.get(id); } }

dbStorage.ts

UserComponent.ts (client)class UserComponent { private storage; constructor(LocalStorage) { this.storage = LocalStorage } }

Page 43: Solid angular

S.O.L.I.DDependency Inversion Principle

Page 44: Solid angular

High-level modules should not depend on low-level modules. Both should depend on abstractions.

Page 45: Solid angular

NATIVE API’s

ANGULAR

3RD PARTY MODULES

APLLICATION CODE

INTERFACES

Application Layers

YOUR MODULES

Abstraction

Abstraction

Page 46: Solid angular

interface IStorage { setItem(key:string, data:any); getItem(key:string, data:any); }

IStorgae.ts UserComponent.ts (client)class UserComponent { private storage; constructor(Storage: IStorage) { this.storage = LocalStorage } }

LocalStorage.ts

class LocalStorage implements IStorage { private storage; constructor($window) { this.storage = $window.localStorage; } setItem(key:string, data) { return this.storage.setItem(key, JSON.stringify(data)); } getItem(key:string):string { return this.storage.getItem(); } }

The ‘client’ can work with any kind of storage that implements the IStorage interface

Page 47: Solid angular

export class WelcomeController { constructor($modal) { this.$modal = $modal; } login() { let loginModalInstance = this.$modal.open({ templateUrl: 'states/welcome/login_modal.html', keyboard: false, backdrop: 'static', controller: LoginModalController, controllerAs: 'Login' }); loginModalInstance.result .then(result => console.log(result)) } }

Angular Bootstrap Modal

export class DashboardController { constructor($modal) { this.$modal = $modal; } showInfo() { let infoModalInstance = this.$modal.open({ templateUrl: 'states/dashboard/info_modal.html', keyboard: false, backdrop: 'static', controller: InfoModalController, controllerAs: 'Info' }); infoModalInstance.result .then(result => console.log(result)) } }

What is the problem?

Page 48: Solid angular

class Modal { constructor($modal) { this.modal = $modal; this.modals = new Map(); } register(type, config) { this.modals.set(type, config) } $get() { return { show: this.show } } show(type) { if(this.modals.has(type)){ this.modal.open(this.modals.get(type)) } } }

Abstraction without TypeScript

Page 49: Solid angular

SUMMARY

Page 50: Solid angular

DON’T MAKE A SWITCH FOR THE WRONG

REASONS

Page 51: Solid angular

DESIGN PATTENS

MATTER

Page 52: Solid angular

GOOD DESIGN IS FRAMEWORK

AGNOSTIC

Page 53: Solid angular

THANK YOU!

Page 54: Solid angular

Angular ES6 / TypeScript Startershttps://github.com/nirkaufman/angular-webpack-starter

https://github.com/nirkaufman/angular-webpack-typescript-starter

Page 55: Solid angular

resourceshttps://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design

http://code.tutsplus.com/series/the-solid-principles--cms-634

http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

Page 56: Solid angular

Read our blog:http://blog.500tech.com

Nir [email protected]