Post on 15-Apr-2017
NIR KUFMN
Nir Kaufman
- Doing Angular for years - Wrote a book about Angular2 - Play the electric Bass
Head of Angular Development @ 500Tech
*This picture have been retouched the actual speaker may look different
I AM NOT GOING TO TALK ABOUT- TypeScript
- Angular2 upgrade module
- Ng-forward library
- Data flow (Redux, RxJs)
- Angular 1.5 new Component API
I WILL TALK ABOUT- Embracing Modules
- Using Classes
- Decoupling from Framework API
- Components as UI building blocks
https://github.com/nirkaufman/ng1-coffee-shop
GRAB THE CODE
http://tinyurl.com/hdmqrem
USE A MODULE LOADERIntegrate a module bundler and an ES6 compiler to use javaScript modules.
http://webpack.github.io/
module.exports = { entry: 'index.js', // configure loaders module: { loaders: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel", query: {presets: ['es2015', 'stage-1']} }, // load css as inline styles {test: /\.css$/, loader: "style!css"} ] } };
new webpack.config.js
<script src="bundle.js"></script>
old index.html
<!-- load core scripts --> <script src="../node_modules/angular/angular.js"></script> <script src="../node_modules/angular-ui-router/release/angular-ui-router.js"></script> <script src="index.js"></script> <!-- load services --> <script src="services/logger.factory.js"></script> <script src="services/storage.service.js"></script> <script src="services/orders.service.js"></script> <!-- load filters --> <script src="filters/paid-orders.filter.js"></script> <!-- load directives --> <script src="directives/simple-table/simple-table.js"></script> <!-- load controllers --> <script src="states/home.controller.js"></script> <script src="states/log.controller.js"></script>
<body><script src=“bundle.js"></script></body>
new index.html
import 'angular'; import 'angular-ui-router'; import './assets/index.css'; import services from './services/services.module'; import filters from './filters/filters.module' import directives from './directives/directives.module'; import states from './states/state.module';
new index.js
old log.controller.js
(function (angular) { angular.module('app') .controller('LogController', function ($scope, $window, Orders, Storage) { $scope.orders = Orders.getOrders(); $scope.clearHistory = function () { if ($scope.orders.length === 0) { return; } var result = $window.confirm("Clear History?"); if (result) { Storage.clear(); $scope.orders = []; } } }) }(window.angular));
new log.controller.js
export function LogController ($scope, $window, Orders, Storage) { $scope.orders = Orders.getOrders(); $scope.clearHistory = function () { if ($scope.orders.length === 0) { return; } var result = $window.confirm("Clear History?"); if (result) { Storage.clear(); $scope.orders = []; } } }
old storage.service.js
(function (angular) { angular.module('app') .service('Storage', function () { var ORDERS_KEY = "ORDERS"; this.store = localStorage; this.getOrders = function () { return JSON.parse(this.store.getItem(ORDERS_KEY)) || []; }; this.saveOrders = function (orders) { this.store.setItem(ORDERS_KEY, JSON.stringify(orders)) }; this.clear = function () { this.store.clear(); } })}(window.angular));
new storage.service.js
export function Storage () { var ORDERS_KEY = "ORDERS"; this.store = localStorage; this.getOrders = function () { return JSON.parse(this.store.getItem(ORDERS_KEY)) || []; }; this.saveOrders = function (orders) { this.store.setItem(ORDERS_KEY, JSON.stringify(orders)) }; }
import {Storage} from './storage.service'; export default angular.module('services', []) .service('Storage', Storage)
new service.module.js
old index.js
(function (angular) { angular.module('app', ['ui.router']) .config(function ($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise("/"); $stateProvider .state('home', { url: '/', templateUrl: 'templates/home.html', controller: 'HomeController' }) .state('log', { url: '/log', templateUrl: 'templates/log.html', controller: 'LogController' }) });}(window.angular)
new index.js
import 'angular'; import 'angular-ui-router'; import ‘./assets/index.css';import services from './services/services.module'; import filters from './filters/filters.module' import directives from './directives/directives.module'; import states from './states/state.module'; import {routes} from './config/routes'; angular.module('app', [ 'ui.router', services.name, directives.name, filters.name, states.name]).config(routes); // bootstrap angular angular.element(document).ready(function () { angular.bootstrap(document, ['app']); });
new routes.js
export function routes($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise("/"); // configure application routes $stateProvider .state('home', { url: '/', templateUrl: 'templates/home.html', controller: 'HomeController' }) .state('log', { url: '/log', templateUrl: 'templates/log.html', controller: 'LogController' }) }
MOVE FORWARD TO ES6You can adopt the new syntax slowly. Start by using classes instead of functions.
http://babeljs.io/
old storage.service.js
export function Storage () { var ORDERS_KEY = "ORDERS"; this.store = localStorage; this.getOrders = function () { return JSON.parse(this.store.getItem(ORDERS_KEY)) || []; }; this.saveOrders = function (orders) { this.store.setItem(ORDERS_KEY, JSON.stringify(orders)) }; }
new storage.service.js
export class Storage { constructor() { this.ORDERS_KEY = "ORDERS"; this.store = localStorage; } getOrders() { return JSON.parse(this.store.getItem(this.ORDERS_KEY)) || []; }; saveOrders(orders) { this.store.setItem(this.ORDERS_KEY, JSON.stringify(orders)) }; }
old paid-orders.filter.js
export function paidOrders() { return function (orders) { return orders.filter(order => order.paid === false) }}
import {paidOrders} from './paid-orders.filter'; export default angular.module('filters', []) .filter('paidOrders',paidOrders);
old filters.module.js
new paid-orders.filter.js
export class PaidOrders { static transform () { return (orders) => orders.filter(order => order.paid === false) } }
import {PaidOrders} from './paid-orders.filter'; export default angular.module('filters', []) .filter('paidOrders', PaidOrders.transform);
new filters.module.js
old log.controller.js
export function LogController ($scope, $window, Orders, Storage) { $scope.orders = Orders.getOrders(); $scope.clearHistory = function () { if ($scope.orders.length === 0) { return; } var result = $window.confirm("Clear History?"); if (result) { Storage.clear(); $scope.orders = []; } } }
new log.controller.js
export class LogController { constructor($scope, $window, Orders, Storage) { $scope.orders = Orders.getOrders(); $scope.clearHistory = function () { if ($scope.orders.length === 0) { return; } const result = $window.confirm("Clear History?"); if (result) { Storage.clear(); $scope.orders = []; } } } }
MORE JAVASCRIPT LESS ANGULARBind to the controller instance, use setters for watching changes, free yourself from $scope events
old log.controller.js
export class LogController { constructor($scope, $window, Orders, Storage) { $scope.orders = Orders.getOrders(); $scope.clearHistory = function () { if ($scope.orders.length === 0) { return; } const result = $window.confirm("Clear History?"); if (result) { Storage.clear(); $scope.orders = []; } } }
old routes.js
export function routes($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise("/"); $stateProvider .state('log', { url: '/log', templateUrl: 'templates/log.html', controller: 'LogController' }) }
<div class="container-fluid"> <div class="row"> <div class="container-fluid"> <span ng-click="clearHistory()"><i>clear history</i></span> <br/> <simple-table orders="orders"></simple-table> </div> </div> </div>
old log.html
new log.controller.js
export class LogController { constructor( $window, Orders, Storage) { this.window = $window; this.store = Storage; this.orders = Orders.getOrders(); } clearHistory = function () { if (this.orders.length === 0) { return; } const result = this.window.confirm("Clear History?"); if (result) { this.store.clear(); this.orders = []; } } }
new routes.js
export function routes($stateProvider, $urlRouterProvider) { $urlRouterProvider.otherwise("/"); $stateProvider .state('log', { url: '/log', templateUrl: 'templates/log.html', controller: ‘LogController as Logs' }) }
<div class="container-fluid"> <div class="row"> <div class="container-fluid"> <span ng-click="Logs.clearHistory()"><i>clear history</i></span> <br/> <simple-table orders="Logs.orders"></simple-table> </div> </div> </div>
new log.html
old home.controller.js
export class HomeController { constructor(Orders, $scope) { this.Orders = Orders; this.menu = this.Orders.getMenuItems(); this.orders = this.Orders.getOrders(); this.selectedOrder = null; $scope.$watch(()=>this.selectedOrder, this.changeHandler) } changeHandler(newVal,oldVal){ console.log('New order Was selected!'); console.log(newVal, oldVal); } }
new home.controller.js
export class HomeController { constructor(Orders) { this.Orders = Orders; this.menu = this.Orders.getMenuItems(); this.orders = this.Orders.getOrders(); this._selectedOrder = null; } set selectedOrder(order){ this.changeHandler(order); this._selectedOrder = order; } changeHandler(newVal){ console.log('New order Was selected!'); console.log(newVal); } }
old home.controller.js
export class HomeController { constructor(Orders) { this.Orders = Orders; this.menu = this.Orders.getMenuItems(); this.orders = this.Orders.getOrders(); this.selectedOrder = null; } createOrder(clientName) { const order = this.Orders.createOrder(clientName); this.clientName = ''; this.selectOrder(order); };}
new home.controller.js
export class HomeController { constructor(Orders) { this.Orders = Orders; this.menu = this.Orders.getMenuItems(); this._orders = this.Orders.getOrders(); this.selectedOrder = null; } set orders(orders) { console.log('orders changed!', orders); this._orders = orders; } createOrder(clientName) { const order = this.Orders.createOrder(clientName); this.orders = this.Orders.getOrders(); this.clientName = ''; this.selectOrder(order); };}
new dispatcher.js
export class Dispatcher { constructor() { this.subjects = {}; } emit(event, payload){ this.validate(event); this.subjects[event].forEach( callback => callback.apply(null, payload)) } on(event, callback){ this.validate(event); this.subjects[event].push(callback); } validate(event){ if(!this.subjects[event]) { this.subjects[event] = []; } } }
new storage.service.js
export class Storage { constructor(Dispatcher) { this.ORDERS_KEY = "ORDERS"; this.store = localStorage; this.dispatcher = Dispatcher; } getOrders() { return JSON.parse(this.store.getItem(this.ORDERS_KEY)) || []; }; saveOrders(orders) { this.store.setItem(this.ORDERS_KEY, JSON.stringify(orders)) this.dispatcher.emit('ORDERS_SAVED', orders); }; clear() { this.store.clear(); } }
new logger.factory.js
export class Logger { constructor($log, Dispatcher) { this.$log = $log; this.timeStamp = new Date().toString(); Dispatcher.on('ORDERS_SAVED', function (data) { this.debug(`storage saved the orders!`); console.log(data); }) } debug(msg) { this.$log.debug(this.timeStamp + ": " + msg) } log(msg) { this.$log.log(msg) } }
HOME
ORDERS LIST ORDER EDITOR
NEW ORDER FORM
ORDER LIST ORDER FORM
NO ORDER
EDIT ORDER FORM
ORDER ITEM LIST
EDITOR FOOTER
new home.module.js
import {HomeController} from './home.controller'; function routes($stateProvider) { $stateProvider .state('home', { url: '/', templateUrl: 'home/home.html', controller: 'HomeController as Home' }) } export default angular.module('home', []) .config(routes) .controller({HomeController});
new home.module.js
function routes($stateProvider) { $stateProvider .state('home', { url: '/', template: '<home></home>' }) } export default angular.module('home', []) .config(routes) .filter('paidOrders', PaidOrders.transform) .directive({ home, newOrderForm, orderList, ordersList, noSelectedOrder, editOrderForm, orderItemList, orderEditorFooter, orderEditor });
new home.js
export function home () { return { template: ` <div class="container-fluid"> <div class="row"> <orders-list></orders-list> <order-editor></order-editor> </div> </div> `, controller: HomeController, controllerAs: 'Home' } }
new order-list.js
export function ordersList() { return { template: ` <div class="col-sm-6"> <new-order-form></new-order-form> <br/> <order-list></order-list> </div> ` } }
new order-editor.js
export function orderEditor() { return { template: ` <div class="col-sm-6"> <no-selected-order></no-selected-order> <div class="card" ng-if="Home.selectedOrder"> <div class="card-block"> <edit-order-form></edit-order-form> <order-item-list></order-item-list> <order-editor-footer></order-editor-footer> </div> </div> </div> ` } }
new no-selected-order.js
export function noSelectedOrder () { return { template: ` <div class="card" ng-if="!Home.selectedOrder"> <div class="card-block"> <h4 class="card-title">No order selected.</h4> </div> </div> ` } }
new order-list-item.js
export function orderItemList () { return { template:` <ul class="list-group list-group-flush"> <li class="list-group-item" ng-repeat="menuItem in Home.selectedOrder.items"> <span class="label label-default label-pill pull-xs-right"> {{menuItem.itemPrice}}</span> {{menuItem.itemName}} </li> </ul> ` } }
https://github.com/nirkaufman/ng1-coffee-shop
GRAB THE CODE
http://tinyurl.com/hdmqrem
THANKS
ROME 18-19 MARCH 2016
Nir@500tech.com@nirkaufman on twitter slideshare.net/nirkaufman/ github.com/nirkaufman
All pictures belong to their respective authors