docs(scope): rewrite

This commit is contained in:
Misko Hevery 2012-02-28 12:14:47 -08:00
parent 2e90cdc3d4
commit dd38ce6585
11 changed files with 348 additions and 358 deletions

View file

@ -1007,7 +1007,7 @@ behavior and migrate your controllers one at a time: <https://gist.github.com/16
- complete rewrite of the Scope implementation with several API and semantic changes. Please see:
- [angular.scope API docs](http://docs-next.angularjs.org/#!/api/angular.scope)
- [scopes dev guide article](http://docs-next.angularjs.org/#!/guide/dev_guide.scopes)
- [scopes dev guide article](http://docs-next.angularjs.org/#!/guide/scopes)
- [scope.js source file](https://github.com/angular/angular.js/blob/master/src/Scope.js)
- breaking changes section of this changelog
- added event system to scopes (see [$on], [$emit] and [$broadcast])

View file

@ -3,7 +3,7 @@
@description
In angular, a controller is a JavaScript function(type/class) that is used to augment instances of
angular {@link dev_guide.scopes Scope}, excluding the root scope. When you or angular create a new
angular {@link scope Scope}, excluding the root scope. When you or angular create a new
child scope object via the {@link api/angular.module.ng.$rootScope.Scope#$new scope.$new} API , there is an
option to pass in a controller as a method argument. This will tell angular to associate the
controller with the new scope and to augment its behavior.

View file

@ -7,7 +7,7 @@ either a single object representing one entity (for example, a model called "pho
being an array of phones) or the entire data model for the application (all entities).
In angular, a model is any data that is reachable as a property of an angular {@link
dev_guide.scopes Scope} object. The name of the property is the model identifier and the value is
scope Scope} object. The name of the property is the model identifier and the value is
any JavaScript object (including arrays and primitives).
The only requirement for a JavaScript object to be a model in angular is that the object must be

View file

@ -1,230 +0,0 @@
@ngdoc overview
@name Developer Guide: Scopes: Scope Internals
@description
## What is a scope?
A scope is an execution context for {@link dev_guide.expressions expressions}. You can think of a
scope as a JavaScript object that has an extra set of APIs for registering change listeners and for
managing its own life cycle. In Angular's implementation of the model-view-controller design
pattern, a scope's properties comprise both the model and the controller methods.
### Scope characteristics
- Scopes provide APIs ({@link api/angular.module.ng.$rootScope.Scope#$watch $watch}) to observe model mutations.
- Scopes provide APIs ({@link api/angular.module.ng.$rootScope.Scope#$apply $apply}) to propagate any model changes
through the system into the view from outside of the "Angular realm" (controllers, services,
Angular event handlers).
- Scopes can be nested to isolate application components while providing access to shared model
properties. A scope (prototypically) inherits properties from its parent scope.
- In some parts of the system (such as controllers, services and directives), the scope is made
available as `this` within the given context. (Note: This api will change before 1.0 is released.)
### Root scope
Every application has a root scope, which is the ancestor of all other scopes.
### What is scope used for?
{@link dev_guide.expressions Expressions} in the view are {@link api/angular.module.ng.$rootScope.Scope#$eval evaluated}
against the current scope. When HTML DOM elements are attached to a scope, expressions in those
elements are evaluated against the attached scope.
There are two kinds of expressions:
- Binding expressions, which are observations of property changes. Property changes are reflected
in the view during the {@link api/angular.module.ng.$rootScope.Scope#$digest digest cycle}.
- Action expressions, which are expressions with side effects. Typically, the side effects cause
execution of a method in a controller in response to a user action, such as clicking on a button.
### Scope inheritance
A scope (prototypically) inherits properties from its parent scope. Since a given property may not
reside on a child scope, if a property read does not find the property on a scope, the read will
recursively check the parent scope, grandparent scope, etc. all the way to the root scope before
defaulting to undefined.
{@link guide/directive directives} associated with elements
(ngController, ngRepeat, ngInclude, etc.) create new child scopes that inherit properties from
the current parent scope. Any code in Angular is free to create a new scope. Whether or not your
code does so is an implementation detail of the directive, that is, you can decide when or if this
happens. Inheritance typically mimics HTML DOM element nesting, but does not do so with the same
granularity.
A property write will always write to the current scope. This means that a write can hide a parent
property within the scope it writes to, as shown in the following example.
<pre>
it('should inherit properties', inject(function($rootScope)) {
var root = $rootScope;
var child = root.$new();
root.name = 'angular';
expect(child.name).toEqual('angular');
expect(root.name).toEqual('angular');
child.name = 'super-heroic framework';
expect(child.name).toEqual('super-heroic framework');
expect(root.name).toEqual('angular');
});
</pre>
### Scope life cycle
1. **Creation**
* The root scope is created by the {@link api/angular.module.ng.$rootScope $rootScope} service.
* To create a child scopes, you should call {@link api/angular.module.ng.$rootScope.Scope#$new parentScope.$new()}.
2. **Watcher registration**
Watcher registration can happen at any time and on any scope (root or child) via {@link
api/angular.module.ng.$rootScope.Scope#$watch scope.$watch()} API.
3. **Model mutation**
For mutations to be properly observed, you should make them only within the execution of the
function passed into {@link api/angular.module.ng.$rootScope.Scope#$apply scope.$apply()} call. (Angular apis do this
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers, or
asynchronous work with {@link api/angular.module.ng.$http $http} or {@link api/angular.module.ng.$defer
$defer} services.
4. **Mutation observation**
At the end of each `$apply` call {@link api/angular.module.ng.$rootScope.Scope#$digest $digest} cycle is started on
the root scope, which then propagates throughout all child scopes.
During the `$digest` cycle, all `$watch-ers` expressions or functions are checked for model
mutation and if a mutation is detected, the `$watch-er` listener is called.
5. **Scope destruction**
When child scopes are no longer needed, it is the responsibility of the child scope creator to
destroy them via {@link api/angular.module.ng.$rootScope.Scope#$destroy scope.$destroy()} API. This will stop
propagation of `$digest` calls into the child scope and allow for memory used by the child scope
models to be reclaimed by the garbage collector.
The root scope can't be destroyed via the `$destroy` API. Instead, it is enough to remove all
references from your application to the scope object and garbage collector will do its magic.
## Scopes in Angular applications
To understand how Angular applications work, you need to understand how scopes work within an
application. This section describes the typical life cycle of an application so you can see how
scopes come into play throughout and get a sense of their interactions.
### How scopes interact in applications
1. At application compile time, a root scope is created and is attached to the root `<HTML>` DOM
element.
2. During the compilation phase, the {@link dev_guide.compiler compiler} matches {@link
guide/directive directives} against the DOM template. The directives
usually fall into one of two categories:
- Observing {@link guide/directive directives}, such as double-curly
expressions `{{expression}}`, register listeners using the {@link
api/angular.module.ng.$rootScope.Scope#$watch $watch()} method. This type of directive needs to
be notified whenever the expression changes so that it can update the view.
- Listener directives, such as {@link api/angular.module.ng.$compileProvider.directive.ngClick
ngClick}, register a listener with the DOM. When the DOM listener fires, the directive executes
the associated expression and updates the view using the {@link
api/angular.module.ng.$rootScope.Scope#$apply $apply()} method.
3. When an external event (such as a user action, timer or XHR) is received, the associated {@link
dev_guide.expressions expression} must be applied to the scope through the {@link
api/angular.module.ng.$rootScope.Scope#$apply $apply()} method so that all listeners are updated correctly.
### Directives that create scopes
In most cases, {@link guide/directive directives} and scopes interact but do not create new
instances of scope. However, some directives, such as {@link api/angular.module.ng.$compileProvider.directive.ngController
ngController} and {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}, create new child scopes using
the {@link api/angular.module.ng.$rootScope.Scope#$new $new()} method and then attach the child scope to the
corresponding DOM element. You can retrieve a scope for any DOM element by using an
`angular.element(aDomElement).scope()` method call.)
### Controllers and scopes
Scopes and controllers interact with each other in the following situations:
- Controllers use scopes to expose controller methods to templates (see {@link
api/angular.module.ng.$compileProvider.directive.ngController ngController}).
- Controllers define methods (behavior) that can mutate the model (properties on the scope).
- Controllers may register {@link api/angular.module.ng.$rootScope.Scope#$watch watches} on the model. These watches
execute immediately after the controller behavior executes, but before the DOM gets updated.
See the {@link dev_guide.mvc.understanding_controller controller docs} for more information.
### Updating scope properties
You can update a scope by calling its {@link api/angular.module.ng.$rootScope.Scope#$apply $apply()} method with an
expression or a function as the function argument. However it is typically not necessary to do this
explicitly. In most cases, angular intercepts all external events (such as user interactions, XHRs,
and timers) and wraps their callbacks into the `$apply()` method call on the scope object for you
at the right time. The only time you might need to call `$apply()` explicitly is when you create
your own custom asynchronous widget or service.
The reason it is unnecessary to call `$apply()` from within your controller functions when you use
built-in angular widgets and services is because your controllers are typically called from within
an `$apply()` call already.
When a user inputs data, angularized widgets invoke `$apply()` on the current scope and evaluate an
angular expression or execute a function on this scope. Afterwards `$apply` will trigger `$digest`
call on the root scope, to propagate your changes through the entire system, which results in
$watch-ers firing and view getting updated. Similarly, when a request to fetch data from a server
is made and the response comes back, the data is written into the model (scope) within an $apply,
which then pushes updates through to the view and any other dependents.
A widget that creates scopes (such as {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat}) via `$new`,
doesn't need to worry about propagating the `$digest` call from the parent scope to child scopes.
This happens automatically.
## Scopes in unit-testing
You can create scopes, including the root scope, in tests by having the $rootScope injected into
your spec. This allows you to mimic the run-time environment and have full control over
the life cycle of the scope so that you can assert correct model transitions. Since these scopes
are created outside the normal compilation process, their life cycles must be managed by the test.
### Using scopes in unit-testing
The following example demonstrates how the scope life cycle needs to be manually triggered from
within the unit-tests.
<pre>
// example of a test
it('should trigger a watcher', inject(function($rootScope) {
var scope = $rootScope;
scope.$watch('name', function(name) {
scope.greeting = 'Hello ' + name + '!';
});
scope.name = 'angular';
// The watch does not fire yet since we have to manually trigger the digest phase.
expect(scope.greeting).toEqual(undefined);
// manually trigger digest phase from the test
scope.$digest();
expect(scope.greeting).toEqual('Hello Angular!');
}
</pre>
### Dependency injection in Tests
When you find it necessary to inject your own mocks in your tests, use a scope to override the
service instances, as shown in the following example.
<pre>
it('should allow override of providers', inject(
function($provide) {
$provide.value('$location', {mode:'I am a mock'});
},
function($location){
expect($location.mode).toBe('I am a mock');
}
)};
</pre>
## Related Topics
* {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
## Related API
* {@link api/angular.module.ng.$rootScope.Scope Angular Scope API}

View file

@ -1,35 +0,0 @@
@ngdoc overview
@name Developer Guide: Scopes
@description
An Angular scope is a JavaScript object with additional APIs useful for watching property changes,
Angular scope is the model in Model-View-Controller paradigm. Instances of scope serve as the
context within which all {@link dev_guide.expressions expressions} get evaluated.
You can think of Angular scope objects as the medium through which the model, view, and controller
communicate. Scopes are linked during the compilation process with the view. This linkage provides
the contexts in which Angular creates data-bindings between the model and the view.
In addition to providing the context in which data is evaluated, Angular scope objects watch for
model changes. The scope objects also notify all components interested in any model changes (for
example, functions registered through {@link api/angular.module.ng.$rootScope.Scope#$watch $watch}, bindings created by
{@link api/angular.module.ng.$compileProvider.directive.ngBind ngBind}, or HTML input elements).
Angular scope objects:
* Link the model, controller and view template together.
* Provide the mechanism to watch for model changes ({@link api/angular.module.ng.$rootScope.Scope#$watch $watch}).
* Apply model changes to the system ({@link api/angular.module.ng.$rootScope.Scope#$apply $apply}).
* Provide the context in which expressions are evaluated ({@link api/angular.module.ng.$rootScope.Scope#$eval $eval}).
## Related Topics
* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
* {@link dev_guide.scopes.internals Scopes Internals}
## Related API
* {@link api/angular.module.ng.$rootScope.Scope Angular Scope API}

View file

@ -1,66 +0,0 @@
@ngdoc overview
@name Developer Guide: Scopes: Understanding Scopes
@description
Angular automatically creates a root scope during initialization, and attaches it to the page's
root DOM element (usually `<html>`). The root scope object, along with any of its child scope
objects, serves as the infrastructure on which your data model is built. The data model (JavaScript
objects, arrays, or primitives) is attached to angular scope properties. Angular binds the property
values to the DOM where bindings are specified in the template. Angular attaches any controller
functions you have created to their respective scope objects.
<img src="img/guide/simple_scope_final.png">
Angular scopes can be nested, so a child scope has a parent scope upstream in the DOM. When you
display an angular expression in the view, angular walks the DOM tree looking in the closest
attached scope object for the specified data. If it doesn't find the data in the closest attached
scope, it looks further up the scope hierarchy until it finds the data.
A child scope object inherits properties from its parents. For example, in the following snippet of
code, observe how the value of `name` changes, based on the HTML element it is displayed in:
<doc:example>
<doc:source>
<ul ng-init="name='Hank'; names=['Igor', 'Misko', 'Gail', 'Kai']">
<li ng-repeat="name in names">
Name = {{name}}!
</li>
</ul>
<pre>Name={{name}}</pre>
</doc:source>
<doc:scenario>
it('should override the name property', function() {
expect(using('.doc-example-live').repeater('li').row(0)).
toEqual(['Igor']);
expect(using('.doc-example-live').repeater('li').row(1)).
toEqual(['Misko']);
expect(using('.doc-example-live').repeater('li').row(2)).
toEqual(['Gail']);
expect(using('.doc-example-live').repeater('li').row(3)).
toEqual(['Kai']);
expect(using('.doc-example-live').element('pre').text()).
toBe('Name=Hank');
});
</doc:scenario>
</doc:example>
The angular {@link api/angular.module.ng.$compileProvider.directive.ngRepeat ngRepeat} directive creates a new scope for each
element that it repeats (in this example the elements are list items). In the `<ul>` element, we
initialized `name` to "Hank", and we created an array called `names` to use as the data source for
the list items. In each `<li>` element, `name` is overridden. Outside of the `<li>` repeater, the
original value of `name` is displayed.
The following illustration shows the DOM and angular scopes for the example above:
<img src="img/guide/dom_scope_final.png">
## Related Topics
* {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.internals Scopes Internals}
## Related API
* {@link api/angular.module.ng.$rootScope.Scope Angular Scope API}

View file

@ -34,5 +34,5 @@ isolation without the view and the related DOM/browser dependency.
## Related Topics
* {@link dev_guide.scopes Angular Scopes}
* {@link scope Angular Scopes}
* {@link dev_guide.templates Angular Templates}

View file

@ -2,7 +2,7 @@
@name Developer Guide
@description
Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular
Welcome to the angular Developer Guide. If you are here to learn the details of how to use angular
to develop web apps, you've come to the right place.
If you are completely or relatively unfamiliar with angular, you may want to check out one or both
@ -13,7 +13,7 @@ of the following documents before returning here to the Developer Guide:
<hr>
## {@link dev_guide.overview Overview of Angular}
## {@link overview Overview of Angular}
## {@link bootstrap Initializing Angular}
@ -23,14 +23,9 @@ of the following documents before returning here to the Developer Guide:
* {@link dev_guide.mvc.understanding_controller Understanding the Controller Component}
* {@link dev_guide.mvc.understanding_view Understanding the View Component}
## {@link dev_guide.scopes Angular Scope Objects}
## {@link scope Angular Scope Objects}
* {@link dev_guide.scopes.understanding_scopes Understanding Angular Scope Objects}
* {@link dev_guide.scopes.internals Angular Scope Internals}
## {@link dev_guide.compiler Angular HTML Compiler}
* {@link guide/directive Understanding Angular Directives}
## {@link compiler Angular HTML Compiler}
## {@link dev_guide.templates Angular Templates}

View file

@ -0,0 +1,330 @@
@ngdoc overview
@name Developer Guide: Scopes
@description
# What are Scopes?
{@link api/angular.module.ng.$rootScope.Scope scope} is an object that refers to the application
model. It is an execution context for {@link expression expressions}. Scopes are
arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can
watch {@link guide/expression expressions} and propagate events.
## Scope characteristics
- Scopes provide APIs ({@link api/angular.module.ng.$rootScope.Scope#$watch $watch}) to observe
model mutations.
- Scopes provide APIs ({@link api/angular.module.ng.$rootScope.Scope#$apply $apply}) to
propagate any model changes through the system into the view from outside of the "Angular
realm" (controllers, services, Angular event handlers).
- Scopes can be nested to isolate application components while providing access to shared model
properties. A scope (prototypically) inherits properties from its parent scope.
- Scopes provide context against which {@link guide/expression expressions} are evaluated. For
example `{{username}}` expression is meaningless, unless it is evaluated against a specific
scope which defines the `username` property.
## Scope as Data-Model
Scope is the glue between application controller and the view. During the template {@link compiler
linking} phase the {@link api/angular.module.ng.$compileProvider.directive directives} set up
{@link api/angular.module.ng.$rootScope.Scope#$watch `$watch`} expressions on the scope. The
`$watch` allows the directives to be notified of property changes, which allows the directive to
render the updated value to the DOM.
Both controllers and directives have reference to the scope, but not to each other. This
arrangement isolates the controller from the directive as well as from DOM. This is an important
point since it makes the controllers view agnostic, which greatly improves the testing story of
the applications.
<doc-example>
<doc-source>
<script>
function MyController($scope) {
$scope.username = 'World';
$scope.sayHello = function() {
$scope.greeting = 'Hello ' + $scope.username + '!';
};
}
</script>
<div ng-controller="MyController">
Your name:
<input type="text" ng-model="username">
<button ng-click='sayHello()'>greet</button>
<hr>
{{greeting}}
</div>
</doc-source>
</doc-example>
In the above example notice that the `MyController` assigns `World` to the `username` property of
the scope. The scope then notifies the `input` of the assignment, which then renders the input
with username pre-filled. This demonstrates how a controller can write data into the scope.
Similarly the controller can assign behavior to scope as seen by the `sayHello` method, which is
invoked when the user clicks on the 'greet' button. The `sayHello` method can read the `username`
property and create a `greeting` property. This demonstrates that the properties on scope update
automatically when they are bound to HTML input widgets.
Logically the rendering of `{{greeting}}` involves:
* retrieval of the scope associated with DOM node where `{{greeting}}` is defined in template.
In this example this is the same scope as the scope which was passed into `MyController`. (We
will discuss scope hierarchies later.)
* Evaluate the `greeting` {@link guide/expression expression} against the scope retrieved above,
and assign the result to the text of the enclosing DOM element.
You can think of the scope and its properties as the data which is used to render the view. The
scope is the single source-of-truth for all things view related.
From testability, the separation of the controller and the view is desirable, because it allows us
to test the behavior without being distracted by the rendering details.
<pre>
it('should say hello', function() {
var scopeMock = {};
var cntl = new MyController(scopeMock);
// Assert that username is pre-filled
expect(scopeMock.username).toEqual('World');
// Assert that we read new username and greet
scopeMock.username = 'angular';
scopeMock.sayHello();
expect(scopeMock.greeting).toEqual('Hello angular!');
});
</pre>
## Scope Hierarchies
Each Angular application has exactly one {@link api/angular.module.ng.$rootScope root scope}, but
may have several child scopes.
The application can have multiple scopes, because some {@link guide/directive directives} create
new child scopes (refer to directive documentation to see which directives create new scopes).
When new scopes are created, they are added as children of their parent scope. This creates a tree
structure which parallels the DOM where they're attached
When Angular evaluates `{{username}}`, it first looks at the scope associated with the given
element for the `username` property. If no such property is found, it searches the parent scope
and so on until the root scope is reached. In JavaScript this behavior is known as prototypical
inheritance, and child scopes prototypically inherit from their parents.
This example illustrates scopes in application, and prototypical inheritance of properties.
<doc-example>
<doc-source>
<style>
/* remove .doc-example-live in jsfiddle */
.doc-example-live .ng-scope {
border: 1px dashed red;
}
</style>
<script>
function EmployeeController($scope) {
$scope.department = 'Engineering';
$scope.employee = {
name: 'Joe the Manager',
reports: [
{name: 'John Smith'},
{name: 'Mary Run'}
]
};
}
</script>
<div ng-controller="EmployeeController">
Manager: {{employee.name}} [ {{department}} ]<br>
Reports:
<ul>
<li ng-repeat="employee in employee.reports">
{{employee.name}} [ {{department}} ]
</li>
</ul>
<hr>
{{greeting}}
</div>
</doc-source>
</doc-example>
Notice that the Angular automatically places `ng-scope` class on elements where scopes are
attached. The `<style>` definition in this example highlights in red the new scope locations. The
child scopes are necessary because the repeater evaluates `{{employee.name}}` expression, but
depending on which scope the expression is evaluated it produces different result. Similarly the
evaluation of `{{department}}` prototypically inherits from root scope, as it is the only place
where the `department` property is defined.
## Retrieving Scopes from the DOM.
Scopes are attached to the DOM as `$scope` data property, and can be retrieved for debugging
purposes. (It is unlikely that one would need to retrieve scopes in this way inside the
application.) The location where the root scope is attached to the DOM is defined by the location
of {@link api/angular.module.ng.$compileProvider.directive.ng:app `ng-app`} directive. Typically
`ng-app` is placed an the `<html>` element, but it can be placed on other elements as well, if,
for example, only a portion of the view needs to be controlled by angular.
To examine the scope in the debugger:
1. right click on the element of interest in your browser and select 'inspect element'. You
should see the browser debugger with the element you clicked on highlighted.
2. The debugger allows you to access the currently selected element in the console as `$0`
variable.
3. To retrieve the associated scope in console execute: `angular.element($0).scope()`
## Scope Events Propagation
Scopes can propagate events in similar fashion to DOM events. The event can be {@link
api/angular.module.ng.$rootScope.Scope#$broadcast broadcasted} to the scope children or {@link
api/angular.module.ng.$rootScope.Scope#$emit emitted} to scope parents.
<doc-example>
<doc-source>
<script>
function EventController($scope) {
$scope.count = 0;
$scope.$on('MyEvent', function() {
$scope.count++;
});
}
</script>
<div ng-controller="EventController">
Root scope <tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="i in [1]" ng-controller="EventController">
<button ng-click="$emit('MyEvent')">$emit('MyEvent')</button>
<button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button>
<br>
Middle scope <tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="item in [1, 2]" ng-controller="EventController">
Leaf scope <tt>MyEvent</tt> count: {{count}}
</li>
</ul>
</li>
</ul>
</div>
</doc-source>
</doc-example>
## Scope Life Cycle
The normal flow of browser receiving an event is that it executes a corresponding JavaScript
callback. Once the callback completes the browser re-renders the DOM and returns to waiting for
more events.
When the browser calls into JavaScript the code executes outside they Angular execution context,
which means that Angular is unaware of model modifications. To properly process model
modifications the execution has to enter the Angular execution context using the {@link
api/angular.module.ng.$rootScope.Scope#$apply `$apply`} method. Only model modifications which
execute inside the `$apply` method will be properly accounted for by Angular. For example if a
directive listens on DOM events, such as {@link
api/angular.module.ng.$compileProvider.directive.ng:click `ng-click`} it must evaluate the
expression inside the `$apply` method.
After evaluating the expression `$apply` method performs a {@link
api/angular.module.ng.$rootScope.Scope#$digest `$digest`}. In $digest phase the scope examines all
of the `$watch` expressions and compares them with previous value. This dirty checking, is done
asynchronously. This means that assignment such as `$scope.username="angular"` will not
immediately cause a `$watch` to be notified, instead the `$watch` notification is delayed until
the `$digest` phase. This delay is desirable, since it coalesces multiple model updates into one
`$watch` notification as well as it guarantees that during the `$watch` notification no other
`$watch`es are running. If a `$watch` changes the value of the model, it will force additional
`$digest` cycle.
1. **Creation**
The {@link api/angular.module.ng.$rootScope root scope} is created during the application
bootstrap by the {@link api/angular.module.AUTO.$injector $injector}. During template
linking, some directives create new child scopes.
2. **Watcher registration**
During template linking directives register {@link
api/angular.module.ng.$rootScope.Scope#$watch watches} on the scope. This watches will be
used to propagate model values to the DOM.
3. **Model mutation**
For mutations to be properly observed, you should make them only within the {@link
api/angular.module.ng.$rootScope.Scope#$apply scope.$apply()}. (Angular apis do this
implicitly, so no extra `$apply` call is needed when doing synchronous work in controllers,
or asynchronous work with {@link api/angular.module.ng.$http $http} or {@link
api/angular.module.ng.$defer $defer} services.
4. **Mutation observation**
At the end `$apply`, Angular performs a {@link api/angular.module.ng.$rootScope.Scope#$digest
$digest} cycle on the root scope, which then propagates throughout all child scopes. During
the `$digest` cycle, all `$watch`ed expressions or functions are checked for model mutation
and if a mutation is detected, the `$watch` listener is called.
5. **Scope destruction**
When child scopes are no longer needed, it is the responsibility of the child scope creator
to destroy them via {@link api/angular.module.ng.$rootScope.Scope#$destroy scope.$destroy()}
API. This will stop propagation of `$digest` calls into the child scope and allow for memory
used by the child scope models to be reclaimed by the garbage collector.
### Scopes and Directives
During the compilation phase, the {@link compiler compiler} matches {@link
api/angular.module.ng.$compileProvider.directive directives} against the DOM template. The directives
usually fall into one of two categories:
- Observing {@link api/angular.module.ng.$compileProvider.directive directives}, such as
double-curly expressions `{{expression}}`, register listeners using the {@link
api/angular.module.ng.$rootScope.Scope#$watch $watch()} method. This type of directive needs
to be notified whenever the expression changes so that it can update the view.
- Listener directives, such as {@link api/angular.module.ng.$compileProvider.directive.ng:click
ng:click}, register a listener with the DOM. When the DOM listener fires, the directive
executes the associated expression and updates the view using the {@link
api/angular.module.ng.$rootScope.Scope#$apply $apply()} method.
When an external event (such as a user action, timer or XHR) is received, the associated {@link
expression expression} must be applied to the scope through the {@link
api/angular.module.ng.$rootScope.Scope#$apply $apply()} method so that all listeners are updated
correctly.
### Directives that Create Scopes
In most cases, {@link api/angular.module.ng.$compileProvider.directive directives} and scopes interact
but do not create new instances of scope. However, some directives, such as {@link
api/angular.module.ng.$compileProvider.directive.ng:controller ng:controller} and {@link
api/angular.module.ng.$compileProvider.directive.ng:repeat ng:repeat}, create new child scopes
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
element by using an `angular.element(aDomElement).scope()` method call.
### Controllers and Scopes
Scopes and controllers interact with each other in the following situations:
- Controllers use scopes to expose controller methods to templates (see {@link
api/angular.module.ng.$compileProvider.directive.ng:controller ng:controller}).
- Controllers define methods (behavior) that can mutate the model (properties on the scope).
- Controllers may register {@link api/angular.module.ng.$rootScope.Scope#$watch watches} on
the model. These watches execute immediately after the controller behavior executes.
See the {@link controller controller documentation} for more information.
### Scope `$watch` Performance Considerations
Dirty checking the scope for property changes is a common operation in Angular and for this reason
the dirty checking function must be efficient. Care should be taken that the dirty checking
function does not do any DOM access, as DOM access is orders of magnitude slower then property
access on JavaScript object.

View file

@ -113,7 +113,7 @@ write, test, maintain, and understand.
## Data
The Model is referenced from properties on {@link guide/dev_guide.scopes angular scope objects}.
The Model is referenced from properties on {@link guide/scope angular scope objects}.
The data in your model could be Javascript objects, arrays, or primitives, it doesn't matter. What
matters is that these are all referenced by the scope object.

View file

@ -56,7 +56,7 @@
*
* Every application has a single root {@link angular.module.ng.$rootScope.Scope scope}.
* All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide
* event processing life-cycle. See {@link guide/dev_guide.scopes developer guide on scopes}.
* event processing life-cycle. See {@link guide/scope developer guide on scopes}.
*/
function $RootScopeProvider(){
var TTL = 10;
@ -119,9 +119,6 @@ function $RootScopeProvider(){
expect(parent.salutation).toEqual('Hello');
* </pre>
*
* # Dependency Injection
* See {@link guide/dev_guide.di dependency injection}.
*
*
* @param {Object.<string, function()>=} providers Map of service factory which need to be provided
* for the current scope. Defaults to {@link angular.module.ng}.
@ -273,12 +270,12 @@ function $RootScopeProvider(){
* {@link angular.module.ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a
* call to the `listener`.
*
* - `string`: Evaluated as {@link guide/dev_guide.expressions expression}
* - `string`: Evaluated as {@link guide/expression expression}
* - `function(scope)`: called with current `scope` as a parameter.
* @param {(function()|string)=} listener Callback called whenever the return value of
* the `watchExpression` changes.
*
* - `string`: Evaluated as {@link guide/dev_guide.expressions expression}
* - `string`: Evaluated as {@link guide/expression expression}
* - `function(newValue, oldValue, scope)`: called with current and previous values as parameters.
*
* @param {boolean=} objectEquality Compare object for equality rather then for refference.
@ -325,7 +322,7 @@ function $RootScopeProvider(){
* Because a {@link angular.module.ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the
* `$digest()` keeps calling the {@link angular.module.ng.$rootScope.Scope#$watch watchers} until no more listeners are
* firing. This means that it is possible to get into an infinite loop. This function will throw
* `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 100.
* `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10.
*
* Usually you don't call `$digest()` directly in
* {@link angular.module.ng.$compileProvider.directive.ngController controllers} or in
@ -503,9 +500,8 @@ function $RootScopeProvider(){
*
* @param {(string|function())=} expression An angular expression to be executed.
*
* - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
* - `function(scope, locals)`: execute the function with the current `scope` parameter.
* @param {Object=} locals Hash object of local variables for the expression.
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with the current `scope` parameter.
*
* @returns {*} The result of evaluating the expression.
*/
@ -533,7 +529,7 @@ function $RootScopeProvider(){
*
* @param {(string|function())=} expression An angular expression to be executed.
*
* - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with the current `scope` parameter.
*
*/
@ -570,7 +566,7 @@ function $RootScopeProvider(){
*
* Scope's `$apply()` method transitions through the following stages:
*
* 1. The {@link guide/dev_guide.expressions expression} is executed using the
* 1. The {@link guide/expression expression} is executed using the
* {@link angular.module.ng.$rootScope.Scope#$eval $eval()} method.
* 2. Any exceptions from the execution of the expression are forwarded to the
* {@link angular.module.ng.$exceptionHandler $exceptionHandler} service.
@ -580,7 +576,7 @@ function $RootScopeProvider(){
*
* @param {(string|function())=} exp An angular expression to be executed.
*
* - `string`: execute using the rules as defined in {@link guide/dev_guide.expressions expression}.
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
* - `function(scope)`: execute the function with current `scope` parameter.
*
* @returns {*} The result of evaluating the expression.