Scope inheritance in nested controllers

Loading

In AngularJS, scope inheritance allows child controllers to inherit properties and methods from parent controllers. This behavior is useful for organizing and sharing data across different parts of an application. Understanding how scope inheritance works is crucial for avoiding unexpected behavior, especially when dealing with objects, primitives, and scope bindings.


Step 1: Understanding AngularJS Scope

Each controller in AngularJS has its own $scope object, which:

  1. Acts as a bridge between the controller and the view (HTML).
  2. Inherits from its parent controller’s scope, unless explicitly specified.
  3. Uses prototypal inheritance, meaning changes in parent scope reflect in child scope unless overridden.

Step 2: Basic Example of Scope Inheritance

1️⃣ Parent and Child Controllers

Let’s define a parent and a child controller in AngularJS.

HTML

<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
<title>Scope Inheritance</title>
</head>
<body ng-controller="ParentCtrl">
<h2>Parent Scope: {{ parentMessage }}</h2>

<div ng-controller="ChildCtrl">
<h3>Child Scope: {{ parentMessage }}</h3>
<h3>Child Message: {{ childMessage }}</h3>
</div>

<script src="app.js"></script>
</body>
</html>

JavaScript (app.js)

var app = angular.module('app', []);

app.controller('ParentCtrl', function($scope) {
$scope.parentMessage = "Hello from Parent!";
});

app.controller('ChildCtrl', function($scope) {
$scope.childMessage = "Hello from Child!";
});

How It Works?

  • The ChildCtrl inherits parentMessage from ParentCtrl because of scope inheritance.
  • The ChildCtrl adds childMessage, which is specific to it.

The child scope can access parent scope variables but also define its own variables.


Step 3: Overriding Parent Scope Variables in Child Controllers

If a child controller modifies a primitive variable from the parent, it creates a local copy in the child scope (shadowing the parent’s variable).

Example – Overriding Parent Scope

HTML

<body ng-controller="ParentCtrl">
<h2>Parent: {{ message }}</h2>

<div ng-controller="ChildCtrl">
<h3>Child: <input type="text" ng-model="message"></h3>
</div>
</body>

JavaScript

app.controller('ParentCtrl', function($scope) {
$scope.message = "Original Parent Message";
});

app.controller('ChildCtrl', function($scope) {
$scope.message = "Modified in Child";
});

What Happens?

  • The message variable in ChildCtrl creates a new local copy and does NOT modify ParentCtrl’s message.
  • This is because primitives (strings, numbers, booleans) are assigned by value.

Solution: Use objects instead.


Step 4: Avoiding Shadowing with Objects

To avoid shadowing issues, use objects instead of primitives.

Example – Using Objects

HTML

<body ng-controller="ParentCtrl">
<h2>Parent: {{ data.message }}</h2>

<div ng-controller="ChildCtrl">
<h3>Child: <input type="text" ng-model="data.message"></h3>
</div>
</body>

JavaScript

app.controller('ParentCtrl', function($scope) {
$scope.data = { message: "Original Parent Message" };
});

app.controller('ChildCtrl', function($scope) {
// No local copy; modifies parent’s message directly
});

Why This Works?

  • Objects are passed by reference, so ChildCtrl modifies the same object data used in ParentCtrl.

Using objects ensures that both controllers share the same data reference.


Step 5: Using $scope.$parent to Access Parent Scope

If the child scope has a local variable but still needs access to the parent variable, we can use $scope.$parent.

Example – Accessing Parent Scope Explicitly

JavaScript

app.controller('ChildCtrl', function($scope) {
$scope.message = "Modified in Child";
$scope.parentMessage = $scope.$parent.message;
});

Use $scope.$parent when needed, but avoid it for better maintainability.


Step 6: Isolating Scope with scope: {} in Directives

If you define a directive and want to prevent scope inheritance, use an isolated scope.

Example – Isolated Scope in Directive

Directive

app.directive('customDirective', function() {
return {
restrict: 'E',
scope: {
message: '@'
},
template: '<p>Directive Message: {{ message }}</p>'
};
});

HTML

<custom-directive message="Hello from Parent!"></custom-directive>

Isolated scopes prevent unintentional modifications to the parent scope.


Step 7: $rootScope for Global Scope

  • $rootScope is the top-level scope shared across the entire AngularJS application.
  • Be careful using $rootScope because changes affect all controllers.

Example – Using $rootScope

JavaScript

app.run(function($rootScope) {
$rootScope.globalMessage = "Accessible everywhere!";
});

HTML

<p>{{ globalMessage }}</p> <!-- Accessible in any controller -->

Use $rootScope sparingly to avoid unexpected side effects.


Conclusion

ConceptBehavior
Scope InheritanceChild controllers inherit from parent controllers.
Primitive ShadowingModifying a primitive in the child creates a new local copy.
Using ObjectsObjects are passed by reference, avoiding shadowing issues.
$scope.$parentExplicitly accesses the parent’s scope.
Isolated Scope in DirectivesPrevents accidental scope inheritance.
$rootScopeAccessible globally but should be used with caution.

Leave a Reply

Your email address will not be published. Required fields are marked *