rich object models & angular.js

38
Rich Object Models & Angular Ben Teese, Shine Technologies

Upload: ben-teese

Post on 10-May-2015

7.578 views

Category:

Technology


0 download

DESCRIPTION

Super excited about Angular? Ready to change the world and build a super-heroic app? With directives, Angular has pretty much nailed it when it comes to interacting with the DOM. And plain-old Javascript objects as models? AWESOME! Wait, hang on. Say that you're building a app that has lots of business logic and interrelated data. For that sort of app, history shows us that a rich object model can often be the best place to put your logic and data relationships - from both a testing and ease-of-development perspective. That's the approach that frameworks like Ember advocate - but Ember forces you to extend on its own object model. In this talk I'll ask whether we can get the best of both worlds - a rich data model, whilst still using plain-old Javascript objects. We'll delve into things like: - Lazy-loading data relationships between models - Decorating loaded data with business logic - Object identity uniqueness (critical for bindings to work as expected) - Computed properties for models Attendees will leave with an understanding of how a rich object model can help them build beautiful, fast and easy-to-maintain apps.

TRANSCRIPT

Page 1: Rich Object Models & Angular.js

Rich Object Models & AngularBen Teese, Shine Technologies

Page 2: Rich Object Models & Angular.js

Overview

Why?

Loading Data

Adding Business Logic

Advanced Stuff

Page 3: Rich Object Models & Angular.js

Most of the apps I build are

CRUD

Page 4: Rich Object Models & Angular.js

...it was nice

Page 5: Rich Object Models & Angular.js

WARNING

The remainder of this presentation contains UX that some viewers may find

disturbing

Page 6: Rich Object Models & Angular.js
Page 7: Rich Object Models & Angular.js

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

Page 8: Rich Object Models & Angular.js
Page 9: Rich Object Models & Angular.js
Page 10: Rich Object Models & Angular.js

Restangular

Page 11: Rich Object Models & Angular.js

// GET /proposalsRestangular.all('proposals').getList().then( function(proposals) { $scope.proposals = proposals; });

Getting Stuff

Page 12: Rich Object Models & Angular.js

or...

// GET /proposals$scope.proposals = Restangular.all('proposals').getList(). $object;

Page 13: Rich Object Models & Angular.js

// GET /proposals/:id/cost_items $scope.proposal.getList('cost_items').then( function(costItems) { $scope.costItems = costItems; });

Getting Nested Stuff

Page 14: Rich Object Models & Angular.js

Rich Models

Page 15: Rich Object Models & Angular.js

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'); })

Page 16: Rich Object Models & Angular.js

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

Page 17: Rich Object Models & Angular.js

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

Page 18: Rich Object Models & Angular.js

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?

Page 19: Rich Object Models & Angular.js

angular.module('pimpMyPlane.services', ['restangular', 'pimpMyPlane.models']). factory('Proposals', function(Restangular, Proposal) { Restangular.extendModel('proposals', function(obj) { return Proposal.mixInto(obj); }); ... });

Introduce mixInto()

Page 20: Rich Object Models & Angular.js

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()); }, ... }; });

Page 21: Rich Object Models & Angular.js

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

Page 22: Rich Object Models & Angular.js

Shazam

Page 23: Rich Object Models & Angular.js

Identity Maps

Page 24: Rich Object Models & Angular.js

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

Page 25: Rich Object Models & Angular.js

Identity Map

USD

EUR

IT

“currency”:1

“currency”:2

“department”:1

......

Finance

“department”:2

......

Page 26: Rich Object Models & Angular.js
Page 27: Rich Object Models & Angular.js

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

Page 28: Rich Object Models & Angular.js

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

Page 29: Rich Object Models & Angular.js

Getter Functions(Uniform Access Principle)

Page 30: Rich Object Models & Angular.js

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 ); }, ... }; });

Page 31: Rich Object Models & Angular.js

Memoization

Page 32: Rich Object Models & Angular.js

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 ); }, ... }; });

Page 33: Rich Object Models & Angular.js

<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

Page 34: Rich Object Models & Angular.js

Computed Properties

Page 35: Rich Object Models & Angular.js

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'] }, }); });

Page 36: Rich Object Models & Angular.js

...needs more work

Page 37: Rich Object Models & Angular.js

Let’s Wrap This Up

Rich Models can work

Identity Maps

Getters, Memoization

Computed properties

Shrink-wrappedBoeing 737

Page 38: Rich Object Models & Angular.js

@benteese

Please enjoy the remainder of your flight