Click here to Skip to main content
15,883,835 members
Articles / Programming Languages / Javascript

Sharing Data Between Controllers? Best Practice: Use a Service

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
7 Jan 2016CPOL3 min read 7.4K   1  
Sharing Data Between Controllers? Best Practice: Use a Service

Angular started off nice and easy. Magical, even. “Two-way binding! Wow!”

And you trotted off and started building your masterpiece, until you hit a snag: you’re building standalone components like everyone online suggests, but how do you share data between those components?

Maybe you have 2 views in separate routes that need access to some status variable. Or you have 3 separate components that all need access to the same piece of data.

What’s the best way to share it? Some sort of crazy controller-inheritance scheme?

No, of course not. The simple, easy way is to use a service.

The Problem

Let’s say you have 2 panes, side-by-side, each represented by a directive.

Two Panes

Here’s the code for Pane 1:

JavaScript
angular.directive('paneOne', function() {
  return {
    restrict: 'E',
    scope: {},
    template: [
      '<div>',
        '<input ng-model="p1.text">',
        '<button ng-click="p1.addToList()">Add To List</button>',
      '</div>'
    ].join(''),
    controllerAs: 'p1',
    controller: function() {
      var vm = this;
      vm.text = "";
      vm.addToList = function() {
        // TODO: add to the list in Pane 2 somehow
        vm.text = "";
      };
    }
  };
});

And for Pane 2:

JavaScript
angular.directive('paneTwo', function() {
  return {
    restrict: 'E',
    scope: {},
    template: [
      '<ul>',
        '<li ng-repeat="item in p2.listItems">{{ item }}</li>',
      '</ul>'
    ].join(''),
    controllerAs: 'p2',
    controller: function() {
      var vm = this;
      // TODO: get this list of items from Pane 1 somehow
      vm.listItems = [];
    }
  };
});

We want to be able to type something into the input box in Pane 1, click “Add To List”, and have it appear in Pane 2’s list.

Create a Service to Hold Shared State

In order to share data between 2 or more controllers, create a service that acts as a mediator. This keeps the controllers (or components) loosely-coupled: they don’t need to know about each other, they just need to know about the data source – your service.

JavaScript
angular.factory('sharedList', function() {
  var list = [];

  return {
    addItem: addItem,
    getList: getList
  };

  function addItem(item) {
    list.push(item);
  }

  function getList() {
    return list;
  }
});

This service is super simple. Call addItem to put things in the list, and getList to retrieve the whole list. It’s so simple, it doesn’t even support removing or clearing items. That’s how simple this thing is.

Inject That Service Everywhere That Cares

Now that we have our service, we need to inject it everywhere that needs to access or modify the data.

Start with Pane 1’s controller:

JavaScript
// Inject sharedList
controller: function(sharedList) {
  var vm = this;
  vm.text = "";
  vm.addToList = function() {
    // Stuff the item into the shared list
    sharedList.addItem(vm.text);
    vm.text = "";
  };
}

Now Pane 2’s controller, to read the data:

JavaScript
// Inject sharedList
controller: function(sharedList) {
  var vm = this;
  // Read the data
  vm.listItems = sharedList.getList();
}

See it working on JSBin.

No Watchers Though?

As I was writing this, I was pretty sure that the list in Pane 2 would not automatically update until I added some watchers.

But then, I put the code into JSBin, and … lo and behold, it works! Why?

  1. The ng-repeat sets up a watcher on the array
  2. Clicking “Add To List” triggers a digest cycle, which re-evaluates the ng-repeat’s watcher.
  3. Because sharedData.getList() returns a reference to an array, the watcher sees that p2.listItems has changed. This is crucial: if getList returned a different array than the one modified by addToList, this would not work.

So: This communication method may work perfectly fine without watchers. But if you find that it isn’t, check how you’re passing data around. You may need to explicitly watch for changes.

Recap

  1. Create a service to contain your data. Give it getter and setter methods.
  2. Inject that service anywhere that needs the data.
  3. That’s pretty much it (unless you need watchers – in which case, add them).

Want to learn best-practices Angular development, as well as get a head start on Angular 2, ES6, and TypeScript? Sign up for my newsletter below!

Thanks for reading.

Sharing Data Between Controllers? Best Practice: Use a Service was originally published by Dave Ceddia at Angularity on December 04, 2015.

This article was originally posted at https://daveceddia.com/feed.xml

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Dave is a Software Engineer in the Boston area and writes about AngularJS and other JavaScript things over at daveceddia.com

Comments and Discussions

 
-- There are no messages in this forum --