aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/guide/scope.ngdoc
diff options
context:
space:
mode:
Diffstat (limited to 'docs/content/guide/scope.ngdoc')
-rw-r--r--docs/content/guide/scope.ngdoc330
1 files changed, 330 insertions, 0 deletions
diff --git a/docs/content/guide/scope.ngdoc b/docs/content/guide/scope.ngdoc
new file mode 100644
index 00000000..1946e7d8
--- /dev/null
+++ b/docs/content/guide/scope.ngdoc
@@ -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.
+