AngularJS $onInit hook executes before parent scope inheritance completes in nested components
0 reputation · 05 Aug 2026, 09:36 UTC
0 reputation · 05 Aug 2026, 09:36 UTC
A small AngularJS application requires migration without downtime, necessitating an incremental approach. The application uses nested components with parent-child scope relationships, relying on $onInit for initialization logic that depends on inherited scope properties.
In nested component hierarchies, $onInit fires before the full scope inheritance chain is established, causing initialization logic to execute with incomplete or undefined inherited values. This creates race conditions where child components attempt to access parent scope properties that haven't propagated yet.
The migration must maintain existing functionality while transitioning to a modern framework. The current implementation assumes $onInit has access to complete scope inheritance, but this assumption breaks in deeply nested scenarios.
35775 reputation · 05 Aug 2026, 15:56 UTC
No. AngularJS does not guarantee that $onInit executes only after complete scope inheritance in a nested component tree. $onInit runs after the component controller is instantiated, but AngularJS may still be propagating inherited scope properties to child components at that point.
$onInit, then child component compilation.$onInit even in shallow trees.AngularJS instantiates controllers top-down as the template compiles. A child component's $onInit can fire before the parent controller finishes assigning inherited values, or before AngularJS finishes linking the child scope to the parent scope. This is a timing race, not a missing scope chain.
$onInit, defer access to inherited parent properties until the next digest cycle using $timeout with no delay, or $scope.$evalAsync.$watch on the parent property or require the parent controller directly via require: ^parentComponent.angular.module('app').component('child', {
bindings: { parentValue: '<' },
require: { parent: '^parentComponent' },
controller: function($scope, $timeout) {
this.$onInit = function() {
// Unsafe: inherited scope property may still be undefined here.
console.log(this.parentValue);
// Safer: defer until after current digest cycle.
var ctrl = this;
$timeout(function() {
console.log(ctrl.parentValue);
});
};
}
});
console.log statements in the child $onInit and in a parent $scope.$watch for the inherited property.$digest to flush pending changes and assert the parent property is defined before the child initialization logic runs.Missing diagnostic detail: Is the parent property assigned synchronously during the parent controller's construction, or is it resolved asynchronously through an HTTP call or promise? This determines whether a simple $timeout deferral is sufficient or whether you need a watcher or promise-based initialization.
Use comments to ask for clarification. Post a solution as an answer.
6,690 reputation · 05 Aug 2026, 20:34 UTC
A useful distinction is between component input bindings and prototypal $scope.$parent inheritance. AngularJS initializes one-way (<) and two-way (=) bindings before $onInit, so this.parentValue is available when declared explicitly. What is not guaranteed is reading arbitrary parent scope properties through $scope.$parent.someProp, especially in nested trees, ng-if, ng-repeat, or dynamically compiled templates where the parent controller may still be assigning values. $postLink does not close this gap; it only confirms the child element and its compiled children are linked.
For verification, mount the same tree in an isolated harness under AngularJS 1.6.x and 1.8.2, log $scope.$id, $scope.$parent.$id, and binding values inside $onInit, then flush a digest and compare capture order. Test on staging behind a feature flag, not on live production traffic, because AngularJS is EOL and digest behavior can vary by version.