2014-12-08 17:29:04 -05:00
|
|
|
# View
|
|
|
|
|
|
|
|
## Overview
|
|
|
|
|
2015-04-27 12:26:55 -04:00
|
|
|
This document explains the concept of a View.
|
|
|
|
A View is a core primitive used by angular to render the DOM tree.
|
2015-04-29 18:07:55 -04:00
|
|
|
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.
|
2015-02-12 05:54:22 -05:00
|
|
|
Views form a tree structure which mimics the DOM tree.
|
|
|
|
|
2015-04-27 12:26:55 -04:00
|
|
|
* 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
|
2015-02-12 05:54:22 -05:00
|
|
|
not undergo structural changes (only property changes).
|
2015-04-27 12:26:55 -04:00
|
|
|
* 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
|
2015-02-12 05:54:22 -05:00
|
|
|
removing elements requires adding or removing child Views into ViewContainers).
|
2015-04-29 18:07:55 -04:00
|
|
|
* View can have zero or more ViewContainers. A ViewContainer is a marker in the DOM which allows
|
2015-02-12 05:54:22 -05:00
|
|
|
the insertion of child Views.
|
2015-04-27 12:26:55 -04:00
|
|
|
* Views are created from a ProtoView. A ProtoView is a compiled DOM View which is efficient at
|
2015-02-12 05:54:22 -05:00
|
|
|
creating Views.
|
2015-04-27 12:26:55 -04:00
|
|
|
* View contains a context object. The context represents the object instance against which all
|
2015-02-12 05:54:22 -05:00
|
|
|
expressions are evaluated.
|
2014-12-08 17:29:04 -05:00
|
|
|
* 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';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-04-09 15:20:11 -04:00
|
|
|
And assume following HTML View:
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
<div>
|
|
|
|
Your name:
|
|
|
|
<input var="name" type="Text">
|
|
|
|
<br>
|
|
|
|
{{greeting}} {{name.value}}!
|
|
|
|
</div>
|
|
|
|
```
|
|
|
|
|
2015-04-27 12:26:55 -04:00
|
|
|
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
|
2015-02-12 05:54:22 -05:00
|
|
|
associated with the template. (See compilation for more details.)
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
<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.
|
2015-04-27 12:26:55 -04:00
|
|
|
* The structure of the DOM can not change during runtime. To allow structural changes to the DOM we need
|
2015-02-12 05:54:22 -05:00
|
|
|
to understand Composed View.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
|
|
|
|
## Composed View
|
|
|
|
|
2015-04-27 12:26:55 -04:00
|
|
|
An important part of an application is to be able to change the DOM structure to render data for the
|
2015-04-29 18:07:55 -04:00
|
|
|
user. In Angular this is done by inserting child views into the ViewContainer.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
2015-04-09 15:20:11 -04:00
|
|
|
Let's start with a View such as:
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
<ul>
|
2015-11-23 19:02:19 -05:00
|
|
|
<li template="ngFor: #person of people">{{person}}</li>
|
2014-12-08 17:29:04 -05:00
|
|
|
</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)
|
2015-04-29 18:07:55 -04:00
|
|
|
<template></template> | protoViewA(someContext): protoViewB
|
2015-02-14 14:06:20 -05:00
|
|
|
</ul> | protoViewA(someContext)
|
2014-12-08 17:29:04 -05:00
|
|
|
```
|
|
|
|
|
|
|
|
|
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.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
*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)
|
2014-12-08 17:29:04 -05:00
|
|
|
```
|
|
|
|
|
2015-05-14 11:21:43 -04:00
|
|
|
*Step2:* Instantiate `NgFor` directive which will receive the `ViewContainerRef`. (The ViewContainerRef
|
2015-02-12 05:54:22 -05:00
|
|
|
has a reference to `protoViewA`).
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
|
2015-05-14 11:21:43 -04:00
|
|
|
*Step3:* As the `NgFor` directive unrolls it asks the `ViewContainerRef` to instantiate `protoViewB` and insert
|
2015-04-29 18:07:55 -04:00
|
|
|
it after the `ViewContainer` anchor. This is repeated for each `person` in `people`. Notice that
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
<ul> | viewA(someContext)
|
2015-05-14 11:21:43 -04:00
|
|
|
<template></template> | viewA(someContext): new NgFor(new ViewContainer(protoViewB))
|
2014-12-08 17:29:04 -05:00
|
|
|
<li>{{person}}</li> | viewB0(locals0(someContext))
|
|
|
|
<li>{{person}}</li> | viewB1(locals0(someContext))
|
2015-02-14 14:06:20 -05:00
|
|
|
</ul> | viewA(someContext)
|
2014-12-08 17:29:04 -05:00
|
|
|
```
|
|
|
|
|
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`
|
2015-04-27 12:26:55 -04:00
|
|
|
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
|
2015-02-12 05:54:22 -05:00
|
|
|
delegate any unknown references to the parent context.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
<ul> | viewA
|
2015-05-14 11:21:43 -04:00
|
|
|
<template></template> | viewA: new NgFor(new ViewContainer(protoViewB))
|
2014-12-08 17:29:04 -05:00
|
|
|
<li>Alice</li> | viewB0
|
|
|
|
<li>Bob</li> | viewB1
|
|
|
|
</ul> | viewA
|
|
|
|
```
|
|
|
|
|
2015-04-29 18:07:55 -04:00
|
|
|
Each View can have zero or more ViewContainers. By inserting and removing child Views to and from the
|
2015-04-27 12:26:55 -04:00
|
|
|
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
|
2015-02-12 05:54:22 -05:00
|
|
|
inserted.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
## Component Views
|
|
|
|
|
2015-04-27 12:26:55 -04:00
|
|
|
A View can also contain Components. Components contain Shadow DOM for encapsulating their internal
|
2015-04-29 18:07:55 -04:00
|
|
|
rendering state. Unlike ViewContainers which can contain zero or more Views, the Component always contains
|
2015-02-12 05:54:22 -05:00
|
|
|
exactly one Shadow View.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
<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:
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
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.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
Let's assume following component:
|
|
|
|
|
|
|
|
```
|
|
|
|
class Greeter {
|
|
|
|
greeting:string;
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.greeting = 'Hello';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-04-09 15:20:11 -04:00
|
|
|
And assume the following HTML View:
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
<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)
|
|
|
|
```
|
|
|
|
|
2015-04-27 12:26:55 -04:00
|
|
|
The above UI is built using a single View, and hence a single context `greeter`. It can be expressed
|
2015-02-12 05:54:22 -05:00
|
|
|
in this pseudo-code.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
```
|
|
|
|
var greeter = new Greeter();
|
|
|
|
```
|
|
|
|
|
|
|
|
The View contains two bindings:
|
|
|
|
|
|
|
|
1. `greeting`: This is bound to the `greeting` property on the `Greeter` instance.
|
2015-04-27 12:26:55 -04:00
|
|
|
2. `name.value`: This poses a problem. There is no `name` property on the `Greeter` instance. To solve
|
2015-02-12 05:54:22 -05:00
|
|
|
this we wrap the `Greeter` instance in the `Local` instance like so:
|
2014-12-08 17:29:04 -05:00
|
|
|
```
|
|
|
|
var greeter = new Locals(new Greeter(), {name: ref_to_input_element })
|
|
|
|
```
|
|
|
|
|
|
|
|
|
2015-04-27 12:26:55 -04:00
|
|
|
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.
|
2014-12-08 17:29:04 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## View LifeCycle (Hydration and Dehydration)
|
|
|
|
|
|
|
|
Views transition through a particular set of states:
|
|
|
|
|
|
|
|
1. View is created from the ProtoView.
|
2015-04-27 12:26:55 -04:00
|
|
|
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
|
2015-02-12 05:54:22 -05:00
|
|
|
involves instantiating all of the Directives associated with the current View.
|
2015-04-27 12:26:55 -04:00
|
|
|
4. At this point the view is ready and renderable. Multiple changes can be delivered to the
|
2015-02-12 05:54:22 -05:00
|
|
|
Directives from the ChangeDetection.
|
2015-04-27 12:26:55 -04:00
|
|
|
5. At some point the View can be removed. At this point all of the directives are destroyed during
|
2015-02-12 05:54:22 -05:00
|
|
|
the dehydration process and the view becomes inactive.
|
2015-04-27 12:26:55 -04:00
|
|
|
6. The View has to wait until it is detached from the DOM. The delay in detaching could be caused
|
2015-02-12 05:54:22 -05:00
|
|
|
because an animation is animating the view away.
|
2015-04-27 12:26:55 -04:00
|
|
|
7. After the View is detached from the DOM it is ready to be reused. The view reuse allows the
|
2015-02-12 05:54:22 -05:00
|
|
|
application to be faster in subsequent renderings.
|