Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Scope #331

Closed
wants to merge 1 commit into from
Closed

Scope #331

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
<a name="0.9.19"><a/>
# 0.9.19 canine-psychokinesis (in-progress) #

# Breaking Changes
- Controller constructor functions are now looked up on scope first and then on window.
- angular.equals now use === which means that things which used to be equal are no longer.
Example '0' !== 0 and [] !== ''
- angular.scope (http://docs.angularjs.org/#!angular.scope) now (providers, cache) instead of
(parent, providers, cache)
- Watch functions (see http://docs.angularjs.org/#!angular.scope.$watch) used to take
fn(newValue, oldValue) and be bound to scope, now they take fn(scope, newValue, oldValue)
- calling $eval() [no args] should be replaced with call to $apply()
(http://docs.angularjs.org/#!angular.scope.$apply) ($eval(exp) should remain as is see
http://docs.angularjs.org/#!angular.scope.$eval)
- scope $set/$get have been removed. ($get is same as $eval; no replacement for $set)
- $route.onChange() callback (http://docs.angularjs.org/#!angular.service.$route)
no longer has this bound.
- Removed undocumented $config in root scope. (You should have not been depending on this.)




Expand Down
2 changes: 1 addition & 1 deletion docs/content/cookbook/mvc.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ no connection between the controller and the view.
});
this.$location.hashSearch.board = rows.join(';') + '/' + this.nextMove;
},
readUrl: function(value) {
readUrl: function(scope, value) {
if (value) {
value = value.split('/');
this.nextMove = value[1];
Expand Down
39 changes: 0 additions & 39 deletions docs/content/guide/dev_guide.scopes.controlling_scopes.ngdoc

This file was deleted.

217 changes: 217 additions & 0 deletions docs/content/guide/dev_guide.scopes.innternals.ngdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
@workInProgress
@ngdoc overview
@name Developer Guide: Scope Internals
@description

## What is a scope?

A scope is an execution context for {@link guide.expression 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` in the given context. (Note: This 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 angular.scope.$service
$service} property, and initializing the services.
### What is scope used for?

{@link guide.expression 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 angular.scope.$flush flush cycle}.
- Action expressions, which are expressions with side effects. The side effects cause typically
executes 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, a property read will check a property on the current scope and if the
property is not found the read will recursively check the parent scope, grandparent scope, etc. all
the way to the root scope before defaulting to undefined.

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 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 in an
application context. 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 angular.injector injector} which is assigned to the {@link
angular.scope.$service $service} property of the root scope.
2. Any eager {@link angular.scope.$service services} are initialized at this point.
2. During the compilation phase, the {@link guide.compiler compiler} matches {@link
angular.directive directives} against the DOM template. The directives usually fall into one of two
categories:
- Observing {@link angular.directive directives}, such as double-curly expressions
`{{expression}}`, register listeners using the {@link 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 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 angular.scope.$apply $apply()} method.
3. When an external event (such as a user action, timer or XHR) is received, the associated {@link
guide.expression expression} must be applied to the scope through the {@link angular.scope.$apply
$apply()} method so that all listeners are updated correctly.


### Directives that create scopes
In most cases, {@link angular.directive directives} and scopes interact but do not create new
instances of scope. This section briefly discusses cases in which a directive will create one or
more scopes.

Some directives such as {@link angular.directive.ng:controller ng:controller} or {@link
angular.widget.@ng:repeat ng:repeat} create new child scopes using the {@link angular.scope.$new
$new()} method, and attach the child scope to the corresponding DOM element. (A scope can be
retrieved for any DOM element using a `angular.element(aDomElement).scope()` method call.)


### Controllers and scopes
Some scopes act as controllers (see {@link angular.directive.ng:controller ng:controller}).
Controllers define methods (behavior) that can mutate the model (properties on the scope).
Controllers may register {@link angular.scope.$watch watches} on the model. The watches execute
immediately after the controller behavior executes, but before the DOM renders. (A controller
should NEVER reference a DOM element).

When a controller function is applied to a scope, the scope is augmented with the behavior defined
in the controller. The end result is that the scope behaves as if it were the controller:

<pre>
var scope = angular.scope();
scope.salutation = 'Hello';
scope.name = 'World';

expect(scope.greeting).toEqual(undefined);

scope.$watch('name', function(){
this.greeting = this.salutation + ' ' + this.name + '!';
});

expect(scope.greeting).toEqual('Hello World!');
scope.name = 'Misko';
// scope.$eval() will propagate the change to listeners
expect(scope.greeting).toEqual('Hello World!');

scope.$eval();
expect(scope.greeting).toEqual('Hello Misko!');
</pre>


### 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 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 angular.service.$updateView $updateView} calls the {@link
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

In tests it is often necessary to inject one's own mocks. You can 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}
* {@link dev_guide.scopes.inner_workings.ngdoc Inner Workings of Scopes}

## Related API

* {@link api/angular.scope Angular Scope API}

28 changes: 13 additions & 15 deletions docs/content/guide/dev_guide.scopes.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,31 @@
@description


An angular scope is a JavaScript type defined by angular. Instances of this type are objects that
serve as the context within which all model and controller methods live and get evaluated.
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 guide.expression expressions} get evaluated.

Angular links scope objects to specific points in a compiled (processed) template. This linkage
provides the contexts in which angular creates data-bindings between the model and the view. You
can think of angular scope objects as the medium through which the model, view, and controller
communicate.
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
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.scope.$watch $watch}, bindings created by
{@link api/angular.directive.ng:bind ng:bind}, or HTML input elements).

Angular scope objects are responsible for:
Angular scope objects:

* Gluing the model, controller and view template together.
* Providing the mechanism to watch for model changes ({@link api/angular.scope.$watch}).
* Notifying interested components when the model changes ({@link api/angular.scope.$eval}).
* Providing the context in which all controller functions and angular expressions are evaluated.
* Link the model, controller and view template together.
* Provide the mechanism to watch for model changes ({@link api/angular.scope.$watch}).
* Notify interested components when the model changes ({@link api/angular.scope.$eval}).
* Provide the context in which expressions are evaluated.


## Related Topics

* {@link dev_guide.scopes.understanding_scopes Understanding Scopes}
* {@link dev_guide.scopes.working_scopes Working With Scopes}
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
* {@link dev_guide.scopes.internals Scopes Internals}

## Related API

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ The following illustration shows the DOM and angular scopes for the example abov
## Related Topics

* {@link dev_guide.scopes Angular Scope Objects}
* {@link dev_guide.scopes.working_scopes Working With Scopes}
* {@link dev_guide.scopes.controlling_scopes Applying Controllers to Scopes}
* {@link dev_guide.scopes.updating_scopes Updating Scopes}
* {@link dev_guide.scopes.internals Scopes Internals}

## Related API

Expand Down
38 changes: 0 additions & 38 deletions docs/content/guide/dev_guide.scopes.updating_scopes.ngdoc

This file was deleted.

Loading