150

I'm curious what exactly decorators are in AngularJS. There isn't much information online for decorators save for a blurb in the AngularJS documentation and a brief (albeit interesting) mention in a youtube video.

As the Angular guys put it a decorator is:

Decoration of service, allows the decorator to intercept the service instance creation. The returned instance may be the original instance, or a new instance which delegates to the original instance.

I don't really know what that means, and I'm not sure why you would separate this logic from the service itself. For example if I wanted to return something different under different conditions I would just pass different arguments to the relevant functions or use another function sharing that private state.

I'm still kind of an AngularJS noob so I'm sure it's just ignorance and/or bad habits I've picked up.

Rahil Wazir
  • 10,007
  • 11
  • 42
  • 64
Kevin Beal
  • 10,500
  • 12
  • 66
  • 92

5 Answers5

219

A good use case of $provide.decorator is when you need to do minor "tweak" on some third-party/upstream service, on which your module depends, while leaving the service intact (because you are not the owner/maintainer of the service). Here is a demonstration on plunkr.

tamakisquare
  • 16,659
  • 26
  • 88
  • 129
  • 6
    Awesome example. I was actually wondering how to extend third party modules functionality without meddling with them – Arthur Kovacs Jan 29 '14 at 14:30
  • 5
    Do decorators actually ducktype all instances of a service, or are they scoped to just the module that decorates them? In other words, say I have module A that decorates a service from module B. I then have module C that depends upon module A and module B. Inside of module C, is the service from module B the original or decorated version? – Jon Jaques Feb 26 '14 at 23:32
  • 3
    @JonJaques - That's a great question. I have not come across such situation. If I were to guess, the version of the service that module C sees should be the decorated one from module A but I cannot say that for sure until I try it myself. Why don't you write a simple plunkr/jsffidle and experiment with that. It would be awesome if you could share your finding with us. Cheers. – tamakisquare Feb 28 '14 at 22:32
  • 6
    @JonJaques - Couldn't hold down my curiosity, so I added a few lines to my original example to find the answer to your question, [link](http://plnkr.co/edit/KrE93Efay4B8leCATVrh?p=preview). In short, the guess in my previous comment is right. – tamakisquare Feb 28 '14 at 22:38
  • I found it useful to use a Decorator to initialize a value in a Value service where such initialization requires access to a factory service. Otherwise, it seems that the initialization value in a Value service only allows immediate values (literals, iife, etc) – zumalifeguard Mar 30 '14 at 18:55
  • The example given makes use of the internal method of the third party service it decorates - `this.email`. This breaks the encapsulation of the third party service and makes the decorator vulnerable against further changes of the third party service. – Dmitri Zaitsev Apr 27 '14 at 06:02
  • 17
    Factories, Services etc. are singletons (as they are provided), so once decorated, always decorated. – FlavorScape Jul 14 '14 at 19:56
  • Does decorators work only with third party modules or I could use decorator to override my own services in my module? – Thiago C. S Ventura Apr 21 '15 at 15:25
  • @Ventura You can override you own services as well, check JBland answer for examples on why you'd override your own services (the caching one is a good one). – Lucas Lazaro Jun 11 '15 at 06:47
67

Decorators allow us to separate out cross-cutting concerns and allow services to preserve the single-responsibility-principle without worrying about "infrastructure" code.

Practical uses of decorators:

  • Caching: if we have a service which makes potentially expensive HTTP calls, we can wrap the service in a caching decorator which checks local storage before making the external call.
  • Debugging/Tracing: have a switch depending on your development/production configuration which decorates your services with debugging or tracing wrappers.
  • Throttling : wrap frequently triggered calls in a debouncing wrapper. Allows us to easily interact with rate-limited services, for example.

In all these cases, we limit the code in the service to its main responsibility.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
JBland
  • 1,263
  • 10
  • 13
10

decorator can intercept service instance created by factory, service, value, provider, and gives the options to change some instance(service) that is otherwise not configurable / with options.

It can also provide mock up instances for testing purpose, for example $http.

Daiwei
  • 40,666
  • 3
  • 38
  • 48
  • 1
    It is worth noting that you can also override `directive` definitions as [presented by Ben Nadel](http://www.bennadel.com/?site-photo=490) – David Salamon Jul 05 '16 at 12:00
  • Here is the reference in the official Angular docs: [https://docs.angularjs.org/guide/decorators](https://docs.angularjs.org/guide/decorators) – David Salamon Jul 05 '16 at 12:51
4

In simple word we can say that it’s like an extension method. For Ex. We have a class and it has two methods and at run time we want to add more method in it then we use Decorator.

We cannot use $provide.decorator with constants because we cannot change the constants they are heaving read only property.

Gurupreet
  • 189
  • 2
  • 1
1

In short decorators can be described as follows :-

A decorator function intercepts the creation of a service, allowing it to override or modify the behavior of the service.

It uses the $provide service by angular and modify or replaces the implementation of another service

$provide.decorator('service to decorate',['$delegate', function($delegate) {
  // $delegate - The original service instance, 
  //             which can be replaced, monkey patched, 
  //             configured, decorated or delegated to. 
  //             ie here what is there in the 'service to decorate'

  //   This function will be invoked, 
  //   when the service needs to be provided 
  //   and should return the decorated service instance.
  return $delegate;
}]);

Example:

$provide.decorator('$log', ['$delegate', function($delegate) {
  // This will change implementation of log.war to log.error
  $delegate.warn = $delegate.error; 
  return $delegate;
}]);

Applications

In addition to @JBland answer.

  • Application wide locale settings :-

    You can find an example here

  • Changiging default behaviour of and existing implementation of a service by angular service :-

    You can find an eample here

  • Switching behavior of a function in different environments.

SAMUEL
  • 8,098
  • 3
  • 42
  • 42