Top Banner
HOW ANGULAR CAN SOLVE ALL YOUR PROBLEMS Nir Kaufman
56

Solid angular

Jan 12, 2017

Download

Technology

Nir Kaufman
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
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]