angular-cn/modules/angular2/docs/core/10_view.md

247 lines
8.8 KiB
Markdown
Raw Normal View History

# View
## Overview
This document explains the concept of a View.
A View is a core primitive used by angular to render the DOM tree.
A ViewContainer is location in a View which can accept child Views.
Every ViewContainer has an associated ViewContainerRef than can contain any number of child Views.
Views form a tree structure which mimics the DOM tree.
* View is a core rendering construct. A running application is just a collection of Views which are
nested in a tree like structure. The View tree is a simplified version of the DOM tree. A View can
have a single DOM Element or large DOM structures. The key is that the DOM tree in the View can
not undergo structural changes (only property changes).
* Views represent a running instance of a DOM View. This implies that while elements in a View
can change properties, they can not change structurally. (Structural changes such as, adding or
removing elements requires adding or removing child Views into ViewContainers).
* View can have zero or more ViewContainers. A ViewContainer is a marker in the DOM which allows
the insertion of child Views.
* Views are created from a ProtoView. A ProtoView is a compiled DOM View which is efficient at
creating Views.
* View contains a context object. The context represents the object instance against which all
expressions are evaluated.
* View contains a ChangeDetector for looking for detecting changes to the model.
* View contains ElementInjector for creating Directives.
## Simple View
Let's examine a simple View and all of its parts in detail.
Assume the following Component:
```
class Greeter {
greeting:string;
constructor() {
this.greeting = 'Hello';
}
}
```
And assume following HTML View:
```
<div>
Your name:
<input var="name" type="Text">
<br>
{{greeting}} {{name.value}}!
</div>
```
The above template is compiled by the Compiler to create a ProtoView. The ProtoView is then used to
create an instance of the View. The instantiation process involves cloning the above template and
locating all of the elements which contain bindings and finally instantiating the Directives
associated with the template. (See compilation for more details.)
```
<div> | viewA(greeter)
Your name: | viewA(greeter)
<input var="name" type="Text"> | viewA(greeter): local variable 'name'
<br> | viewA(greeter)
{{greeting}} {{name.value}}! | viewA(greeter): binding expression 'greeting' & 'name.value'
</div> | viewA(greeter)
```
The resulting View instance looks something like this (simplified pseudo code):
```
viewA = new View({
template: ...,
context: new Greeter(),
localVars: ['name'],
watchExp: ['greeting', 'name.value']
});
```
Note:
* View uses instance of `Greeter` as the evaluation context.
* View knows of local variables `name`.
* View knows which expressions need to be watched.
* View knows what needs to be updated if the watched expression changes.
* All DOM elements are owned by single instance of the view.
* The structure of the DOM can not change during runtime. To allow structural changes to the DOM we need
to understand Composed View.
## Composed View
An important part of an application is to be able to change the DOM structure to render data for the
user. In Angular this is done by inserting child views into the ViewContainer.
Let's start with a View such as:
```
<ul>
2015-05-14 11:21:43 -04:00
<li template="ng-for: #person of people">{{person}}</li>
</ul>
```
During the compilation process the Compiler breaks the HTML template into these two ProtoViews:
```
<li>{{person}}</li> | protoViewB(Locals)
```
and
```
2015-02-14 14:06:20 -05:00
<ul> | protoViewA(someContext)
<template></template> | protoViewA(someContext): protoViewB
2015-02-14 14:06:20 -05:00
</ul> | protoViewA(someContext)
```
2015-02-14 14:06:20 -05:00
The next step is to compose these two ProtoViews into an actual view which is rendered to the user.
*Step 1:* Instantiate `viewA`
```
2015-02-14 14:06:20 -05:00
<ul> | viewA(someContext)
2015-05-14 11:21:43 -04:00
<template></template> | viewA(someContext): new NgFor(new ViewContainer(protoViewB))
2015-02-14 14:06:20 -05:00
</ul> | viewA(someContext)
```
2015-05-14 11:21:43 -04:00
*Step2:* Instantiate `NgFor` directive which will receive the `ViewContainerRef`. (The ViewContainerRef
has a reference to `protoViewA`).
2015-05-14 11:21:43 -04:00
*Step3:* As the `NgFor` directive unrolls it asks the `ViewContainerRef` to instantiate `protoViewB` and insert
it after the `ViewContainer` anchor. This is repeated for each `person` in `people`. Notice that
```
<ul> | viewA(someContext)
2015-05-14 11:21:43 -04:00
<template></template> | viewA(someContext): new NgFor(new ViewContainer(protoViewB))
<li>{{person}}</li> | viewB0(locals0(someContext))
<li>{{person}}</li> | viewB1(locals0(someContext))
2015-02-14 14:06:20 -05:00
</ul> | viewA(someContext)
```
2015-05-14 11:21:43 -04:00
*Step4:* All of the bindings in the child Views are updated. Notice that in the case of `NgFor`
the evaluation context for the `viewB0` and `viewB1` are `locals0` and `locals1` respectively.
Locals allow the introduction of new local variables visible only within the scope of the View, and
delegate any unknown references to the parent context.
```
<ul> | viewA
2015-05-14 11:21:43 -04:00
<template></template> | viewA: new NgFor(new ViewContainer(protoViewB))
<li>Alice</li> | viewB0
<li>Bob</li> | viewB1
</ul> | viewA
```
Each View can have zero or more ViewContainers. By inserting and removing child Views to and from the
ViewContainers, the application can mutate the DOM structure to any desirable state. A View may contain
individual nodes or a complex DOM structure. The insertion points for the child Views, known as
ViewContainers, contain a DOM element which acts as an anchor. The anchor is either a `template` or
a `script` element depending on your browser. It is used to identify where the child Views will be
inserted.
## Component Views
A View can also contain Components. Components contain Shadow DOM for encapsulating their internal
rendering state. Unlike ViewContainers which can contain zero or more Views, the Component always contains
exactly one Shadow View.
```
<div> | viewA
<my-component> | viewA
#SHADOW_ROOT | (encapsulation boundary)
<div> | viewB
encapsulated rendering | viewB
</div> | viewB
</my-component> | viewA
</div> | viewA
```
## Evaluation Context
2015-02-14 14:06:20 -05:00
Each View acts as a context for evaluating its expressions. There are two kinds of contexts:
1. A component controller instance and
2015-02-14 14:06:20 -05:00
2. a `Locals` context for introducing local variables into the View.
Let's assume following component:
```
class Greeter {
greeting:string;
constructor() {
this.greeting = 'Hello';
}
}
```
And assume the following HTML View:
```
<div> | viewA(greeter)
Your name: | viewA(greeter)
<input var="name" type="Text"> | viewA(greeter)
<br> | viewA(greeter)
{{greeting}} {{name.value}}! | viewA(greeter)
</div> | viewA(greeter)
```
The above UI is built using a single View, and hence a single context `greeter`. It can be expressed
in this pseudo-code.
```
var greeter = new Greeter();
```
The View contains two bindings:
1. `greeting`: This is bound to the `greeting` property on the `Greeter` instance.
2. `name.value`: This poses a problem. There is no `name` property on the `Greeter` instance. To solve
this we wrap the `Greeter` instance in the `Local` instance like so:
```
var greeter = new Locals(new Greeter(), {name: ref_to_input_element })
```
By wrapping the `Greeter` instance into the `Locals` we allow the view to introduce variables which
are in addition to the `Greeter` instance. During the resolution of the expressions we first check
2015-02-14 14:06:20 -05:00
the locals, and then the `Greeter` instance.
## View LifeCycle (Hydration and Dehydration)
Views transition through a particular set of states:
1. View is created from the ProtoView.
2. View can be attached to an existing ViewContainerRef.
3. Upon attaching View to the ViewContainerRef the View needs to be hydrated. The hydration process
involves instantiating all of the Directives associated with the current View.
4. At this point the view is ready and renderable. Multiple changes can be delivered to the
Directives from the ChangeDetection.
5. At some point the View can be removed. At this point all of the directives are destroyed during
the dehydration process and the view becomes inactive.
6. The View has to wait until it is detached from the DOM. The delay in detaching could be caused
because an animation is animating the view away.
7. After the View is detached from the DOM it is ready to be reused. The view reuse allows the
application to be faster in subsequent renderings.