Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
HOW ANGULAR2 CAN
IMPROVE YOUR
ANGULARJS APPS
TODAY!
Nir Kaufman
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
ARE YOU
READY FOR
MIGRATION?
How Angular2 Can Improve Your AngularJS Apps Today!
UNAGENDA
I AM NOT GOING TO TALK ABOUT
- TypeScript
- Angular2 upgrade module
- Ng-forward library
- Data flow (Redux, RxJs)
- Angular 1.5 new Component API
BE REALISTIC.
GET READY FOR
THE MIGRATION
BUT…
DON’T FORCE
ANGULAR1 TO
BEHAVE LIKE
ANGULAR2
IT WON’T WORK.
WHAT SHOULD
YOU DO?
APPLY SOME
ANGULAR2 IDEAS
IT WILL IMPROVE
YOUR APP DESIGN
AND GET YOU
READY FOR THE
FUTURE
WINWIN SITUATION
I WILL TALK ABOUT
- Embracing Modules
- Using Classes
- Decoupling from Framework API
- Components as UI building blocks
WAIT A SECOND…
THESE ARE NOT
ANGULAR2 IDEAS!
THAT’S TRUE.
THESE ARE GOOD
PRACTICES
THAT YOU CAN
COMPLETE IN
ONE SPRINT
How Angular2 Can Improve Your AngularJS Apps Today!
https://github.com/nirkaufman/ng1-coffee-shop
GRAB THE CODE
http://tinyurl.com/hdmqrem
REVIEW THE APP
EVERYTHING
IS A MODULE
$ git checkout 01_modules
USE A MODULE LOADER
Integrate a module bundler and an ES6
compiler to use javaScript modules.
http://webpack.github.io/
MODULE LOADER
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
BUNDLE != CONTACT
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 = [];

}

}

}
MODULAR APPROACH
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'

})

}
EVERYTHING
IS A CLASS
$ git checkout 02_classes
MOVE FORWARD TO ES6
You can adopt the new syntax slowly. Start
by using classes instead of functions.
http://babeljs.io/
PLAIN SERVICES
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))

};

}
ANGULAR FILTERS
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
VIEW CONTROLLERS
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 = [];

}

}

}

}
DROP THE
$SCOPE
MORE JAVASCRIPT LESS ANGULAR
Bind to the controller instance, use setters
for watching changes, free yourself from
$scope events
CLASS SYNTAX
$ git checkout 03_drop_the_scope
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
WATCH CHANGES
$ git checkout 04_watch_for_changes
SHALLOW WATCH?
JUST USE SETTERS.
$ git checkout 05_getters_setters
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);

}

}
DEEP WATCHING?
THINK IMMUTABLE.
$ git checkout 06_be_immutable
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);

};
}
EMITTING EVENTS
$ git checkout 07_event_emitter
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)

}

}
BREAK IT TO
COMPONENTS
COMPOSE COMPONENTS
Break the UI layer into small, maintainable
and reusable building blocks.
HOME
ORDERS LIST ORDER EDITOR
NEW ORDER
FORM
ORDER LIST
ORDER
FORM
NO ORDER
EDIT ORDER
FORM
ORDER ITEM
LIST
EDITOR
FOOTER
PROJECT SRTUCTURE
$ git checkout 08_group_by_feature
old structure new structure
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});

DIRECTIVES AS
COMPONENTS
$ git checkout 09_directives_as_components
new folder structure
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>

`

}

}
FINAL WORDS
MODULAR AND
CLEAN CODE IS
ALWAYS BETTER
MIGRATION
WON’T BE EASY
START WITH
SMALL
IMPORTANT
IMPROVEMENTS
THAT YOU CAN
APPLY TODAY
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

More Related Content

How Angular2 Can Improve Your AngularJS Apps Today!

  • 1. HOW ANGULAR2 CAN IMPROVE YOUR ANGULARJS APPS TODAY! Nir Kaufman
  • 2. 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
  • 6. I AM NOT GOING TO TALK ABOUT - TypeScript - Angular2 upgrade module - Ng-forward library - Data flow (Redux, RxJs) - Angular 1.5 new Component API
  • 8. GET READY FOR THE MIGRATION
  • 14. IT WILL IMPROVE YOUR APP DESIGN
  • 15. AND GET YOU READY FOR THE FUTURE
  • 17. I WILL TALK ABOUT - Embracing Modules - Using Classes - Decoupling from Framework API - Components as UI building blocks
  • 22. THAT YOU CAN COMPLETE IN ONE SPRINT
  • 26. EVERYTHING IS A MODULE $ git checkout 01_modules
  • 27. USE A MODULE LOADER Integrate a module bundler and an ES6 compiler to use javaScript modules. http://webpack.github.io/
  • 29. 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
  • 30. <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>
  • 31. <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
  • 33. 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));
  • 34. 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 = [];
 }
 }
 }
  • 36. 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));
  • 37. 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
  • 38. 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)
  • 39. 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']);
 });
  • 40. 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'
 })
 }
  • 41. EVERYTHING IS A CLASS $ git checkout 02_classes
  • 42. MOVE FORWARD TO ES6 You can adopt the new syntax slowly. Start by using classes instead of functions. http://babeljs.io/
  • 44. 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))
 };
 }
  • 45. 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))
 };
 }
  • 47. 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
  • 48. 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
  • 50. 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 = [];
 }
 }
 }
  • 51. 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 = [];
 }
 }
 }
 }
  • 53. MORE JAVASCRIPT LESS ANGULAR Bind to the controller instance, use setters for watching changes, free yourself from $scope events
  • 54. CLASS SYNTAX $ git checkout 03_drop_the_scope
  • 55. 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 = [];
 }
 }
 }
  • 56. 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
  • 57. 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 = [];
 }
 }
 }
  • 58. 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
  • 59. WATCH CHANGES $ git checkout 04_watch_for_changes
  • 60. SHALLOW WATCH? JUST USE SETTERS. $ git checkout 05_getters_setters
  • 61. 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);
 }
 }
  • 62. 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);
 }
 }
  • 63. DEEP WATCHING? THINK IMMUTABLE. $ git checkout 06_be_immutable
  • 64. 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);
 }; }
  • 65. 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);
 }; }
  • 66. EMITTING EVENTS $ git checkout 07_event_emitter
  • 67. 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] = [];
 }
 }
 }
  • 68. 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();
 }
 }
  • 69. 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)
 }
 }
  • 71. COMPOSE COMPONENTS Break the UI layer into small, maintainable and reusable building blocks.
  • 72. HOME ORDERS LIST ORDER EDITOR NEW ORDER FORM ORDER LIST ORDER FORM NO ORDER EDIT ORDER FORM ORDER ITEM LIST EDITOR FOOTER
  • 73. PROJECT SRTUCTURE $ git checkout 08_group_by_feature
  • 74. old structure new structure
  • 75. 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});

  • 76. DIRECTIVES AS COMPONENTS $ git checkout 09_directives_as_components
  • 78. 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
 });
  • 79. 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'
 }
 }
  • 80. 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>
 `
 }
 }

  • 81. 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>
 `
 }
 }
  • 82. 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>
 `
 }
 }
  • 83. 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>
 `
 }
 }
  • 85. MODULAR AND CLEAN CODE IS ALWAYS BETTER
  • 90. 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