object oriented ngresource - angularjs dc lightning round talks 2015-02-18

18
Object-Oriented ngResource Carl Gieringer Software Engineer, Revmetrix https://github.com/carlgieringer/ngResourcePattern

Upload: carl-gieringer

Post on 15-Jul-2015

566 views

Category:

Software


0 download

TRANSCRIPT

Object-Oriented ngResource

Carl Gieringer Software Engineer, Revmetrix

https://github.com/carlgieringer/ngResourcePattern

Overview

  $resource: good for CRUD

  Impedance mismatch with OOD   Data

  Behavior

  Solutions   Data: transformers

  Behavior: Resource.prototype

  Wrap that pattern in a service

ngResource: good for CRUD

// Definition angular.module('app.books', []) .factory('Book', function($resource) { return $resource('/api/v1/books/:id', { id: '@id' }); }). // Create controller('BookCreateCtrl', function(Book) { $scope.book = new Book(); $scope.saveBook = function() { $scope.book.$save(); } });

ngResource: good for CRUD

// List $scope.books = Book.query();

// Read $scope.book = Book.get({id: bookId});

// Update $scope.book.$save();

// Delete $scope.book = new Book({id: bookId}); $scope.book.$delete();

OOD: Semantically Coherent Units of Data and Behavior

function Book(props) { this.id = props.id; this.name = toTitleCase(props.name); this.published = new Date(props.published); ... } Book.prototype = { isPublished: function() { return this.published < new Date(); } }

Resource-OO Impedance Mismatch: data

$scope.book = Book.get({id: 42}); // GET /api/v1/books/42 // { id: 42, name: "A Guide", published: 308548800 } // In controller $scope.book = Book.get({id: bookId}, function() { $scope.book.published = new Date($scope.book.published); }); // In another controller $scope.book = Book.get({id: bookId});

Resource-OO Impedance Mismatch: behavior

// In controller $scope.isPublished = function(book) { return book.published < new Date(); } // In another controller $scope.isPublished = function(book) { return book.published < new Date(); } // In yet another controller $scope.isPublished = function(book) { return book.published <= new Date(); }

Impedance Matching Data: transformers

$resource('/api/books/:id', null, { get: { transformRequest: [ transformBookForServer angular.toJson ], transformResponse: [ angular.fromJson, transformBookFromServer ] } // query, save, delete omitted });

Impedance Matching Data: transformers

function transformBookForServer(book) { book.published = book.published.valueOf(); // ... return book; } function transformBookFromServer(book) { book.published = new Date(book.published); // ... return book; }

Impedance Matching Behavior: prototype

angular.module('app.books', []) .factory('Book', function($resource) { var Book = $resource('/api/books/:id'); Book.prototype.isPublished = function() { return this.published < new Date(); }; return Book; });

Result Usage: DRY and Object-Oriented

$scope.book = Book.get({id: bookId}); <button ng-show="book.isPublished()"> Order Now! </button>

Resulting Invocation: Repetitive

angular.module('app.books', []) .factory('Book', function($resource) { return $resource('/api/v1/books/:id', null, { get: { transformRequest: [ transformBookForServer angular.toJson ], transformResponse: [ angular.fromJson, transformBookFromServer ] }, query: { isArray: true, transformRequest: [ transformBookForServer angular.toJson ], transformResponse: [ angular.fromJson, _.partialRight(_.map, transformBookFromServer) ] },

save: { method: 'POST', transformRequest: [ transformBookForServer angular.toJson ], transformResponse: [ angular.fromJson, transformBookFromServer ] }, delete: { method: 'DELETE', transformRequest: [ transformBookForServer angular.toJson ], transformResponse: [ angular.fromJson, transformBookFromServer ] } }); });

Wrap It in a Service: resoureSrv

angular.module('app.books', []) .factory('Book', function(resourceSrv) { return resourceSrv.makeResource('/api/v1/books/:id',{ responseTransformer: transformBookFromServer, requestTransformer: transformBookForServer, prototype: { isPublished: function() { return this.published < new Date(); } } }); });

Wrap It in a Service: makeResource function makeResource(url, options) { options = options || {}; var actions = { get: makeAction({ responseTransformer: options.responseTransformer }), query: makeAction({ isArray: true, responseTransformer: options.responseTransformer }), save: makeAction({ method: 'POST', responseTransformer: options.responseTransformer, requestTransformer: options.requestTransformer }), update: makeAction({ method: 'PUT', responseTransformer: options.responseTransformer, requestTransformer: options.requestTransformer }) }; var Resource = $resource(url, options.params, actions, options.options); angular.extend(Resource.prototype, options.prototype); return Resource; }

Wrap It in a Service: makeAction

function makeAction(options) { var action = {}; if (options.method) { action.method = options.method; } if (options.isArray) { action.isArray = options.isArray; } if (options.params) { action.params = options.params; } if (options.requestTransformer) { action.transformRequest = makeRequestTransformer(options.requestTransformer); } if (options.responseTransformer) { action.transformResponse = makeResponseTransformer(options.responseTransformer); } return action; }

Wrap It in a Service: makeResponseTransformer

function makeResponseTransformer(responseTransformer) { return function generatedResponseTransformer(data) { try { data = angular.fromJson(data); } catch (err) { throw new Error("API response is not JSON"); } return _.isArray(data) ? _.map(data, responseTransformer) : responseTransformer(data); }; }

Wrap It in a Service: makeResponseTransformer

function makeRequestTransformer(requestTransformer) { return function generatedRequestTransformer(data) { var copy = angular.copy(data); var transformed = requestTransformer(copy); return angular.toJson(transformed); }; }

Thank you. Carl Gieringer

Software Engineer, Revmetrix https://github.com/carlgieringer/ngResourcePattern