Angular applications normally rely on controllers to control the flow of data in an application. This data is then passed into a view to being rendered. To join the controller and view together Angular uses a special object called scope. This scope object acts as an execution context for expressions and is arranged hierarchically mimicking the DOM structure.
During template linking phase the directives setup $watch expressions on the scope. Those allow the directives to be notified whenever there are property changes. This way, the directive can render the updated value to the DOM.
Both controllers and directives have access to the scope, but not to each other. Now the scope ensures proper encapsulation of the controllers from the directives and the DOM.
See the Pen
I love LogRocket! by Claudio (@DailyMatters)
on CodePen.
As we can see in the example, a controller can either write data into the scope or assign a behavior to it.
It starts by assigning LogRocket
to the initial
property of the scope. And then assigning the sayIlove()
behavior to the Love!
button. The sayIlove()
method can read the initial
property and create a token
property. With this example, we can see that the properties on the scope can update automatically when they’re bound to HTML input widgets.
When we’re finally rendering {{token}}
we’re actually:
{{token}}
is defined andtoken
expression against the scope retrieved above, and assigning the result to the text of the enclosing DOM element.All in all, in a very simplistic manner, the scope can be seen as nothing more than the data which is used to render the view.
Each Angular application has a root scope and can have any number of child scopes. The root scope is created whenever the Angular application is created, but then, directives cam create new child scopes. When a new child scope is created it is added as a child of his parent scope. This tree of child scopes normally parallels the DOM where they’re attached.
A perfect example of this effect in action is the one of a dynamically generated list. Each element of the list is fetched whatever source by the controller and then a new scope is created for each element. This new scope is then passed to the view, where it will be bound to a newly created DOM element. Just as we can see in the next example:
See the Pen
Scope Hierarchies by Claudio (@DailyMatters)
on CodePen.
A quick scope hierarchies example.
If we take a close look at the DOM tree, we can see that Angular adds the ng-scope
to all elements where scopes are attached. All these child scopes are necessary because depending on which scope the expression is evaluated it produces a different result.
The $scope
data property is attached to the DOM, and as so, it can be retrieved for debugging purposes. To examine a particular scope in the debugger we can use the built-in debugger of the browser. There are essentially three steps for this:
$0
variable.angular.element($0).scope()
command.Similarly to DOM events, scopes can also propagate events. The event can be broadcasted to the scope children or emitted to scope parents. The following example shows how this can be done.
See the Pen
Angular scope event propagation by Claudio (@DailyMatters)
on CodePen.
Angular scope propagation in action.
The $emit
function is used to propagate events upwards through the scope hierarchy. As we can see in the example when the ‘emit’ button is clicked an event is propagated to the upper scope layer, which means the root scope.
The $broadcast
function is propagated events downwards to every child scopes and their children scopes. In our example, when the ‘broadcast’ button is clicked, an event is propagated to the lower scope layers, which means the child and second child scopes.
Before we wrap up, let’s talk about the scope lifecycle.
Angular uses something we can call the $digest — $apply loop to handle the lifecycle of events.
Angular by itself is unaware of model modifications. This happens because when the browser calls into Javascript, the code runs outside the Angular execution context. To enter the Angular execution context, the $apply
method needs to be called. This call to $apply
will evaluate the expression passed to it and then perform a ‘$digest’.
$digest
is an internal cycle that runs through the application and executes $watch
expressions and compares the value returned with the previous value already in the scope. If the values don’t match, a listener is fired. This cycle will run until there are no more listeners to be fired.
For example, an assignment like $scope.company=LogRocket
will not trigger a $watch
immediately. This is the $digest
responsibility, which may or may not trigger a $watch
, depending on the value that’s already on $scope.company
.
This effect is beneficial for Angular performance as it consolidates all model updates into one single $watch
notification. It also assures that we don’t get to inconsistent states as only one $watch
notification can be running at a given time. For additional model modifications, a new $digest
cycle must be triggered.
On this article, we spoke about Angular scopes and how they are a vital part of an Angular application. We looked scope hierarchies and events and touched base on scope lifecycle.
I hope this article was able to get you up and running with Angular Scopes and to better understand how important they are in every Angular application.
Debugging Angular applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Angular state and actions for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your site including network requests, JavaScript errors, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket NgRx plugin logs Angular state and actions to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.
Modernize how you debug your Angular apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThe useReducer React Hook is a good alternative to tools like Redux, Recoil, or MobX.
Node.js v22.5.0 introduced a native SQLite module, which is is similar to what other JavaScript runtimes like Deno and Bun already have.
Understanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]