aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/guide/dev_guide.scopes.internals.ngdoc
diff options
context:
space:
mode:
authorMisko Hevery2011-03-23 09:33:29 -0700
committerVojta Jina2011-08-02 01:00:03 +0200
commit8f0dcbab804180828d6859b1340c86cf161209fb (patch)
treed13d47d47a1889cb7c96a87cecacd2e25307d51c /docs/content/guide/dev_guide.scopes.internals.ngdoc
parent1f4b417184ce53af15474de065400f8a686430c5 (diff)
downloadangular.js-8f0dcbab804180828d6859b1340c86cf161209fb.tar.bz2
feat(scope): new and improved scope implementation
- Speed improvements (about 4x on flush phase) - Memory improvements (uses no function closures) - Break $eval into $apply, $dispatch, $flush - Introduced $watch and $observe Breaks angular.equals() use === instead of == Breaks angular.scope() does not take parent as first argument Breaks scope.$watch() takes scope as first argument Breaks scope.$set(), scope.$get are removed Breaks scope.$config is removed Breaks $route.onChange callback has not "this" bounded
Diffstat (limited to 'docs/content/guide/dev_guide.scopes.internals.ngdoc')
-rw-r--r--docs/content/guide/dev_guide.scopes.internals.ngdoc196
1 files changed, 196 insertions, 0 deletions
diff --git a/docs/content/guide/dev_guide.scopes.internals.ngdoc b/docs/content/guide/dev_guide.scopes.internals.ngdoc
new file mode 100644
index 00000000..3aed7970
--- /dev/null
+++ b/docs/content/guide/dev_guide.scopes.internals.ngdoc
@@ -0,0 +1,196 @@
+@workInProgress
+@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 ($watch and $observe) to observe model mutations.
+- Scopes provide APIs ($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 functionality will change before 1.0 is
+released.)
+
+
+### Root scope
+
+Every application has a root scope, which is the ancestor of all other scopes. The root scope is
+responsible for creating the injector which is assigned to the {@link api/angular.scope.$service
+$service} property, and initializing the services.
+
+### What is scope used for?
+
+{@link dev_guide.expressions Expressions} in the view are 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.scope.$flush flush 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 api/angular.directive Directives} associated with elements (ng:controller, ng:repeat,
+ng:include, 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>
+var root = angular.scope();
+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>
+
+
+
+## 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.
+ 1. The root scope creates an {@link api/angular.injector injector} which is assigned to the
+{@link api/angular.scope.$service $service} property of the root scope.
+ 2. Any eager {@link api/angular.scope.$service services} are initialized at this point.
+2. During the compilation phase, the {@link dev_guide.compiler compiler} matches {@link
+api/angular.directive directives} against the DOM template. The directives usually fall into one of
+two categories:
+ - Observing {@link api/angular.directive directives}, such as double-curly expressions
+`{{expression}}`, register listeners using the {@link api/angular.scope.$observe $observe()}
+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.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.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.scope.$apply $apply()} method so that all listeners are updated correctly.
+
+
+### Directives that create scopes
+In most cases, {@link api/angular.directive directives} and scopes interact but do not create new
+instances of scope. However, some directives, such as {@link api/angular.directive.ng:controller
+ng:controller} and {@link api/angular.widget.@ng:repeat ng:repeat}, create new child scopes using
+the {@link api/angular.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.directive.ng:controller ng:controller}).
+ - Controllers define methods (behavior) that can mutate the model (properties on the scope).
+ - Controllers may register {@link api/angular.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.scope.$eval $eval()} method, but usually
+you do not have to do this explicitly. In most cases, angular intercepts all external events (such
+as user interactions, XHRs, and timers) and calls the `$eval()` method on the scope object for you
+at the right time. The only time you might need to call `$eval()` explicitly is when you create
+your own custom widget or service.
+
+The reason it is unnecessary to call `$eval()` from within your controller functions when you use
+built-in angular widgets and services is because a change in the data model triggers a call to the
+`$eval()` method on the scope object where the data model changed.
+
+When a user inputs data, angularized widgets copy the data to the appropriate scope and then call
+the `$eval()` method on the root scope to update the view. It works this way because scopes are
+inherited, and a child scope `$eval()` overrides its parent's `$eval()` method. Updating the whole
+page requires a call to `$eval()` on the root scope as `$root.$eval()`. Similarly, when a request
+to fetch data from a server is made and the response comes back, the data is written into the model
+and then `$eval()` is called to push updates through to the view and any other dependents.
+
+A widget that creates scopes (such as {@link api/angular.widget.@ng:repeat ng:repeat}) is
+responsible for forwarding `$eval()` calls from the parent to those child scopes. That way, calling
+`$eval()` on the root scope will update the whole page. This creates a spreadsheet-like behavior
+for your app; the bound views update immediately as the user enters data.
+
+## Scopes in unit-testing
+You can create scopes, including the root scope, in tests using the {@link api/angular.scope} API.
+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.
+
+There is a key difference between the way scopes are called in Angular applications and in Angular
+tests. In tests, the {@link api/angular.service.$updateView $updateView} calls the {@link
+api/angular.scope.$flush $flush()} method synchronously.(This is in contrast to the asynchronous
+calls used for applications.) Because test calls to scopes are synchronous, your tests are simpler
+to write.
+
+### 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
+ var scope = angular.scope();
+ scope.$watch('name', function(scope, 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>
+var myLocation = {};
+var scope = angular.scope(null, {$location: myLocation});
+expect(scope.$service('$location')).toEqual(myLocation);
+</pre>
+
+## Related Topics
+
+* {@link dev_guide.scopes Angular Scope Objects}
+* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
+
+## Related API
+
+* {@link api/angular.scope Angular Scope API}
+