johnkeese johnkeese - 7 months ago 29
Javascript Question

Share data between AngularJS controllers

I'm trying to share data across controllers. Use-case is a multi-step form, data entered in one input is later used in multiple display locations outside the original controller. Code below and in jsfiddle here.

HTML

<div ng-controller="FirstCtrl">
<input type="text" ng-model="FirstName"><!-- Input entered here -->
<br>Input is : <strong>{{FirstName}}</strong><!-- Successfully updates here -->
</div>

<hr>

<div ng-controller="SecondCtrl">
Input should also be here: {{FirstName}}<!-- How do I automatically updated it here? -->
</div>


JS

// declare the app with no dependencies
var myApp = angular.module('myApp', []);

// make a factory to share data between controllers
myApp.factory('Data', function(){
// I know this doesn't work, but what will?
var FirstName = '';
return FirstName;
});

// Step 1 Controller
myApp.controller('FirstCtrl', function( $scope, Data ){

});

// Step 2 Controller
myApp.controller('SecondCtrl', function( $scope, Data ){
$scope.FirstName = Data.FirstName;
});


Any help is greatly appreciated.

Answer

A simple solution is to have your factory return an object and let your controllers work with a reference to the same object:

JS:

myApp.factory('Data', function () {
    return { FirstName: '' };
});

myApp.controller('FirstCtrl', function ($scope, Data) {
    $scope.Data = Data;
});

myApp.controller('SecondCtrl', function ($scope, Data) {
    $scope.Data = Data;
});

HTML:

<div ng-controller="FirstCtrl">
  <input type="text" ng-model="Data.FirstName">
  <br>Input is : <strong>{{Data.FirstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
  Input should also be here: {{Data.FirstName}}
</div>

Demo: http://jsfiddle.net/HEdJF/

When applications get larger, more complex and harder to test you might not want to expose the entire object from the factory this way, but instead give limited access for example via getters and setters:

myApp.factory('Data', function () {

    var data = {
        FirstName: ''
    };

    return {
        getFirstName: function () {
            return data.FirstName;
        },
        setFirstName: function (firstName) {
            data.FirstName = firstName;
        }
    };
});

With this approach it is up to the consuming controllers to update the factory with new values, and to watch for changes to get them:

myApp.controller('FirstCtrl', function ($scope, Data) {

    $scope.firstName = '';

    $scope.$watch('firstName', function (newValue, oldValue) {
        if (newValue !== oldValue) Data.setFirstName(newValue);
    });
});

myApp.controller('SecondCtrl', function ($scope, Data) {

    $scope.$watch(function () { return Data.getFirstName(); }, function (newValue, oldValue) {
        if (newValue !== oldValue) $scope.firstName = newValue;
    });
});

HTML:

<div ng-controller="FirstCtrl">
  <input type="text" ng-model="FirstName">
  <br>Input is : <strong>{{FirstName}}</strong>
</div>
<hr>
<div ng-controller="SecondCtrl">
  Input should also be here: {{FirstName}}
</div>

Demo: http://jsfiddle.net/27mk1n1o/