2018-02-26 16:58:15 -08:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2018-05-09 16:49:39 -07:00
|
|
|
import {ApplicationRef} from '../application_ref';
|
|
|
|
import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
2018-05-31 15:45:46 +02:00
|
|
|
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
|
2018-05-09 16:49:39 -07:00
|
|
|
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
2018-03-08 16:55:47 -08:00
|
|
|
|
2018-08-02 16:53:34 -07:00
|
|
|
import {checkNoChanges, checkNoChangesInRootView, detectChanges, detectChangesInRootView, getRendererFactory, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
2018-09-13 16:07:23 -07:00
|
|
|
import {TViewNode} from './interfaces/node';
|
2018-06-07 22:42:32 -07:00
|
|
|
import {FLAGS, LViewData, LViewFlags} from './interfaces/view';
|
2018-05-31 15:45:46 +02:00
|
|
|
import {destroyLView} from './node_manipulation';
|
2018-02-26 16:58:15 -08:00
|
|
|
|
2018-08-02 16:53:34 -07:00
|
|
|
|
2018-05-09 16:49:39 -07:00
|
|
|
// Needed due to tsickle downleveling where multiple `implements` with classes creates
|
|
|
|
// multiple @extends in Closure annotations, which is illegal. This workaround fixes
|
|
|
|
// the multiple @extends by making the annotation @implements instead
|
|
|
|
export interface viewEngine_ChangeDetectorRef_interface extends viewEngine_ChangeDetectorRef {}
|
|
|
|
|
|
|
|
export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_InternalViewRef,
|
|
|
|
viewEngine_ChangeDetectorRef_interface {
|
2018-07-20 14:32:23 +02:00
|
|
|
private _appRef: ApplicationRef|null = null;
|
|
|
|
private _viewContainerRef: viewEngine_ViewContainerRef|null = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2018-09-13 16:07:23 -07:00
|
|
|
_view: LViewData;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
_tViewNode: TViewNode|null = null;
|
2018-05-09 16:49:39 -07:00
|
|
|
|
2018-02-26 16:58:15 -08:00
|
|
|
context: T;
|
2018-06-18 16:38:33 -07:00
|
|
|
// TODO(issue/24571): remove '!'.
|
|
|
|
rootNodes !: any[];
|
2018-02-26 16:58:15 -08:00
|
|
|
|
2018-09-13 16:07:23 -07:00
|
|
|
constructor(_view: LViewData, context: T|null) {
|
|
|
|
this.context = context !;
|
|
|
|
this._view = _view;
|
|
|
|
}
|
2018-02-26 16:58:15 -08:00
|
|
|
|
|
|
|
/** @internal */
|
2018-06-07 22:42:32 -07:00
|
|
|
_setComponentContext(view: LViewData, context: T) {
|
2018-03-25 21:32:39 -07:00
|
|
|
this._view = view;
|
|
|
|
this.context = context;
|
|
|
|
}
|
2018-02-26 16:58:15 -08:00
|
|
|
|
2018-05-31 15:45:46 +02:00
|
|
|
get destroyed(): boolean {
|
2018-06-07 22:42:32 -07:00
|
|
|
return (this._view[FLAGS] & LViewFlags.Destroyed) === LViewFlags.Destroyed;
|
2018-05-31 15:45:46 +02:00
|
|
|
}
|
|
|
|
|
2018-07-20 14:32:23 +02:00
|
|
|
destroy(): void {
|
|
|
|
if (this._viewContainerRef && viewAttached(this._view)) {
|
|
|
|
this._viewContainerRef.detach(this._viewContainerRef.indexOf(this));
|
|
|
|
this._viewContainerRef = null;
|
|
|
|
}
|
|
|
|
destroyLView(this._view);
|
|
|
|
}
|
2018-05-31 15:45:46 +02:00
|
|
|
|
2018-06-05 15:28:15 -07:00
|
|
|
onDestroy(callback: Function) { storeCleanupFn(this._view, callback); }
|
2018-03-08 16:55:47 -08:00
|
|
|
|
|
|
|
/**
|
2018-03-09 12:45:31 -08:00
|
|
|
* Marks a view and all of its ancestors dirty.
|
2018-03-08 16:55:47 -08:00
|
|
|
*
|
2018-03-09 12:45:31 -08:00
|
|
|
* It also triggers change detection by calling `scheduleTick` internally, which coalesces
|
|
|
|
* multiple `markForCheck` calls to into one change detection run.
|
|
|
|
*
|
|
|
|
* This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is
|
|
|
|
* checked when it needs to be re-rendered but the two normal triggers haven't marked it
|
|
|
|
* dirty (i.e. inputs haven't changed and events haven't fired in the view).
|
|
|
|
*
|
|
|
|
* <!-- TODO: Add a link to a chapter on OnPush components -->
|
|
|
|
*
|
2018-05-18 16:13:00 +01:00
|
|
|
* @usageNotes
|
2018-05-17 11:23:55 -05:00
|
|
|
* ### Example
|
2018-03-09 12:45:31 -08:00
|
|
|
*
|
|
|
|
* ```typescript
|
|
|
|
* @Component({
|
|
|
|
* selector: 'my-app',
|
|
|
|
* template: `Number of ticks: {{numberOfTicks}}`
|
|
|
|
* changeDetection: ChangeDetectionStrategy.OnPush,
|
|
|
|
* })
|
|
|
|
* class AppComponent {
|
|
|
|
* numberOfTicks = 0;
|
|
|
|
*
|
|
|
|
* constructor(private ref: ChangeDetectorRef) {
|
|
|
|
* setInterval(() => {
|
|
|
|
* this.numberOfTicks++;
|
|
|
|
* // the following is required, otherwise the view will not be updated
|
|
|
|
* this.ref.markForCheck();
|
|
|
|
* }, 1000);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
markForCheck(): void { markViewDirty(this._view); }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Detaches the view from the change detection tree.
|
|
|
|
*
|
|
|
|
* Detached views will not be checked during change detection runs until they are
|
|
|
|
* re-attached, even if they are dirty. `detach` can be used in combination with
|
|
|
|
* {@link ChangeDetectorRef#detectChanges detectChanges} to implement local change
|
|
|
|
* detection checks.
|
|
|
|
*
|
|
|
|
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
|
|
|
* <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
|
|
|
*
|
2018-05-18 16:13:00 +01:00
|
|
|
* @usageNotes
|
2018-03-09 12:45:31 -08:00
|
|
|
* ### Example
|
|
|
|
*
|
|
|
|
* The following example defines a component with a large list of readonly data.
|
|
|
|
* Imagine the data changes constantly, many times per second. For performance reasons,
|
|
|
|
* we want to check and update the list every five seconds. We can do that by detaching
|
|
|
|
* the component's change detector and doing a local check every five seconds.
|
|
|
|
*
|
|
|
|
* ```typescript
|
|
|
|
* class DataProvider {
|
|
|
|
* // in a real application the returned data will be different every time
|
|
|
|
* get data() {
|
|
|
|
* return [1,2,3,4,5];
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* @Component({
|
|
|
|
* selector: 'giant-list',
|
|
|
|
* template: `
|
|
|
|
* <li *ngFor="let d of dataProvider.data">Data {{d}}</li>
|
|
|
|
* `,
|
|
|
|
* })
|
|
|
|
* class GiantList {
|
|
|
|
* constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
|
|
|
|
* ref.detach();
|
|
|
|
* setInterval(() => {
|
|
|
|
* this.ref.detectChanges();
|
|
|
|
* }, 5000);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* @Component({
|
|
|
|
* selector: 'app',
|
|
|
|
* providers: [DataProvider],
|
|
|
|
* template: `
|
|
|
|
* <giant-list><giant-list>
|
|
|
|
* `,
|
|
|
|
* })
|
|
|
|
* class App {
|
|
|
|
* }
|
|
|
|
* ```
|
2018-03-08 16:55:47 -08:00
|
|
|
*/
|
2018-06-07 22:42:32 -07:00
|
|
|
detach(): void { this._view[FLAGS] &= ~LViewFlags.Attached; }
|
2018-03-08 16:55:47 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Re-attaches a view to the change detection tree.
|
|
|
|
*
|
|
|
|
* This can be used to re-attach views that were previously detached from the tree
|
2018-03-09 12:45:31 -08:00
|
|
|
* using {@link ChangeDetectorRef#detach detach}. Views are attached to the tree by default.
|
|
|
|
*
|
|
|
|
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
|
|
|
*
|
2018-05-18 16:13:00 +01:00
|
|
|
* @usageNotes
|
2018-05-17 11:23:55 -05:00
|
|
|
* ### Example
|
2018-03-09 12:45:31 -08:00
|
|
|
*
|
|
|
|
* The following example creates a component displaying `live` data. The component will detach
|
|
|
|
* its change detector from the main change detector tree when the component's live property
|
|
|
|
* is set to false.
|
|
|
|
*
|
|
|
|
* ```typescript
|
|
|
|
* class DataProvider {
|
|
|
|
* data = 1;
|
|
|
|
*
|
|
|
|
* constructor() {
|
|
|
|
* setInterval(() => {
|
|
|
|
* this.data = this.data * 2;
|
|
|
|
* }, 500);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* @Component({
|
|
|
|
* selector: 'live-data',
|
|
|
|
* inputs: ['live'],
|
|
|
|
* template: 'Data: {{dataProvider.data}}'
|
|
|
|
* })
|
|
|
|
* class LiveData {
|
|
|
|
* constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
|
|
|
|
*
|
|
|
|
* set live(value) {
|
|
|
|
* if (value) {
|
|
|
|
* this.ref.reattach();
|
|
|
|
* } else {
|
|
|
|
* this.ref.detach();
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* @Component({
|
|
|
|
* selector: 'my-app',
|
|
|
|
* providers: [DataProvider],
|
|
|
|
* template: `
|
|
|
|
* Live Update: <input type="checkbox" [(ngModel)]="live">
|
|
|
|
* <live-data [live]="live"><live-data>
|
|
|
|
* `,
|
|
|
|
* })
|
|
|
|
* class AppComponent {
|
|
|
|
* live = true;
|
|
|
|
* }
|
|
|
|
* ```
|
2018-03-08 16:55:47 -08:00
|
|
|
*/
|
2018-06-07 22:42:32 -07:00
|
|
|
reattach(): void { this._view[FLAGS] |= LViewFlags.Attached; }
|
2018-03-06 11:58:08 -08:00
|
|
|
|
2018-03-09 12:45:31 -08:00
|
|
|
/**
|
|
|
|
* Checks the view and its children.
|
|
|
|
*
|
|
|
|
* This can also be used in combination with {@link ChangeDetectorRef#detach detach} to implement
|
|
|
|
* local change detection checks.
|
|
|
|
*
|
|
|
|
* <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
|
|
|
* <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
|
|
|
*
|
2018-05-18 16:13:00 +01:00
|
|
|
* @usageNotes
|
2018-03-09 12:45:31 -08:00
|
|
|
* ### Example
|
|
|
|
*
|
|
|
|
* The following example defines a component with a large list of readonly data.
|
|
|
|
* Imagine, the data changes constantly, many times per second. For performance reasons,
|
|
|
|
* we want to check and update the list every five seconds.
|
|
|
|
*
|
|
|
|
* We can do that by detaching the component's change detector and doing a local change detection
|
|
|
|
* check every five seconds.
|
|
|
|
*
|
|
|
|
* See {@link ChangeDetectorRef#detach detach} for more information.
|
|
|
|
*/
|
2018-08-02 16:53:34 -07:00
|
|
|
detectChanges(): void {
|
|
|
|
const rendererFactory = getRendererFactory();
|
|
|
|
if (rendererFactory.begin) {
|
|
|
|
rendererFactory.begin();
|
|
|
|
}
|
|
|
|
detectChanges(this.context);
|
|
|
|
if (rendererFactory.end) {
|
|
|
|
rendererFactory.end();
|
|
|
|
}
|
|
|
|
}
|
2018-03-06 11:58:08 -08:00
|
|
|
|
2018-03-09 20:22:18 -08:00
|
|
|
/**
|
|
|
|
* Checks the change detector and its children, and throws if any changes are detected.
|
|
|
|
*
|
|
|
|
* This is used in development mode to verify that running change detection doesn't
|
|
|
|
* introduce other changes.
|
|
|
|
*/
|
|
|
|
checkNoChanges(): void { checkNoChanges(this.context); }
|
2018-05-09 16:49:39 -07:00
|
|
|
|
2018-07-20 14:32:23 +02:00
|
|
|
attachToViewContainerRef(vcRef: viewEngine_ViewContainerRef) { this._viewContainerRef = vcRef; }
|
|
|
|
|
2018-05-09 16:49:39 -07:00
|
|
|
detachFromAppRef() { this._appRef = null; }
|
|
|
|
|
|
|
|
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
2018-02-26 16:58:15 -08:00
|
|
|
}
|
2018-07-25 11:07:54 +02:00
|
|
|
|
|
|
|
/** @internal */
|
|
|
|
export class RootViewRef<T> extends ViewRef<T> {
|
2018-09-13 16:07:23 -07:00
|
|
|
constructor(public _view: LViewData) { super(_view, null); }
|
2018-07-25 11:07:54 +02:00
|
|
|
|
|
|
|
detectChanges(): void { detectChangesInRootView(this._view); }
|
|
|
|
|
|
|
|
checkNoChanges(): void { checkNoChangesInRootView(this._view); }
|
|
|
|
}
|