rich object models & angular.js
Post on 10-May-2015
7.571 Views
Preview:
DESCRIPTION
TRANSCRIPT
Rich Object Models & AngularBen Teese, Shine Technologies
Overview
Why?
Loading Data
Adding Business Logic
Advanced Stuff
Most of the apps I build are
CRUD
...it was nice
WARNING
The remainder of this presentation contains UX that some viewers may find
disturbing
Non-Recurring
Engineering
Internal Currency
External Currency Customer
Recurring Engineering
Material Cost Items
Internal Cost Items
DepartmentCost
Currency
External Cost Items
Cost
Currency
Shipsets
Details Years
Line Replaceable
Unit
Sales Price Override
SubassembliesInternal Cost Item
External Cost Items
Standard Sales Price
Spare Parts Sales Price
Customer Type Prices
Currency
Proposal
Currency Currency
Customer Type Sales Price
Currency
Purchase Price RangesSupplier
Purchase Price
Currency
DepartmentCost
Currency
Customer Type
OMG
Restangular
// GET /proposalsRestangular.all('proposals').getList().then( function(proposals) { $scope.proposals = proposals; });
Getting Stuff
or...
// GET /proposals$scope.proposals = Restangular.all('proposals').getList(). $object;
// GET /proposals/:id/cost_items $scope.proposal.getList('cost_items').then( function(costItems) { $scope.costItems = costItems; });
Getting Nested Stuff
Rich Models
angular.module('pimpMyPlane.services', ['restangular']). factory('ProposalSvc', function(Restangular) { Restangular.extendModel('proposals', function(obj) { return angular.extend(obj, {! profit: function() { return this.revenue().minus(this.cost()); }, revenue: function() { return this.price(). convertTo(this.internalCurrency); } ... }); });
return Restangular.all('proposals'); })
angular.module('pimpMyPlane.models'). factory('Proposal', function() { return { profit: function() { return this.revenue().minus(this.cost()); }, revenue: function() { return this.price(). convertTo(this.internalCurrency); }, ... }; }
A Model Mixin
angular.module('pimpMyPlane.services', ['restangular', 'pimpMyPlane.models']).factory('ProposalSvc', function(Restangular, Proposal){ Restangular.extendModel('proposals', function(obj) { return angular.extend(obj, Proposal); });
return Restangular.all('proposals'); });
Using the Mixin
angular.module('pimpMyPlane.services', ['restangular']). factory('ProposalSvc', function(Restangular) { Restangular.extendModel('proposals', function(obj) { angular.extend(obj.recurringEngineering, { ... }); angular.extend(obj.nonRecurringEngineering, { ... }); angular.extend(obj.internalCurrency, { ... }); angular.extend(obj.externalCurrency, { ... });
return angular.extend(obj, Proposal); }); ... })
What about nested models?
angular.module('pimpMyPlane.services', ['restangular', 'pimpMyPlane.models']). factory('Proposals', function(Restangular, Proposal) { Restangular.extendModel('proposals', function(obj) { return Proposal.mixInto(obj); }); ... });
Introduce mixInto()
angular.module('pimpMyPlane.models'). factory('Proposal', function( Currency, RecurringEngineering, NonRecurringEngineering ) { return { mixInto: function(obj) { RecurringEngineering.mixInto( obj.recurringEngineering ); NonRecurringEngineering.mixInto( obj.nonRecurringEngineering ); Currency.mixInto(obj.internalCurrency); Currency.mixInto(obj.externalCurrency)) return angular.extend(obj, this); }, profit: function() { return this.revenue().minus(this.cost()); }, ... }; });
Non-Recurring
Engineering
Internal Currency
External Currency Customer
Recurring Engineering
Material Cost Items
Internal Cost Items
DepartmentCost
Currency
External Cost Items
Cost
Currency
Shipsets
Details Years
Line Replaceable
Unit
Sales Price Override
SubassembliesInternal Cost Item
External Cost Items
Standard Sales Price
Spare Parts Sales Price
Customer Type Prices
Currency
Proposal
Currency Currency
Customer Type Sales Price
Currency
Purchase Price RangesSupplier
Purchase Price
Currency
DepartmentCost
Currency
Customer Type
Shazam
Identity Maps
Non-Recurring
Engineering
Internal Currency
External Currency Customer
Recurring Engineering
Material Cost Items
Internal Cost Items
DepartmentCost
Currency
External Cost Items
Cost
Currency
Shipsets
Details Years
Line Replaceable
Unit
Sales Price Override
SubassembliesInternal Cost Item
External Cost Items
Standard Sales Price
Spare Parts Sales Price
Customer Type Prices
Currency
Proposal
Currency Currency
Customer Type Sales Price
Currency
Purchase Price RangesSupplier
Purchase Price
Currency
DepartmentCost
Currency
Customer Type
Identity Map
USD
EUR
IT
“currency”:1
“currency”:2
“department”:1
......
Finance
“department”:2
......
angular.module('pimpMyPlane.models'). factory('Money', function(Currency, identityMap) { return { mixInto: function(obj) { obj.currency = identityMap( 'currency', Currency.mixInto(obj.currency) ); angular.extend(object, this); }, ... });
Mapping Nested Currencies
angular.module('pimpMyPlane.services', ['restangular', 'pimpMyPlane.models']).factory('CurrenciesSvc', function( Restangular, Currency, identityMap ) { Restangular.extendModel('currencies', function(obj){ return identityMap( 'currency', Currency.mixInto(obj) ); });
return Restangular.all('currencies'); });
Mapping RESTful Currencies
Getter Functions(Uniform Access Principle)
angular.module('pimpMyPlane.models'). factory('Proposal', function(extendWithGetters) { return { mixInto: function(obj) { ... return extendWithGetters(obj, this); }, get profit() { return this.revenue.minus(this.cost); }, get revenue() { return this.price.convertTo( this.internalCurrency ); }, ... }; });
Memoization
angular.module('pimpMyPlane.models'). factory('Proposal', function(Model) { return Model.extend({ memoize: ['revenue', 'cost'], ... get profit() { return this.revenue.minus(this.cost); }, get revenue() { return this.price.convertTo( this.internalCurrency ); }, ... }; });
<div ng-controller="ProposalCtrl"> ... <input type="number" ng-model="currency.conversionFactor" ng-change="proposal.unmemoize()"></input> ... <table> <tr> <td>Number of Aircraft</td> <td> <input type="number" min="1" ng-model="proposal.numberOfAircraft" ng-change="proposal.unmemoize()"></input> </td> </tr> </table></div>
Unmemoization
Computed Properties
angular.module('pimpMyPlane.models'). factory('Proposal', function(...) { return Model.extend({ ... computedProperties: { profit: [function() { return this.revenue.minus(this.cost); }, 'revenue', 'cost'],
cost: [function() { return this.recurringEngineering.cost.plus( this.nonRecurringEngineering.cost ); }, 'recurringEngineering.cost', 'nonRecurringEngineering.cost'] }, }); });
...needs more work
Let’s Wrap This Up
Rich Models can work
Identity Maps
Getters, Memoization
Computed properties
Shrink-wrappedBoeing 737
@benteese
Please enjoy the remainder of your flight
top related