fix(core): check components if an event handler inside of an embedded view fires.
BREAKING CHANGE: - ViewRef.changeDetectorRef was removed as using ChangeDetectorRefs for EmbeddedViewRefs does not make sense. Use ComponentRef.changeDetectorRef or inject ChangeDetectorRef instead. Fixes #8242
This commit is contained in:
parent
11955f9b13
commit
4d691b61ee
@ -62,7 +62,7 @@ export class ComponentRef_ extends ComponentRef {
|
|||||||
get injector(): Injector { return this._hostElement.injector; }
|
get injector(): Injector { return this._hostElement.injector; }
|
||||||
get instance(): any { return this._hostElement.component; };
|
get instance(): any { return this._hostElement.component; };
|
||||||
get hostView(): ViewRef { return this._hostElement.parentView.ref; };
|
get hostView(): ViewRef { return this._hostElement.parentView.ref; };
|
||||||
get changeDetectorRef(): ChangeDetectorRef { return this.hostView; };
|
get changeDetectorRef(): ChangeDetectorRef { return this._hostElement.parentView.ref; };
|
||||||
get componentType(): Type { return this._componentType; }
|
get componentType(): Type { return this._componentType; }
|
||||||
|
|
||||||
destroy(): void { this._hostElement.parentView.destroy(); }
|
destroy(): void { this._hostElement.parentView.destroy(); }
|
||||||
|
@ -68,7 +68,6 @@ export abstract class AppView<T> {
|
|||||||
subscriptions: any[];
|
subscriptions: any[];
|
||||||
contentChildren: AppView<any>[] = [];
|
contentChildren: AppView<any>[] = [];
|
||||||
viewChildren: AppView<any>[] = [];
|
viewChildren: AppView<any>[] = [];
|
||||||
renderParent: AppView<any>;
|
|
||||||
viewContainerElement: AppElement = null;
|
viewContainerElement: AppElement = null;
|
||||||
|
|
||||||
// The names of the below fields must be kept in sync with codegen_name_util.ts or
|
// The names of the below fields must be kept in sync with codegen_name_util.ts or
|
||||||
@ -134,7 +133,6 @@ export abstract class AppView<T> {
|
|||||||
// Note: the render nodes have been attached to their host element
|
// Note: the render nodes have been attached to their host element
|
||||||
// in the ViewFactory already.
|
// in the ViewFactory already.
|
||||||
this.declarationAppElement.parentView.viewChildren.push(this);
|
this.declarationAppElement.parentView.viewChildren.push(this);
|
||||||
this.renderParent = this.declarationAppElement.parentView;
|
|
||||||
this.dirtyParentQueriesInternal();
|
this.dirtyParentQueriesInternal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,18 +238,6 @@ export abstract class AppView<T> {
|
|||||||
*/
|
*/
|
||||||
dirtyParentQueriesInternal(): void {}
|
dirtyParentQueriesInternal(): void {}
|
||||||
|
|
||||||
addRenderContentChild(view: AppView<any>): void {
|
|
||||||
this.contentChildren.push(view);
|
|
||||||
view.renderParent = this;
|
|
||||||
view.dirtyParentQueriesInternal();
|
|
||||||
}
|
|
||||||
|
|
||||||
removeContentChild(view: AppView<any>): void {
|
|
||||||
ListWrapper.remove(this.contentChildren, view);
|
|
||||||
view.dirtyParentQueriesInternal();
|
|
||||||
view.renderParent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
detectChanges(throwOnChange: boolean): void {
|
detectChanges(throwOnChange: boolean): void {
|
||||||
var s = _scope_check(this.clazz);
|
var s = _scope_check(this.clazz);
|
||||||
if (this.cdMode === ChangeDetectionStrategy.Detached ||
|
if (this.cdMode === ChangeDetectionStrategy.Detached ||
|
||||||
@ -304,12 +290,14 @@ export abstract class AppView<T> {
|
|||||||
markAsCheckOnce(): void { this.cdMode = ChangeDetectionStrategy.CheckOnce; }
|
markAsCheckOnce(): void { this.cdMode = ChangeDetectionStrategy.CheckOnce; }
|
||||||
|
|
||||||
markPathToRootAsCheckOnce(): void {
|
markPathToRootAsCheckOnce(): void {
|
||||||
var c: AppView<any> = this;
|
let c: AppView<any> = this;
|
||||||
while (isPresent(c) && c.cdMode !== ChangeDetectionStrategy.Detached) {
|
while (isPresent(c) && c.cdMode !== ChangeDetectionStrategy.Detached) {
|
||||||
if (c.cdMode === ChangeDetectionStrategy.Checked) {
|
if (c.cdMode === ChangeDetectionStrategy.Checked) {
|
||||||
c.cdMode = ChangeDetectionStrategy.CheckOnce;
|
c.cdMode = ChangeDetectionStrategy.CheckOnce;
|
||||||
}
|
}
|
||||||
c = c.renderParent;
|
let parentEl =
|
||||||
|
c.type === ViewType.COMPONENT ? c.declarationAppElement : c.viewContainerElement;
|
||||||
|
c = isPresent(parentEl) ? parentEl.parentView : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,7 @@ import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
|||||||
import {AppView} from './view';
|
import {AppView} from './view';
|
||||||
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection/constants';
|
import {ChangeDetectionStrategy} from 'angular2/src/core/change_detection/constants';
|
||||||
|
|
||||||
export abstract class ViewRef extends ChangeDetectorRef {
|
export abstract class ViewRef {
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
get changeDetectorRef(): ChangeDetectorRef { return <ChangeDetectorRef>unimplemented(); };
|
|
||||||
|
|
||||||
get destroyed(): boolean { return <boolean>unimplemented(); }
|
get destroyed(): boolean { return <boolean>unimplemented(); }
|
||||||
|
|
||||||
abstract onDestroy(callback: Function);
|
abstract onDestroy(callback: Function);
|
||||||
@ -79,16 +74,11 @@ export abstract class EmbeddedViewRef<C> extends ViewRef {
|
|||||||
abstract destroy();
|
abstract destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ViewRef_<C> implements EmbeddedViewRef<C> {
|
export class ViewRef_<C> implements EmbeddedViewRef<C>, ChangeDetectorRef {
|
||||||
constructor(private _view: AppView<C>) { this._view = _view; }
|
constructor(private _view: AppView<C>) { this._view = _view; }
|
||||||
|
|
||||||
get internalView(): AppView<C> { return this._view; }
|
get internalView(): AppView<C> { return this._view; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Return `ChangeDetectorRef`
|
|
||||||
*/
|
|
||||||
get changeDetectorRef(): ChangeDetectorRef { return this; }
|
|
||||||
|
|
||||||
get rootNodes(): any[] { return this._view.flatRootNodes; }
|
get rootNodes(): any[] { return this._view.flatRootNodes; }
|
||||||
|
|
||||||
get context() { return this._view.context; }
|
get context() { return this._view.context; }
|
||||||
|
@ -800,16 +800,33 @@ function declareTests(isJit: boolean) {
|
|||||||
|
|
||||||
.createAsync(MyComp)
|
.createAsync(MyComp)
|
||||||
.then((fixture) => {
|
.then((fixture) => {
|
||||||
var cmp = fixture.debugElement.children[0].references['cmp'];
|
var cmpEl = fixture.debugElement.children[0];
|
||||||
|
var cmp = cmpEl.componentInstance;
|
||||||
fixture.debugElement.componentInstance.ctxProp = "one";
|
fixture.detectChanges();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(cmp.numberOfChecks).toEqual(1);
|
expect(cmp.numberOfChecks).toEqual(1);
|
||||||
|
|
||||||
fixture.debugElement.componentInstance.ctxProp = "two";
|
cmpEl.children[0].triggerEventHandler('click', <Event>{});
|
||||||
|
|
||||||
|
// regular element
|
||||||
|
fixture.detectChanges();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(cmp.numberOfChecks).toEqual(2);
|
expect(cmp.numberOfChecks).toEqual(2);
|
||||||
|
|
||||||
|
// element inside of an *ngIf
|
||||||
|
cmpEl.children[1].triggerEventHandler('click', <Event>{});
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(cmp.numberOfChecks).toEqual(3);
|
||||||
|
|
||||||
|
// element inside a nested component
|
||||||
|
cmpEl.children[2].children[0].triggerEventHandler('click', <Event>{});
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(cmp.numberOfChecks).toEqual(4);
|
||||||
|
|
||||||
async.done();
|
async.done();
|
||||||
})}));
|
})}));
|
||||||
|
|
||||||
@ -1987,11 +2004,18 @@ class DirectiveWithTitleAndHostProperty {
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'event-cmp', template: '<div (click)="noop()"></div>'})
|
||||||
|
class EventCmp {
|
||||||
|
noop() {}
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'push-cmp',
|
selector: 'push-cmp',
|
||||||
inputs: ['prop'],
|
inputs: ['prop'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
template: '{{field}}'
|
template:
|
||||||
|
'{{field}}<div (click)="noop()"></div><div *ngIf="true" (click)="noop()"></div><event-cmp></event-cmp>',
|
||||||
|
directives: [EventCmp, NgIf]
|
||||||
})
|
})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class PushCmp {
|
class PushCmp {
|
||||||
@ -2000,6 +2024,8 @@ class PushCmp {
|
|||||||
|
|
||||||
constructor() { this.numberOfChecks = 0; }
|
constructor() { this.numberOfChecks = 0; }
|
||||||
|
|
||||||
|
noop() {}
|
||||||
|
|
||||||
get field() {
|
get field() {
|
||||||
this.numberOfChecks++;
|
this.numberOfChecks++;
|
||||||
return "fixed";
|
return "fixed";
|
||||||
|
@ -477,7 +477,6 @@ const CORE = [
|
|||||||
'ViewQueryMetadata.isViewQuery:any',
|
'ViewQueryMetadata.isViewQuery:any',
|
||||||
'ViewQueryMetadata.toString():string',
|
'ViewQueryMetadata.toString():string',
|
||||||
'ViewRef',
|
'ViewRef',
|
||||||
'ViewRef.changeDetectorRef:ChangeDetectorRef',
|
|
||||||
'ViewRef.destroyed:boolean',
|
'ViewRef.destroyed:boolean',
|
||||||
'ViewRef.onDestroy(callback:Function):any',
|
'ViewRef.onDestroy(callback:Function):any',
|
||||||
'WrappedException',
|
'WrappedException',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user