When you’re learning Angular, there’s a lot of new vocabulary to learn. This is particularly true for Angular providers. Terms like “directive”, “service”, “filter”, “decorator” and “factory” can be overwhelming at first.

Knowing which provider to pick when you’re adding code to a project can be equally daunting. Given the wide array of choices, it is understandable that some developers hit decision paralysis.

Alternatively, when unsure of the best path forward, the all-too-common response is to load everything into a controller. This gets the job done in many situations but can also lead to technical debt if done recklessly.

Let’s take a look at some common situations and choices below.

I’m Storing Data Structures or Information

Use a Service

When dealing with data or business logic inside of an angular app, the most appropriate solution is a service or a factory. This is in contrast to UI related concerns (covered later).

The service and factory objects differ in syntax, but provide the same end result. If you’re unsure which one to use specifically, I recommend a service for reasons I will cover in a future post.

Examples

  • Storing a list of JSON objects from an API.
  • Sharing data between multiple areas of an app (eg: user.email, user.locale, etc..).
  • Creating a notifier that is accessible by many parts of the app.
  • Storing complicated permission rules in one unified place.

Resources

I’m Writing Complex Business Logic

See above. As a general rule of thumb, complicated business logic (the kind that bloats controllers) is best kept in a service or factory.

I’m Formatting or Manipulating Data

Use a Filter

If you have code that relates to “shaping” data from one format to another, a filter might be the best choice.

Filters ingest an input object and output a new object in a specified format. Usually this is done in the view with the pipe (|) operator. A less common (but useful) strategy is to call a filter inside of a service or controller using the $filter lookup service.

Examples

  • Sorting a list of users.
  • Formatting currency.
  • Transforming an object into a more useful format for your template.

Resources

I’m Manipulating the DOM or HTML

Use a Directive

If you find yourself copying and pasting code from one HTML file to another or copy/pasting code between controllers, it might be time to pull that logic into a directive.

Directives let you re-use view related logic and presentation. Directives make custom HTML tags and attributes possible and they are Angular’s “killer feature”.

Examples

  • Creating a data grid to present users with reports.
  • Building a UI “widget” that is shared by many controllers or pages.
  • Adding interactive visual components such as drag-and-drop features.
  • Creating custom reusable HTML tags such as <user-profile id=123></user-profile> or <receipt order-id=23></receipt>.

Note: Often, UI components require extensive data manipulation internally. For instance, a data grid may need to manipulate data in complex ways. In such cases, isolate complex logic into its own service or factory. You can include this logic by injecting it into the directive directly. This creates a strong separation of UI and data concerns.

Resources

I Need to Tweak an Existing Provider

Use a Decorator

Scenario: You’re using a 3rd party library from Github or NPM. It works great, but there’s one small modification you need to make for your use case. You don’t want to change the original source code because it’s only a minute modification to an otherwise useful provider.

This is where decorators shine. They are one of the least used providers, but are very useful for a small subset of use cases.

A decorator allows you to wrap a service and “decorate” it with additional functionality.

Examples:

  • Modifying $exceptionHandler to work with a 3rd party error reporting tool, such as Honey Badger.
  • Patching an undesired behavior in a 3rd party library without resorting to a full-blown project fork.

Resources:

I Need to Initialize View Data

Put it in the Controller

You’re building an Angular view or template and need to do some “data massaging” when the page loads.

Although the Angular style guide recommends keeping controllers focused, this is a use case where putting code in a controller is OK.

Examples

  • Fetching a list of users from a service on page load (but don’t write the fetching logic itself in the controller- use a service for that!).
  • Setting default values for view data that is unavailable at load time.

Bad Examples

Since controllers are highly prone to bloating, it is worth considering bad controller use cases:

  • DOM manipulation: Use a directive.
  • Binding DOM event handlers: Use ng-click and friends. Better yet, write a directive.
  • Transforming data: Use a filter.
  • Excessive logic that isn’t totally and 100% related to the current view: Use a service.

Resources

Other Use Cases

In real world apps, directives and services are where the majority of code belongs. Following these basics steps can help keep your controllers slim and technical debt to a minimum.

The majority of technical debt in Angular occurs in the controller. It is always worth asking the question “Does this belong here?” when adding new features to an Angular application.

If you’re still unsure of where to put logic in an application, a service is a good place to start. An exception to this rule is code which modifies the DOM, in which case a directive is almost always the correct solution.

Have questions about a use case we didn’t cover? Let us know in the comments.

comments powered by Disqus