2016-06-23 09:47:54 -07: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
|
|
|
|
|
*/
|
|
|
|
|
|
2017-01-27 13:19:00 -08:00
|
|
|
|
2016-11-04 11:58:06 -07:00
|
|
|
import {ApplicationRef} from '../application_ref';
|
2016-08-02 15:53:34 -07:00
|
|
|
import {ChangeDetectorRef, ChangeDetectorStatus} from '../change_detection/change_detection';
|
2016-11-01 11:45:27 -07:00
|
|
|
import {Injector, THROW_IF_NOT_FOUND} from '../di/injector';
|
2017-01-27 13:19:00 -08:00
|
|
|
import {getType} from '../errors';
|
2016-07-13 11:01:32 -07:00
|
|
|
import {isPresent} from '../facade/lang';
|
2016-08-02 15:53:34 -07:00
|
|
|
import {WtfScopeFn, wtfCreateScope, wtfLeave} from '../profile/profile';
|
2017-01-04 13:59:43 -08:00
|
|
|
import {DirectRenderer, RenderComponentType, Renderer} from '../render/api';
|
2016-08-02 15:53:34 -07:00
|
|
|
|
2016-10-12 11:24:54 -07:00
|
|
|
import {AnimationViewContext} from './animation_view_context';
|
2016-11-01 08:21:39 -07:00
|
|
|
import {ComponentRef} from './component_factory';
|
2016-08-02 15:53:34 -07:00
|
|
|
import {DebugContext, StaticNodeDebugInfo} from './debug_context';
|
|
|
|
|
import {ElementInjector} from './element_injector';
|
2017-01-27 13:19:00 -08:00
|
|
|
import {expressionChangedAfterItHasBeenCheckedError, viewDestroyedError, viewWrappedError} from './errors';
|
2016-11-01 11:12:25 -07:00
|
|
|
import {ViewContainer} from './view_container';
|
2016-04-13 17:05:17 -07:00
|
|
|
import {ViewRef_} from './view_ref';
|
2015-12-02 10:35:51 -08:00
|
|
|
import {ViewType} from './view_type';
|
2016-10-31 14:41:16 -07:00
|
|
|
import {ViewUtils, addToArray} from './view_utils';
|
2016-05-25 12:46:22 -07:00
|
|
|
|
2016-11-12 14:08:58 +01:00
|
|
|
const _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2016-11-01 08:21:39 -07:00
|
|
|
/**
|
|
|
|
|
* @experimental
|
|
|
|
|
*/
|
|
|
|
|
const EMPTY_CONTEXT = new Object();
|
|
|
|
|
|
2016-11-01 11:45:27 -07:00
|
|
|
const UNDEFINED = new Object();
|
|
|
|
|
|
2014-11-21 15:13:01 -08:00
|
|
|
/**
|
2015-07-07 08:15:58 +02:00
|
|
|
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
|
2015-03-31 22:47:11 +00:00
|
|
|
*
|
2014-10-10 20:44:55 -07:00
|
|
|
*/
|
2016-01-06 14:13:44 -08:00
|
|
|
export abstract class AppView<T> {
|
2016-04-28 14:00:31 -07:00
|
|
|
ref: ViewRef_<T>;
|
2016-10-31 14:41:16 -07:00
|
|
|
lastRootNode: any;
|
2015-12-02 10:35:51 -08:00
|
|
|
allNodes: any[];
|
|
|
|
|
disposables: Function[];
|
2016-11-04 11:58:06 -07:00
|
|
|
viewContainer: ViewContainer;
|
|
|
|
|
// This will be set if a view is directly attached to an ApplicationRef
|
|
|
|
|
// and not to a view container.
|
|
|
|
|
appRef: ApplicationRef;
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2016-06-27 20:00:30 -07:00
|
|
|
numberOfChecks: number = 0;
|
refactor(compiler): generate less code for bindings to DOM elements
Detailed changes:
- remove `UNINITIALIZED`, initialize change detection fields with `undefined`.
* we use `view.numberOfChecks === 0` now everywhere
as indicator whether we are in the first change detection cycle
(previously we used this only in a couple of places).
* we keep the initialization itself as change detection get slower without it.
- remove passing around `throwOnChange` in various generated calls,
and store it on the view as property instead.
- change generated code for bindings to DOM elements as follows:
Before:
```
var currVal_10 = self.context.bgColor;
if (jit_checkBinding15(self.throwOnChange,self._expr_10,currVal_10)) {
self.renderer.setElementStyle(self._el_0,'backgroundColor',((self.viewUtils.sanitizer.sanitize(jit_21,currVal_10) == null)? null: self.viewUtils.sanitizer.sanitize(jit_21,currVal_10).toString()));
self._expr_10 = currVal_10;
}
var currVal_11 = jit_inlineInterpolate16(1,' ',self.context.data.value,' ');
if (jit_checkBinding15(self.throwOnChange,self._expr_11,currVal_11)) {
self.renderer.setText(self._text_1,currVal_11);
self._expr_11 = currVal_11;
}
```,
After:
```
var currVal_10 = self.context.bgColor;
jit_checkRenderStyle14(self,self._el_0,'backgroundColor',null,self._expr_10,self._expr_10=currVal_10,false,jit_21);
var currVal_11 = jit_inlineInterpolate15(1,' ',self.context.data.value,' ');
jit_checkRenderText16(self,self._text_1,self._expr_11,self._expr_11=currVal_11,false);
```
Performance impact:
- None seen (checked against internal latency lab)
Part of #13651
2016-12-29 15:03:55 -08:00
|
|
|
throwOnChange: boolean = false;
|
2015-06-24 13:46:39 -07:00
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
renderer: Renderer;
|
2015-12-02 10:35:51 -08:00
|
|
|
|
2016-04-13 17:05:17 -07:00
|
|
|
private _hasExternalHostElement: boolean;
|
2016-11-01 11:45:27 -07:00
|
|
|
private _hostInjector: Injector;
|
|
|
|
|
private _hostProjectableNodes: any[][];
|
2016-10-12 11:24:54 -07:00
|
|
|
private _animationContext: AnimationViewContext;
|
2016-11-02 17:54:05 -07:00
|
|
|
private _directRenderer: DirectRenderer;
|
2016-08-22 17:18:25 -07:00
|
|
|
|
2016-04-28 14:00:31 -07:00
|
|
|
public context: T;
|
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
constructor(
|
|
|
|
|
public clazz: any, public componentType: RenderComponentType, public type: ViewType,
|
2016-11-01 11:45:27 -07:00
|
|
|
public viewUtils: ViewUtils, public parentView: AppView<any>, public parentIndex: number,
|
2016-11-03 15:32:44 -07:00
|
|
|
public parentElement: any, public cdMode: ChangeDetectorStatus,
|
|
|
|
|
public declaredViewContainer: ViewContainer = null) {
|
2016-12-09 13:04:18 -08:00
|
|
|
this.ref = new ViewRef_(this, viewUtils.animationQueue);
|
2016-01-06 14:13:44 -08:00
|
|
|
if (type === ViewType.COMPONENT || type === ViewType.HOST) {
|
2016-04-18 13:24:42 -07:00
|
|
|
this.renderer = viewUtils.renderComponent(componentType);
|
2016-01-06 14:13:44 -08:00
|
|
|
} else {
|
2016-11-01 09:35:03 -07:00
|
|
|
this.renderer = parentView.renderer;
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
2016-11-02 17:54:05 -07:00
|
|
|
this._directRenderer = (this.renderer as any).directRenderer;
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
|
2016-10-12 11:24:54 -07:00
|
|
|
get animationContext(): AnimationViewContext {
|
|
|
|
|
if (!this._animationContext) {
|
2016-12-09 13:04:18 -08:00
|
|
|
this._animationContext = new AnimationViewContext(this.viewUtils.animationQueue);
|
2016-08-22 17:18:25 -07:00
|
|
|
}
|
2016-10-12 11:24:54 -07:00
|
|
|
return this._animationContext;
|
2016-08-22 17:18:25 -07:00
|
|
|
}
|
|
|
|
|
|
2016-10-12 11:24:54 -07:00
|
|
|
get destroyed(): boolean { return this.cdMode === ChangeDetectorStatus.Destroyed; }
|
2016-08-22 17:18:25 -07:00
|
|
|
|
2016-11-01 08:21:39 -07:00
|
|
|
create(context: T) {
|
2016-04-28 14:00:31 -07:00
|
|
|
this.context = context;
|
2016-11-01 08:21:39 -07:00
|
|
|
return this.createInternal(null);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 11:45:27 -07:00
|
|
|
createHostView(rootSelectorOrNode: string|any, hostInjector: Injector, projectableNodes: any[][]):
|
|
|
|
|
ComponentRef<any> {
|
2016-11-01 08:21:39 -07:00
|
|
|
this.context = <any>EMPTY_CONTEXT;
|
2016-04-13 17:05:17 -07:00
|
|
|
this._hasExternalHostElement = isPresent(rootSelectorOrNode);
|
2016-11-01 11:45:27 -07:00
|
|
|
this._hostInjector = hostInjector;
|
|
|
|
|
this._hostProjectableNodes = projectableNodes;
|
2016-04-29 09:11:57 -07:00
|
|
|
return this.createInternal(rootSelectorOrNode);
|
2015-12-02 10:35:51 -08:00
|
|
|
}
|
2015-06-24 13:46:39 -07:00
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
/**
|
2016-04-18 13:24:42 -07:00
|
|
|
* Overwritten by implementations.
|
2016-11-01 08:21:39 -07:00
|
|
|
* Returns the ComponentRef for the host element for ViewType.HOST.
|
2016-01-06 14:13:44 -08:00
|
|
|
*/
|
2016-11-01 08:21:39 -07:00
|
|
|
createInternal(rootSelectorOrNode: string|any): ComponentRef<any> { return null; }
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2016-11-01 10:29:25 -07:00
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations.
|
|
|
|
|
*/
|
|
|
|
|
createEmbeddedViewInternal(templateNodeIndex: number): AppView<any> { return null; }
|
|
|
|
|
|
2016-10-31 14:41:16 -07:00
|
|
|
init(lastRootNode: any, allNodes: any[], disposables: Function[]) {
|
|
|
|
|
this.lastRootNode = lastRootNode;
|
2015-12-02 10:35:51 -08:00
|
|
|
this.allNodes = allNodes;
|
|
|
|
|
this.disposables = disposables;
|
2016-01-06 14:13:44 -08:00
|
|
|
if (this.type === ViewType.COMPONENT) {
|
|
|
|
|
this.dirtyParentQueriesInternal();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 11:45:27 -07:00
|
|
|
injectorGet(token: any, nodeIndex: number, notFoundValue: any = THROW_IF_NOT_FOUND): any {
|
|
|
|
|
let result = UNDEFINED;
|
|
|
|
|
let view: AppView<any> = this;
|
|
|
|
|
while (result === UNDEFINED) {
|
|
|
|
|
if (isPresent(nodeIndex)) {
|
|
|
|
|
result = view.injectorGetInternal(token, nodeIndex, UNDEFINED);
|
|
|
|
|
}
|
|
|
|
|
if (result === UNDEFINED && view.type === ViewType.HOST) {
|
|
|
|
|
result = view._hostInjector.get(token, notFoundValue);
|
|
|
|
|
}
|
|
|
|
|
nodeIndex = view.parentIndex;
|
|
|
|
|
view = view.parentView;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations
|
|
|
|
|
*/
|
|
|
|
|
injectorGetInternal(token: any, nodeIndex: number, notFoundResult: any): any {
|
|
|
|
|
return notFoundResult;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 11:45:27 -07:00
|
|
|
injector(nodeIndex: number): Injector { return new ElementInjector(this, nodeIndex); }
|
2014-12-01 18:41:55 -08:00
|
|
|
|
2016-10-31 10:31:48 -07:00
|
|
|
detachAndDestroy() {
|
2016-11-04 11:58:06 -07:00
|
|
|
if (this.viewContainer) {
|
2016-11-03 15:32:44 -07:00
|
|
|
this.viewContainer.detachView(this.viewContainer.nestedViews.indexOf(this));
|
2016-11-04 11:58:06 -07:00
|
|
|
} else if (this.appRef) {
|
|
|
|
|
this.appRef.detachView(this.ref);
|
|
|
|
|
} else if (this._hasExternalHostElement) {
|
|
|
|
|
this.detach();
|
2016-04-13 17:05:17 -07:00
|
|
|
}
|
2016-10-31 10:31:48 -07:00
|
|
|
this.destroy();
|
2016-04-13 17:05:17 -07:00
|
|
|
}
|
|
|
|
|
|
2016-10-31 10:31:48 -07:00
|
|
|
destroy() {
|
2016-06-27 20:00:30 -07:00
|
|
|
if (this.cdMode === ChangeDetectorStatus.Destroyed) {
|
2016-01-06 14:13:44 -08:00
|
|
|
return;
|
|
|
|
|
}
|
2016-11-12 14:08:58 +01:00
|
|
|
const hostElement = this.type === ViewType.COMPONENT ? this.parentElement : null;
|
2016-10-31 15:38:15 -07:00
|
|
|
if (this.disposables) {
|
2016-11-12 14:08:58 +01:00
|
|
|
for (let i = 0; i < this.disposables.length; i++) {
|
2016-10-31 15:38:15 -07:00
|
|
|
this.disposables[i]();
|
|
|
|
|
}
|
2014-12-01 18:41:55 -08:00
|
|
|
}
|
2016-01-06 14:13:44 -08:00
|
|
|
this.destroyInternal();
|
2016-05-04 11:53:12 -07:00
|
|
|
this.dirtyParentQueriesInternal();
|
2016-05-25 12:46:22 -07:00
|
|
|
|
2016-10-12 11:24:54 -07:00
|
|
|
if (this._animationContext) {
|
|
|
|
|
this._animationContext.onAllActiveAnimationsDone(
|
|
|
|
|
() => this.renderer.destroyView(hostElement, this.allNodes));
|
2016-05-25 12:46:22 -07:00
|
|
|
} else {
|
2016-10-12 11:24:54 -07:00
|
|
|
this.renderer.destroyView(hostElement, this.allNodes);
|
2016-05-25 12:46:22 -07:00
|
|
|
}
|
2016-10-31 10:31:48 -07:00
|
|
|
|
|
|
|
|
this.cdMode = ChangeDetectorStatus.Destroyed;
|
2014-12-01 18:41:55 -08:00
|
|
|
}
|
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations
|
|
|
|
|
*/
|
|
|
|
|
destroyInternal(): void {}
|
|
|
|
|
|
2016-05-25 12:46:22 -07:00
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations
|
|
|
|
|
*/
|
|
|
|
|
detachInternal(): void {}
|
|
|
|
|
|
|
|
|
|
detach(): void {
|
|
|
|
|
this.detachInternal();
|
2016-10-12 11:24:54 -07:00
|
|
|
if (this._animationContext) {
|
2016-11-02 17:54:05 -07:00
|
|
|
this._animationContext.onAllActiveAnimationsDone(() => this._renderDetach());
|
|
|
|
|
} else {
|
|
|
|
|
this._renderDetach();
|
|
|
|
|
}
|
2016-12-14 17:33:29 +01:00
|
|
|
if (this.declaredViewContainer && this.declaredViewContainer !== this.viewContainer &&
|
|
|
|
|
this.declaredViewContainer.projectedViews) {
|
2016-11-03 15:32:44 -07:00
|
|
|
const projectedViews = this.declaredViewContainer.projectedViews;
|
|
|
|
|
const index = projectedViews.indexOf(this);
|
|
|
|
|
// perf: pop is faster than splice!
|
|
|
|
|
if (index >= projectedViews.length - 1) {
|
|
|
|
|
projectedViews.pop();
|
|
|
|
|
} else {
|
|
|
|
|
projectedViews.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-04 11:58:06 -07:00
|
|
|
this.appRef = null;
|
2016-11-03 15:32:44 -07:00
|
|
|
this.viewContainer = null;
|
|
|
|
|
this.dirtyParentQueriesInternal();
|
2016-11-02 17:54:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _renderDetach() {
|
|
|
|
|
if (this._directRenderer) {
|
|
|
|
|
this.visitRootNodesInternal(this._directRenderer.remove, null);
|
2016-05-25 12:46:22 -07:00
|
|
|
} else {
|
2016-10-12 11:24:54 -07:00
|
|
|
this.renderer.detachView(this.flatRootNodes);
|
2016-05-25 12:46:22 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-04 11:58:06 -07:00
|
|
|
attachToAppRef(appRef: ApplicationRef) {
|
|
|
|
|
if (this.viewContainer) {
|
|
|
|
|
throw new Error('This view is already attached to a ViewContainer!');
|
|
|
|
|
}
|
|
|
|
|
this.appRef = appRef;
|
|
|
|
|
this.dirtyParentQueriesInternal();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 15:32:44 -07:00
|
|
|
attachAfter(viewContainer: ViewContainer, prevView: AppView<any>) {
|
2016-11-04 11:58:06 -07:00
|
|
|
if (this.appRef) {
|
|
|
|
|
throw new Error('This view is already attached directly to the ApplicationRef!');
|
|
|
|
|
}
|
2016-11-03 15:32:44 -07:00
|
|
|
this._renderAttach(viewContainer, prevView);
|
|
|
|
|
this.viewContainer = viewContainer;
|
|
|
|
|
if (this.declaredViewContainer && this.declaredViewContainer !== viewContainer) {
|
|
|
|
|
if (!this.declaredViewContainer.projectedViews) {
|
|
|
|
|
this.declaredViewContainer.projectedViews = [];
|
|
|
|
|
}
|
|
|
|
|
this.declaredViewContainer.projectedViews.push(this);
|
|
|
|
|
}
|
|
|
|
|
this.dirtyParentQueriesInternal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
moveAfter(viewContainer: ViewContainer, prevView: AppView<any>) {
|
|
|
|
|
this._renderAttach(viewContainer, prevView);
|
|
|
|
|
this.dirtyParentQueriesInternal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _renderAttach(viewContainer: ViewContainer, prevView: AppView<any>) {
|
|
|
|
|
const prevNode = prevView ? prevView.lastRootNode : viewContainer.nativeElement;
|
2016-11-02 17:54:05 -07:00
|
|
|
if (this._directRenderer) {
|
|
|
|
|
const nextSibling = this._directRenderer.nextSibling(prevNode);
|
|
|
|
|
if (nextSibling) {
|
|
|
|
|
this.visitRootNodesInternal(this._directRenderer.insertBefore, nextSibling);
|
|
|
|
|
} else {
|
2016-11-16 10:00:18 -08:00
|
|
|
const parentElement = this._directRenderer.parentElement(prevNode);
|
|
|
|
|
if (parentElement) {
|
|
|
|
|
this.visitRootNodesInternal(this._directRenderer.appendChild, parentElement);
|
|
|
|
|
}
|
2016-11-02 17:54:05 -07:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.renderer.attachViewAfter(prevNode, this.flatRootNodes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
get changeDetectorRef(): ChangeDetectorRef { return this.ref; }
|
2014-12-01 18:41:55 -08:00
|
|
|
|
2016-10-31 14:41:16 -07:00
|
|
|
get flatRootNodes(): any[] {
|
|
|
|
|
const nodes: any[] = [];
|
|
|
|
|
this.visitRootNodesInternal(addToArray, nodes);
|
|
|
|
|
return nodes;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 17:54:05 -07:00
|
|
|
projectNodes(parentElement: any, ngContentIndex: number) {
|
|
|
|
|
if (this._directRenderer) {
|
|
|
|
|
this.visitProjectedNodes(ngContentIndex, this._directRenderer.appendChild, parentElement);
|
|
|
|
|
} else {
|
|
|
|
|
const nodes: any[] = [];
|
|
|
|
|
this.visitProjectedNodes(ngContentIndex, addToArray, nodes);
|
|
|
|
|
this.renderer.projectNodes(parentElement, nodes);
|
|
|
|
|
}
|
2016-10-31 14:41:16 -07:00
|
|
|
}
|
2015-12-02 10:35:51 -08:00
|
|
|
|
2016-10-31 14:41:16 -07:00
|
|
|
visitProjectedNodes<C>(ngContentIndex: number, cb: (node: any, ctx: C) => void, c: C): void {
|
|
|
|
|
switch (this.type) {
|
|
|
|
|
case ViewType.EMBEDDED:
|
2016-11-01 09:35:03 -07:00
|
|
|
this.parentView.visitProjectedNodes(ngContentIndex, cb, c);
|
2016-10-31 14:41:16 -07:00
|
|
|
break;
|
|
|
|
|
case ViewType.COMPONENT:
|
2016-11-01 11:45:27 -07:00
|
|
|
if (this.parentView.type === ViewType.HOST) {
|
|
|
|
|
const nodes = this.parentView._hostProjectableNodes[ngContentIndex] || [];
|
2016-11-12 14:08:58 +01:00
|
|
|
for (let i = 0; i < nodes.length; i++) {
|
2016-11-01 11:45:27 -07:00
|
|
|
cb(nodes[i], c);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.parentView.visitProjectableNodesInternal(this.parentIndex, ngContentIndex, cb, c);
|
|
|
|
|
}
|
2016-10-31 14:41:16 -07:00
|
|
|
break;
|
|
|
|
|
}
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
|
2016-10-31 14:41:16 -07:00
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations
|
|
|
|
|
*/
|
|
|
|
|
visitRootNodesInternal<C>(cb: (node: any, ctx: C) => void, c: C): void {}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations
|
|
|
|
|
*/
|
|
|
|
|
visitProjectableNodesInternal<C>(
|
|
|
|
|
nodeIndex: number, ngContentIndex: number, cb: (node: any, ctx: C) => void, c: C): void {}
|
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations
|
|
|
|
|
*/
|
|
|
|
|
dirtyParentQueriesInternal(): void {}
|
|
|
|
|
|
2016-12-13 16:26:22 -08:00
|
|
|
internalDetectChanges(throwOnChange: boolean): void {
|
|
|
|
|
if (this.cdMode !== ChangeDetectorStatus.Detached) {
|
|
|
|
|
this.detectChanges(throwOnChange);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
detectChanges(throwOnChange: boolean): void {
|
2016-11-12 14:08:58 +01:00
|
|
|
const s = _scope_check(this.clazz);
|
2016-06-27 20:00:30 -07:00
|
|
|
if (this.cdMode === ChangeDetectorStatus.Checked ||
|
2016-12-13 16:26:22 -08:00
|
|
|
this.cdMode === ChangeDetectorStatus.Errored)
|
2016-01-06 14:13:44 -08:00
|
|
|
return;
|
2016-06-27 20:00:30 -07:00
|
|
|
if (this.cdMode === ChangeDetectorStatus.Destroyed) {
|
2016-01-06 14:13:44 -08:00
|
|
|
this.throwDestroyedError('detectChanges');
|
|
|
|
|
}
|
refactor(compiler): generate less code for bindings to DOM elements
Detailed changes:
- remove `UNINITIALIZED`, initialize change detection fields with `undefined`.
* we use `view.numberOfChecks === 0` now everywhere
as indicator whether we are in the first change detection cycle
(previously we used this only in a couple of places).
* we keep the initialization itself as change detection get slower without it.
- remove passing around `throwOnChange` in various generated calls,
and store it on the view as property instead.
- change generated code for bindings to DOM elements as follows:
Before:
```
var currVal_10 = self.context.bgColor;
if (jit_checkBinding15(self.throwOnChange,self._expr_10,currVal_10)) {
self.renderer.setElementStyle(self._el_0,'backgroundColor',((self.viewUtils.sanitizer.sanitize(jit_21,currVal_10) == null)? null: self.viewUtils.sanitizer.sanitize(jit_21,currVal_10).toString()));
self._expr_10 = currVal_10;
}
var currVal_11 = jit_inlineInterpolate16(1,' ',self.context.data.value,' ');
if (jit_checkBinding15(self.throwOnChange,self._expr_11,currVal_11)) {
self.renderer.setText(self._text_1,currVal_11);
self._expr_11 = currVal_11;
}
```,
After:
```
var currVal_10 = self.context.bgColor;
jit_checkRenderStyle14(self,self._el_0,'backgroundColor',null,self._expr_10,self._expr_10=currVal_10,false,jit_21);
var currVal_11 = jit_inlineInterpolate15(1,' ',self.context.data.value,' ');
jit_checkRenderText16(self,self._text_1,self._expr_11,self._expr_11=currVal_11,false);
```
Performance impact:
- None seen (checked against internal latency lab)
Part of #13651
2016-12-29 15:03:55 -08:00
|
|
|
this.throwOnChange = throwOnChange;
|
|
|
|
|
this.detectChangesInternal();
|
2016-06-27 20:00:30 -07:00
|
|
|
if (this.cdMode === ChangeDetectorStatus.CheckOnce) this.cdMode = ChangeDetectorStatus.Checked;
|
2016-01-06 14:13:44 -08:00
|
|
|
|
2016-06-27 20:00:30 -07:00
|
|
|
this.numberOfChecks++;
|
2016-01-06 14:13:44 -08:00
|
|
|
wtfLeave(s);
|
2015-04-07 20:54:20 -07:00
|
|
|
}
|
2015-04-09 21:20:11 +02:00
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
/**
|
|
|
|
|
* Overwritten by implementations
|
|
|
|
|
*/
|
refactor(compiler): generate less code for bindings to DOM elements
Detailed changes:
- remove `UNINITIALIZED`, initialize change detection fields with `undefined`.
* we use `view.numberOfChecks === 0` now everywhere
as indicator whether we are in the first change detection cycle
(previously we used this only in a couple of places).
* we keep the initialization itself as change detection get slower without it.
- remove passing around `throwOnChange` in various generated calls,
and store it on the view as property instead.
- change generated code for bindings to DOM elements as follows:
Before:
```
var currVal_10 = self.context.bgColor;
if (jit_checkBinding15(self.throwOnChange,self._expr_10,currVal_10)) {
self.renderer.setElementStyle(self._el_0,'backgroundColor',((self.viewUtils.sanitizer.sanitize(jit_21,currVal_10) == null)? null: self.viewUtils.sanitizer.sanitize(jit_21,currVal_10).toString()));
self._expr_10 = currVal_10;
}
var currVal_11 = jit_inlineInterpolate16(1,' ',self.context.data.value,' ');
if (jit_checkBinding15(self.throwOnChange,self._expr_11,currVal_11)) {
self.renderer.setText(self._text_1,currVal_11);
self._expr_11 = currVal_11;
}
```,
After:
```
var currVal_10 = self.context.bgColor;
jit_checkRenderStyle14(self,self._el_0,'backgroundColor',null,self._expr_10,self._expr_10=currVal_10,false,jit_21);
var currVal_11 = jit_inlineInterpolate15(1,' ',self.context.data.value,' ');
jit_checkRenderText16(self,self._text_1,self._expr_11,self._expr_11=currVal_11,false);
```
Performance impact:
- None seen (checked against internal latency lab)
Part of #13651
2016-12-29 15:03:55 -08:00
|
|
|
detectChangesInternal(): void {}
|
2015-06-12 09:45:31 -07:00
|
|
|
|
2016-06-27 20:00:30 -07:00
|
|
|
markAsCheckOnce(): void { this.cdMode = ChangeDetectorStatus.CheckOnce; }
|
2015-06-24 13:46:39 -07:00
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
markPathToRootAsCheckOnce(): void {
|
2016-04-29 12:04:03 -07:00
|
|
|
let c: AppView<any> = this;
|
2016-06-27 20:00:30 -07:00
|
|
|
while (isPresent(c) && c.cdMode !== ChangeDetectorStatus.Detached) {
|
|
|
|
|
if (c.cdMode === ChangeDetectorStatus.Checked) {
|
|
|
|
|
c.cdMode = ChangeDetectorStatus.CheckOnce;
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
2016-11-01 09:35:03 -07:00
|
|
|
if (c.type === ViewType.COMPONENT) {
|
|
|
|
|
c = c.parentView;
|
|
|
|
|
} else {
|
2016-11-03 15:32:44 -07:00
|
|
|
c = c.viewContainer ? c.viewContainer.parentView : null;
|
2016-11-01 09:35:03 -07:00
|
|
|
}
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
2014-12-02 17:09:46 -08:00
|
|
|
}
|
2014-09-28 16:29:11 -07:00
|
|
|
|
2016-10-26 16:58:35 -07:00
|
|
|
eventHandler<E, R>(cb: (eventName: string, event?: E) => R): (eventName: string, event?: E) => R {
|
|
|
|
|
return cb;
|
|
|
|
|
}
|
2016-04-29 09:11:57 -07:00
|
|
|
|
2017-01-27 13:19:00 -08:00
|
|
|
throwDestroyedError(details: string): void { throw viewDestroyedError(details); }
|
2016-04-29 09:11:57 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class DebugAppView<T> extends AppView<T> {
|
|
|
|
|
private _currentDebugContext: DebugContext = null;
|
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
constructor(
|
|
|
|
|
clazz: any, componentType: RenderComponentType, type: ViewType, viewUtils: ViewUtils,
|
2016-11-01 11:45:27 -07:00
|
|
|
parentView: AppView<any>, parentIndex: number, parentNode: any, cdMode: ChangeDetectorStatus,
|
2016-11-03 15:32:44 -07:00
|
|
|
public staticNodeDebugInfos: StaticNodeDebugInfo[],
|
|
|
|
|
declaredViewContainer: ViewContainer = null) {
|
|
|
|
|
super(
|
|
|
|
|
clazz, componentType, type, viewUtils, parentView, parentIndex, parentNode, cdMode,
|
|
|
|
|
declaredViewContainer);
|
2016-04-29 09:11:57 -07:00
|
|
|
}
|
|
|
|
|
|
2016-11-01 08:21:39 -07:00
|
|
|
create(context: T) {
|
|
|
|
|
this._resetDebug();
|
|
|
|
|
try {
|
|
|
|
|
return super.create(context);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
this._rethrowWithContext(e);
|
|
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 11:45:27 -07:00
|
|
|
createHostView(
|
|
|
|
|
rootSelectorOrNode: string|any, injector: Injector,
|
|
|
|
|
projectableNodes: any[][] = null): ComponentRef<any> {
|
2016-04-29 09:11:57 -07:00
|
|
|
this._resetDebug();
|
|
|
|
|
try {
|
2016-11-01 11:45:27 -07:00
|
|
|
return super.createHostView(rootSelectorOrNode, injector, projectableNodes);
|
2016-04-29 09:11:57 -07:00
|
|
|
} catch (e) {
|
2016-08-25 00:50:16 -07:00
|
|
|
this._rethrowWithContext(e);
|
2016-04-29 09:11:57 -07:00
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-02 07:36:31 -07:00
|
|
|
injectorGet(token: any, nodeIndex: number, notFoundResult?: any): any {
|
2016-04-29 09:11:57 -07:00
|
|
|
this._resetDebug();
|
|
|
|
|
try {
|
|
|
|
|
return super.injectorGet(token, nodeIndex, notFoundResult);
|
|
|
|
|
} catch (e) {
|
2016-08-25 00:50:16 -07:00
|
|
|
this._rethrowWithContext(e);
|
2016-04-29 09:11:57 -07:00
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-25 12:46:22 -07:00
|
|
|
detach(): void {
|
|
|
|
|
this._resetDebug();
|
|
|
|
|
try {
|
|
|
|
|
super.detach();
|
|
|
|
|
} catch (e) {
|
2016-08-25 00:50:16 -07:00
|
|
|
this._rethrowWithContext(e);
|
2016-05-25 12:46:22 -07:00
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-31 10:31:48 -07:00
|
|
|
destroy() {
|
2016-04-29 09:11:57 -07:00
|
|
|
this._resetDebug();
|
|
|
|
|
try {
|
2016-10-31 10:31:48 -07:00
|
|
|
super.destroy();
|
2016-04-29 09:11:57 -07:00
|
|
|
} catch (e) {
|
2016-08-25 00:50:16 -07:00
|
|
|
this._rethrowWithContext(e);
|
2016-04-29 09:11:57 -07:00
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
detectChanges(throwOnChange: boolean): void {
|
|
|
|
|
this._resetDebug();
|
|
|
|
|
try {
|
|
|
|
|
super.detectChanges(throwOnChange);
|
|
|
|
|
} catch (e) {
|
2016-08-25 00:50:16 -07:00
|
|
|
this._rethrowWithContext(e);
|
2016-04-29 09:11:57 -07:00
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
private _resetDebug() { this._currentDebugContext = null; }
|
|
|
|
|
|
|
|
|
|
debug(nodeIndex: number, rowNum: number, colNum: number): DebugContext {
|
|
|
|
|
return this._currentDebugContext = new DebugContext(this, nodeIndex, rowNum, colNum);
|
2015-07-23 18:01:34 -07:00
|
|
|
}
|
|
|
|
|
|
2016-08-25 00:50:16 -07:00
|
|
|
private _rethrowWithContext(e: any) {
|
2017-01-27 13:19:00 -08:00
|
|
|
if (!(getType(e) == viewWrappedError)) {
|
|
|
|
|
if (!(getType(e) == expressionChangedAfterItHasBeenCheckedError)) {
|
2016-06-27 20:00:30 -07:00
|
|
|
this.cdMode = ChangeDetectorStatus.Errored;
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
if (isPresent(this._currentDebugContext)) {
|
2017-01-27 13:19:00 -08:00
|
|
|
throw viewWrappedError(e, this._currentDebugContext);
|
2015-12-02 10:35:51 -08:00
|
|
|
}
|
|
|
|
|
}
|
2015-07-27 15:47:42 -07:00
|
|
|
}
|
2015-12-02 10:35:51 -08:00
|
|
|
|
2016-10-26 16:58:35 -07:00
|
|
|
eventHandler<E, R>(cb: (eventName: string, event?: E) => R): (eventName: string, event?: E) => R {
|
2016-11-12 14:08:58 +01:00
|
|
|
const superHandler = super.eventHandler(cb);
|
2016-10-26 16:58:35 -07:00
|
|
|
return (eventName: string, event?: any) => {
|
2016-04-29 09:11:57 -07:00
|
|
|
this._resetDebug();
|
|
|
|
|
try {
|
2016-10-28 10:44:48 -07:00
|
|
|
return superHandler.call(this, eventName, event);
|
2016-04-29 09:11:57 -07:00
|
|
|
} catch (e) {
|
2016-08-25 00:50:16 -07:00
|
|
|
this._rethrowWithContext(e);
|
2016-04-29 09:11:57 -07:00
|
|
|
throw e;
|
|
|
|
|
}
|
|
|
|
|
};
|
2016-01-06 14:13:44 -08:00
|
|
|
}
|
|
|
|
|
}
|