Mastering AngularJS scope
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611185/pic_angular.jpg)
Credits
Continues to the right ...
Backup slides
function Scope() {
this.id = nextUid(),
// and some rootScope properties
}
function CreateChildScope(parent) {
function ChildScope() {
this.id = nextUid()
// ...
}
ChildScope.prototype = parent;
return new ChildScope();
}
var rootScope = new Scope();
var mainCtrlScope = CreateChildScope(rootScope)
mainCtrlScope.selectId = 1;
// ...
var directiveScope = CreateChildScope(mainCtrlScope)
Angular in plain JS
Angular uses prototypical inheritance, we can simulate it with plain JS
index
-
Deconstruction of a directive
-
JavaScript Prototypal Inheritance
-
scope inheritance in different angular directives
-
Prepare to Angular2
What are Scopes?
Scope refers to the visibility of variables.
Scope is an execution context for expressions.
Scope is an object that refers to the application model.
Scopes can watch expressions and propagate events.
Scopes are arranged in hierarchical structure which mimic the DOM structure of the application.
...
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611187/chris-rock-huh-face.gif)
scope
$scope
Let's focus only on scope
aka. visibility, context, etc.
what does this directive?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611253/directive3.png)
index.html
app.js
list-directive.tmpl.html
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611653/directive2.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1615299/directive1.png)
?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601760/whatDoesDirective2.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611204/wtf-gif-animated_2.gif)
why does it fail?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611253/directive3.png)
app.js
list-directive.tmpl.html
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611653/directive2.png)
index.html
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1615299/directive1.png)
Access to different scopes
Scope inheritance is normally straightforward, and you often don't even need to know it is happening...
until you try 2-way data binding to a primitive (e.g., number, string, boolean) defined on the parent scope from inside the child scope.
scope life cycle without DOM
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611273/matrix.gif)
Console MODE
rootScope
Every application has a single root scope. All other scopes are descendant scopes of the root scope.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601791/03rootScope.gif)
ngController
When a Controller is attached to the DOM via the ng-controller directive, Angular will instantiate a new Controller object.
A new child scope will be created and made available as an injectable parameter
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601831/04rootScopeNew.gif)
directives
scope: false
The directive does not create a new scope, so there is no inheritance, it's the same scope.
function listDirective(){
return {
scope: false,
templateUrl: 'list-directive.tmpl.html'
}
}
ngRepeat
The ngRepeat directive instantiates a template once per item from a collection. Each template instance gets its own scope, where the given loop variable is set to the current collection item
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601871/05rootScopeNgRepeat.gif)
view
First button is active,
but not the others.
Names are shown.
As expected!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601911/06checkScopeNgRepeat.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611253/directive3.png)
ngClick
button is active as soon it is clicked.
But different selectId?
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601958/07clickScopeNgRepeat.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611253/directive3.png)
Inheritance and the prototype chain
JavaScript? Inheritance?
In JavaScript, classes can't (being that they don't exist!) describe what an object can do.
When it comes to inheritance, JavaScript only has one construct: objects.
Then, how it is done?
JavaScript is a class-free, object-oriented language, it uses prototypal inheritance
... instead of classical inheritance.
Prototype chain
Each object has an internal link to another object called its prototype.
That prototype object has a prototype of its own, and so on until an object is reached with null as its prototype. null, by definition, has no prototype, and acts as the final link in this prototype chain.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1603152/The-JavaScript-Prototype-Chain.png)
JS prototype chain
JavaScript objects have a link to a prototype object.
When getting a property,
the first step is to check if the object itself has the property
if not, it follows the prototype chain
When setting a property,
primitives are shadowed,
objects are accessed
function ParentScope() {
this.aNumber = 101,
this.aString = 'Hello World',
this.aFunction = function() {
console.log( this.aString )
},
this.anArray = [100, 101, 102],
this.anObject = { a: 1, b: 2 }
}
var parent = new ParentScope()
function ChildScope() {}
ChildScope.prototype = parent
var child = new ChildScope()
child.aFunction()
child.aNumber
child.aNumber = 1001
child
child.anObject.a = 101
child
child.anObject = { c: 3, d: 4 }
child
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601980/08ParentScope.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1603137/01scopeImage.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1603138/02scopeImage.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1603140/03scopeImage.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1603141/04scopeImage.png)
Prototype chain
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1601991/Screen_Shot_2015-07-27_at_23.55.43.png)
Prototypal lnheritance
Operator that implements true prototypal inheritance:
Take an old object as the parameter and return a new object that inherits from the old one.
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
.prototype = parent
AngularJS relies on JavaScript
Prototype Inheritance
// angular.js/src/ng/rootScope.js
function createChildScopeClass(parent) {
function ChildScope() {
this.$$watchers = null;
this.$$nextSibling = null;
this.$$childHead = null;
this.$$childTail = null;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$id = nextUid();
this.$$ChildScope = null;
}
ChildScope.prototype = parent;
return ChildScope;
}
Start fixing
... if you use ng-model there has to be a dot somewhere. If you don't have a dot, you're doing it wrong ...
var rootScope = new Scope();
var mainCtrlScope = CreateChildScope(rootScope)
mainCtrlScope.view = {}
mainCtrlScope.view.artists = [
{id: 1001, name: 'Pablo'},
{id: 1002, name: 'Salvador'}
]
mainCtrlScope.view.selectId = 1001;
var childScope = CreateChildScope(mainCtrlScope)
childScope.view.selectId = 1002
childScope.view.selectId
mainCtrlScope.view.selectId
well, that is true for ng-model, but also for all inherited scopes
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1602106/09UseDotScope.gif)
no more primitives in $scope
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611251/directive2.png)
Fixed!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1602125/10workingDirective.gif)
Why using prototypal inheritance?
The design decisions behind the scope are heavily favored for speed and memory consumption.
The typical use of scope is to watch the expressions, which most of the time return the same value as last time so we optimize the operation.
Closures construction is expensive in terms of speed as well as memory
- No closures, instead use prototypical inheritance for API
- Internal state needs to be stored on scope directly, which means that private state is exposed as $$____ properties
Child scopes are created and removed often
- Using an array would be slow since inserts in middle are expensive so we use linked list
Design notes
Directives using
Scope Inheritance
... and inherit prototypically
- ng-include
- ng-if
- ng-controller
- ng-switch
- ng-view
- ng-repeat
- directive with "scope: true"
... and don't inherit
- directive with "scope: { ... }"
creating new scopes
ngInclude
Fetches, compiles and includes an external HTML fragment, creating a new scope.
<div class="slide-animate-container">
<div ng-include="template.url"></div>
</div>
</div>
ngIf
When an element is removed using ngIf its scope is destroyed and a new scope is created when the element is restored
<input type="checkbox"
ng-model="checked"
ng-init="checked=true" />
<div ng-if="checked">
Inside ng-if
<button ng-click="selectId = 7777">
Write selectId
</button>
</div>
same behavior
- ng-controller
- ng-switch
- ng-view
ngRepeat
creates a new scope, which prototypically inherits from the parent scope,
but it also assigns the item's value to a new property on the new child scope
// child scope prototypically inherits from parent scope ...
childScope = scope.$new();
// creates a new childScope property
childScope[valueIdent] = value;
ngRepeat (2)
be careful with arrays on primitives!
$scope.myArrayOfPrimitives = [ 11, 22 ]
// vs.
$scope.myArrayOfObjects = [
{value: 'value 1'},
{value: 'value 2'},
{value: 'value 3'}
]
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1600755/ngRepeat.png)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1600776/ngRepeat2.png)
directives
scope: false
Default
The directive does not create a new scope, so there is no inheritance, it's the same scope.
But careful! Directives are intended as reusable components
function listDirective(){
return {
scope: false,
template: ...
directives
scope: true
The directive creates a new child scope that prototypically inherits from the parent scope
Similar to ng-include, etc.
function listDirective(){
return {
scope: true,
template: ...
directives
scope: { .. }
The directive creates a new isolate/isolated scope. It does not prototypically inherit.
Good isolation
@ one-way model binding from parent to isolated scope
= two-way model binding
& bind to parent expressions
<my-directive
interpolated="{{parentProp1}}"
twowayBinding="parentProp2">
// ...
scope: {
interpolatedProp: '@interpolated',
twowayBindingProp: '=twowayBinding'
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1600845/isolatedScope.png)
Prepare for Angular2
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611502/backToTheFuture.gif)
Core goals on Angular2
Performance
Simplify the framework
...
How?
Performance
Web Workers
No digest cycle (dirty checking)
Simplicity
Moving towards components
Removing $scope
Removing controllers
![](https://s3.amazonaws.com/media-p.slid.es/uploads/299454/images/1611795/jared-padalecki-jensen-acklesSurprise.gif)
death of a $scope
Remove $scope
Use "controller as"
Attach the view properties on the controller instead of the $scope object.
Introduced in Angular 1.2
app.controller('TodoCtrl', TodoCtrl);
// Instead of doing this:
function ($scope) {
$scope.input = 'ex. buy milk';
}
// Do this:
function () {
this.input = 'ex. buy milk';
}
<!-- Then instead of this: -->
<div ng-controller="TodoCtrl">
<input type="text" ng-model="input" />
</div>
<!-- Do this: -->
<div ng-controller="TodoCtrl as todo">
<input type="text" ng-model="todo.input" />
</div>
Remove $scope (2)
Use capture variable for this
when using "controllerAs" syntax
app.controller('TodoCtrl', TodoCtrl);
function TodoCtrl() {
var vm = this;
vm.input= {};
}
<div ng-controller="TodoCtrl as vm">
<input type="text" ng-model="vm.input" />
</div>
The this keyword is contextual and when used within a function inside a controller may change its context.
Remove controllers
Match controllers with directives
Rely on isolated scope, which means cleaner code
Use bindToController, so the component’s properties are bound to the controller rather than to the scope
// Angular 1.3
app.directive('todo', function () {
return {
scope: {
max: '='
},
controller: todoCtrl,
controllerAs: 'TodoCtrl',
bindToController: true,
template: ...
};
});
// Angular 1.4
app.directive('todo', function () {
return {
scope: {},
controller: todoCtrl,
controllerAs: 'TodoCtrl',
bindToController: {
max: '='
},
template: ...
};
});
function TodoCtrl() {
console.log(todoCtrl.max);
...
}
“The $scope is not the model, the $scope refers to the model” and “Whenever you have ng-model there’s gotta be a dot in there somewhere. If you don’t have a dot, you’re doing it wrong.”
Conclusions
Questions?
Gracias!
![](https://s3.amazonaws.com/media-p.slid.es/uploads/carlosmorales/images/1219306/careto_grafiti.jpg)
Mastering AngularJS scope
By Carlos Morales
Mastering AngularJS scope
Slides presented at Zürich AngularJS Meetup (http://www.meetup.com/AngularJS-ZRH/events/224146961/)
- 1,028