In AngularJS (1.x), $rootScope
plays a crucial role in managing global state, event broadcasting, and data sharing across different components. However, Angular (2+) does not have $rootScope
, leading to issues after migration.
This guide explains:
Why $rootScope
breaks in Angular
Common $rootScope
errors
How to replace $rootScope
in Angular
1. Why $rootScope
Breaks in Angular?
When migrating from AngularJS to Angular, $rootScope
causes issues due to fundamental architecture differences:
- No Global Scope in Angular – Angular uses a hierarchical component tree instead of a single global scope.
- Event Broadcasting is Removed –
$broadcast
and$emit
are replaced by RxJS Observables. - Global Variables Need Services – Data previously stored in
$rootScope
must be moved to Angular services. - State Management is Different – Angular prefers services, Redux, or NgRx for state management instead of
$rootScope
.
2. Common Errors After Removing $rootScope
When upgrading, you may encounter errors like:
Error: "Cannot read property '$rootScope' of undefined"
Error: "Unknown provider: $rootScopeProvider"
Error: "$broadcast is not a function"
Error: "$on is not a function"
These errors occur because Angular does not support $rootScope
, and the upgrade process does not automatically replace it.
3. Replacing $rootScope
in Angular
A. Replacing $rootScope
for Global Variables
Issue:
In AngularJS, $rootScope
is used to store global variables:
$rootScope.user = { name: "John Doe", role: "Admin" };
Fix: Use an Angular Service
1️⃣ Create a service for global data:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class GlobalService {
user = { name: "John Doe", role: "Admin" };
}
2️⃣ Access it in a component:
import { Component } from '@angular/core';
import { GlobalService } from './global.service';
@Component({
selector: 'app-header',
template: '<p>Welcome, {{ globalService.user.name }}</p>'
})
export class HeaderComponent {
constructor(public globalService: GlobalService) {}
}
Use a service instead of $rootScope
for global variables.
B. Replacing $rootScope.$broadcast
and $rootScope.$on
(Event System)
Issue:
AngularJS uses $broadcast
and $on
for communication between controllers.
$rootScope.$broadcast('USER_LOGGED_IN', { name: "John Doe" });
Fix: Use RxJS Subject for Event Broadcasting
1️⃣ Create an Event Service:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class EventService {
private eventSubject = new Subject<any>();
sendEvent(eventName: string, data: any) {
this.eventSubject.next({ eventName, data });
}
getEvents() {
return this.eventSubject.asObservable();
}
}
2️⃣ Send an event:
import { Component } from '@angular/core';
import { EventService } from './event.service';
@Component({
selector: 'app-login',
template: '<button (click)="login()">Login</button>'
})
export class LoginComponent {
constructor(private eventService: EventService) {}
login() {
this.eventService.sendEvent('USER_LOGGED_IN', { name: 'John Doe' });
}
}
3️⃣ Receive the event:
import { Component, OnInit } from '@angular/core';
import { EventService } from './event.service';
@Component({
selector: 'app-header',
template: '<p>Welcome, {{ user?.name }}</p>'
})
export class HeaderComponent implements OnInit {
user: any;
constructor(private eventService: EventService) {}
ngOnInit() {
this.eventService.getEvents().subscribe(event => {
if (event.eventName === 'USER_LOGGED_IN') {
this.user = event.data;
}
});
}
}
Use RxJS Subject
for event-based communication instead of $broadcast
.
C. Replacing $rootScope.$emit
(Parent-to-Child Communication)
Issue:
In AngularJS, $emit
sends events from a child component to a parent.
$scope.$emit('EVENT_NAME', data);
Fix: Use EventEmitter
1️⃣ Child Component (Emitter)
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-child',
template: '<button (click)="sendData()">Send</button>'
})
export class ChildComponent {
@Output() dataEvent = new EventEmitter<string>();
sendData() {
this.dataEvent.emit('Hello from Child');
}
}
2️⃣ Parent Component (Receiver)
import { Component } from '@angular/core';
@Component({
selector: 'app-parent',
template: '<app-child (dataEvent)="receiveData($event)"></app-child>'
})
export class ParentComponent {
receiveData(message: string) {
console.log('Received:', message);
}
}
Use EventEmitter
instead of $emit
for parent-to-child communication.
D. Replacing $rootScope.$watch
(Global State Change Detection)
Issue:
AngularJS used $rootScope.$watch
to track global changes.
$rootScope.$watch('user', function(newValue) {
console.log('User changed:', newValue);
});
Fix: Use BehaviorSubject for Global State Tracking
1️⃣ Create a State Management Service:
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class UserService {
private userSubject = new BehaviorSubject<any>(null);
user$ = this.userSubject.asObservable();
updateUser(user: any) {
this.userSubject.next(user);
}
}
2️⃣ Update Global State:
import { Component } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-login',
template: '<button (click)="login()">Login</button>'
})
export class LoginComponent {
constructor(private userService: UserService) {}
login() {
this.userService.updateUser({ name: 'John Doe', role: 'Admin' });
}
}
3️⃣ Listen for Changes in Another Component:
import { Component, OnInit } from '@angular/core';
import { UserService } from './user.service';
@Component({
selector: 'app-header',
template: '<p>Welcome, {{ user?.name }}</p>'
})
export class HeaderComponent implements OnInit {
user: any;
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.user$.subscribe(user => {
this.user = user;
});
}
}
Use BehaviorSubject
instead of $watch
for tracking global state changes.