Blog
February 10, 2016 Marie H.

AngularJS – How to pass data between controllers

AngularJS – How to pass data between controllers

Photo by <a href="https://unsplash.com/@goshua13?utm_source=cloudista&utm_medium=referral" target="_blank" rel="noopener">Joshua Aragon</a> on <a href="https://unsplash.com/?utm_source=cloudista&utm_medium=referral" target="_blank" rel="noopener">Unsplash</a>

One of the first things that trips up AngularJS newcomers is sharing state between controllers. Each controller gets its own $scope so you can't just read from another controller's scope directly. The solution is a shared service — specifically a factory that acts as a singleton.

The factory (shared state container)

app.factory('Stuff', function() {
    return {};
});

That's it. The factory returns an object that AngularJS caches and injects as the same instance into every controller that asks for it. It's a singleton by design.

Controller One (writing)

app.controller('oneController', function($scope, Stuff) {
    $scope.stuff = Stuff;
    $scope.stuff.thing = "I'm a thing!";
});

Controller Two (reading)

app.controller('twoController', function($scope, Stuff) {
    $scope.thing = Stuff.thing;
});

Any controller that injects Stuff is looking at the same object, so changes in one are immediately visible in the other. Name your factory something more meaningful than StuffUserSession, CartState, AppConfig, etc.

Reacting to changes

If Controller Two needs to react when Controller One updates the value (rather than just reading it once), use $watch:

app.controller('twoController', function($scope, Stuff) {
    $scope.$watch(function() {
        return Stuff.thing;
    }, function(newValue) {
        $scope.thing = newValue;
    });
});

Or emit events through $rootScope for one-time notifications:

// In controller one
$rootScope.$emit('thingUpdated', Stuff.thing);

// In controller two
$rootScope.$on('thingUpdated', function(event, value) {
    $scope.thing = value;
});

Modern equivalent: Angular 14+ with RxJS BehaviorSubject

If you're starting a new project today, here's the Angular 2+ equivalent of the same pattern. A BehaviorSubject from RxJS gives you a shared observable that any component can subscribe to:

// shared-state.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class SharedStateService {
    private thingSubject = new BehaviorSubject<string>('');
    thing$ = this.thingSubject.asObservable();

    setThing(value: string) {
        this.thingSubject.next(value);
    }

    getThing(): string {
        return this.thingSubject.getValue();
    }
}
// component-one.component.ts
import { Component } from '@angular/core';
import { SharedStateService } from './shared-state.service';

@Component({ selector: 'app-one', template: `<button (click)="update()">Update</button>` })
export class ComponentOne {
    constructor(private state: SharedStateService) {}

    update() {
        this.state.setThing("I'm a thing!");
    }
}
// component-two.component.ts
import { Component, OnInit } from '@angular/core';
import { SharedStateService } from './shared-state.service';

@Component({ selector: 'app-two', template: `<p>{{ thing }}</p>` })
export class ComponentTwo implements OnInit {
    thing = '';

    constructor(private state: SharedStateService) {}

    ngOnInit() {
        this.state.thing$.subscribe(value => this.thing = value);
    }
}

Same idea — a singleton service — but with an observable stream instead of a plain object, which makes reactive updates much easier to manage.

Tell your friends...