Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NG-Delegates

Avatar for Jack Morgan Jack Morgan
November 06, 2013

 NG-Delegates

Avatar for Jack Morgan

Jack Morgan

November 06, 2013

Other Decks in Programming

Transcript

  1. Using decorators is a pattern that describes transparently modifying an

    object without changing the interface. function MacBook() {! this.cost = function() { return 1299; };! this.screenSize = function() { return 13.3; };! }! function Memory(macbook) {! ! var v = macbook.cost();! ! macbook.cost = function() { return v + 75; };! }! ! function ExtraCpu(macbook) {! ! var v = macbook.cost();! ! macbook.cost = function() { return v + 200; };! }! var mb = new MacBook();! mb.cost(); // => 1299! Memory(mb);! ExtraCpu(mb);! mb.cost(); // => 1574!
  2. Traditional JS decoration (function(win) {! ! var oldGet = document.getElementsByTagName;!

    ! document.getElementsByTagName = function(tagName) {! ! ! if (tagName !== 'head') {! ! ! ! return oldGet.apply(document, arguments);! ! ! }! ! ! ! return [{! ! ! ! appendChild : function() {};! ! ! }];! ! }! })(window);! // Naive approach to preventing head injections ! // by third party code.!
  3. Same thing - in Angular angular.module('myApp', [])! .config(['$provide', function($provide) {!

    ! $provide.decorator('$document', ['$delegate', function(_$document_) {! var doc = _$document_[0];! var oldGet = doc.getElementsByTagName;! ! doc.getElementsByTagName = function(tagName) {! if (tagName !== 'head') {! return oldGet.apply(_$document_[0], arguments);! }! ! return [{! appendChild : angular.noop! }];! };! ! return _$document_;! }]);! }]);!
  4. NOW WE’RE DECORATING $provide.decorator can be used to monkey patch

    or override any provider in Angular. It cannot, however, modify directives or controllers. Even ones from 3rd party modules.
  5. BREAK IT DOWN FOR ME As it modifies services at

    their initialization, you can only decorate objects in your app’s config block. $provide.decorate makes the $delegate provider available to you. This is an alias to the original instance of the service you’re decorating. Decorator functions are called only one time. Decorators from included modules are NOT reset to the original instance.
  6. Adding .success and .error to $q $provide.decorator('$q', ['$delegate', function($delegate) {!

    var oldDefer = $delegate.defer;! ! // Promises are generated in as each deferred is resolved so we have to wrap! // the deferred creation method in order to access it's promise.! $delegate.defer = function() {! var dfd = oldDefer.call($delegate, arguments);! dfd.promise.success = function(fn) {! return dfd.promise.then(fn, angular.noop);! };! ! dfd.promise.error = function(fn) {! return dfd.promise.then(angular.noop, fn);! };! return dfd;! };! ! return $delegate;! }]);!
  7. Patch 3rd party modules // Lets add some protected items

    into ngStorage's localStorage! $provide.decorator('$localStorage', ['$delegate', function($delegate) {! ! // Save the original function so that we can use the default behavior if the new logic! // isn't needed.! var originalReset = $delegate.$reset;! $delegate.$reset = function(items, prefix) {! if (!prefix) {! return originalReset(items);! }! ! /* Original reset code.! for (var k in this) {! '$' === k[0] || delete this[k];! }*/! ! for (var k in this) {! '$' === k[0] || prefix === k.slice(0, prefix.length) || delete this[k];! }! ! return this;! };! ! return delegate;! }]);!
  8. Pseudocode for avoiding duplicate requests in $http.get() $provide.decorator('$http', ['$delegate', function($http)

    {! var inFlight = {}; // We'll store the promises of pending requests here.! var originalRecipe = $http.get;! ! $http.get = function() {! // serialize the incoming arguments.! // check to see if inFlight[serialized] exists - if so, return it.! // otherwise invoke originalRecipe with the passed arguments.! // store and return the invoked response.! // Clean up the inFlight hash as requests are completed.! };! }]);! THE ACTUAL CODE FOR THIS IS VERY VERY LONG.