DEV Community

Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

A quick guide to AngularJS scopes

AngularJS 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.

https://medium.com/media/bed1c2ef9ed019b888d767b2552fc7ef/href

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 ‘’ we’re actually:

  • Retrieving the scope associated with the DOM node where ‘’ is defined and
  • Evaluating the ‘token’ 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.

Scope hierarchy

Each AngularJS application has a root scope and can have any number of child scopes. The root scope is created whenever the AngularJS 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:

https://medium.com/media/97c9cbc66139501c5e743bc0bb2debf9/href

A common DOM tree, check the ‘ng-scope’ in all elements.

If we take a close look at the DOM tree, we can see that AngularJS 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:

  • Right-click the element of interest in the browser and select the Inspect element option. This will open up the debug window with the clicked element highlighted.
  • Select the Console option if it is not already selected. The debugger allows us to access the currently selected element in the console with the ‘$0’ variable.
  • To retrieve the scope associated with the current element execute the ‘angular.element($0).scope()’ command.

Debugging a scope object.

Scope event propagation

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.

https://medium.com/media/c5cf1290e4fdf16843cc48e2358b8ae0/href

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.

Scope life cycle

Before we wrap up, let’s talk about the scope life cycle.

Angular uses something we can call the $digest — $apply loop to handle the lifecycle of events.

AngularJS by itself is unaware of model modifications. This happens because when the browser calls into Javascript, the code runs outside the AngularJS 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 AngularJS 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.

Conclusion

On this article, we spoke about AngularJS scopes and how they are a vital part of an AngularJS application. We looked scope hierarchies and events and touched base on scope life cycle.

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.

Plug: LogRocket, a DVR for web apps

https://logrocket.com/signup/

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single page apps.

Try it for free.


Top comments (0)