fix(query): set fixed `@ViewChild` / `@ContentChild` right after the view is created
This is needed to have a true replacement of the previous `DynamicComponentLoader.loadNextToLocation`, so that components can be loaded into the view before change detection runs. Closes #9040
This commit is contained in:
parent
8847580fd7
commit
c3d2459a4e
|
@ -230,7 +230,8 @@ export class CompileElement extends CompileNode {
|
||||||
|
|
||||||
this._queries.values().forEach(
|
this._queries.values().forEach(
|
||||||
(queries) =>
|
(queries) =>
|
||||||
queries.forEach((query) => query.afterChildren(this.view.updateContentQueriesMethod)));
|
queries.forEach((query) => query.afterChildren(this.view.createMethod,
|
||||||
|
this.view.updateContentQueriesMethod)));
|
||||||
}
|
}
|
||||||
|
|
||||||
addContentNode(ngContentIndex: number, nodeExpr: o.Expression) {
|
addContentNode(ngContentIndex: number, nodeExpr: o.Expression) {
|
||||||
|
|
|
@ -53,7 +53,11 @@ export class CompileQuery {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
afterChildren(targetMethod: CompileMethod) {
|
private _isStatic(): boolean {
|
||||||
|
return !this._values.values.some(value => value instanceof ViewQueryValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
afterChildren(targetStaticMethod, targetDynamicMethod: CompileMethod) {
|
||||||
var values = createQueryValues(this._values);
|
var values = createQueryValues(this._values);
|
||||||
var updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
var updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
||||||
if (isPresent(this.ownerDirectiveExpression)) {
|
if (isPresent(this.ownerDirectiveExpression)) {
|
||||||
|
@ -64,7 +68,15 @@ export class CompileQuery {
|
||||||
if (!this.meta.first) {
|
if (!this.meta.first) {
|
||||||
updateStmts.push(this.queryList.callMethod('notifyOnChanges', []).toStmt());
|
updateStmts.push(this.queryList.callMethod('notifyOnChanges', []).toStmt());
|
||||||
}
|
}
|
||||||
targetMethod.addStmt(new o.IfStmt(this.queryList.prop('dirty'), updateStmts));
|
if (this.meta.first && this._isStatic()) {
|
||||||
|
// for queries that don't change and the user asked for a single element,
|
||||||
|
// set it immediately. That is e.g. needed for querying for ViewContainerRefs, ...
|
||||||
|
// we don't do this for QueryLists for now as this would break the timing when
|
||||||
|
// we call QueryList listeners...
|
||||||
|
targetStaticMethod.addStmts(updateStmts);
|
||||||
|
} else {
|
||||||
|
targetDynamicMethod.addStmt(new o.IfStmt(this.queryList.prop('dirty'), updateStmts));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,7 +196,8 @@ export class CompileView implements NameResolver {
|
||||||
afterNodes() {
|
afterNodes() {
|
||||||
this.pipes.forEach((pipe) => pipe.create());
|
this.pipes.forEach((pipe) => pipe.create());
|
||||||
this.viewQueries.values().forEach(
|
this.viewQueries.values().forEach(
|
||||||
(queries) => queries.forEach((query) => query.afterChildren(this.updateViewQueriesMethod)));
|
(queries) => queries.forEach(
|
||||||
|
(query) => query.afterChildren(this.createMethod, this.updateViewQueriesMethod)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,28 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should set static view and content children already after the constructor call',
|
||||||
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
|
var template =
|
||||||
|
'<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>';
|
||||||
|
|
||||||
|
tcb.overrideTemplate(MyComp0, template)
|
||||||
|
.createAsync(MyComp0)
|
||||||
|
.then((view) => {
|
||||||
|
var q: NeedsStaticContentAndViewChild =
|
||||||
|
view.debugElement.children[0].references['q'];
|
||||||
|
expect(q.contentChild.text).toBeFalsy();
|
||||||
|
expect(q.viewChild.text).toBeFalsy();
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(q.contentChild.text).toEqual('contentFoo');
|
||||||
|
expect(q.viewChild.text).toEqual('viewFoo');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should contain the first view child accross embedded views',
|
it('should contain the first view child accross embedded views',
|
||||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||||
var template = '<needs-view-child #q></needs-view-child>';
|
var template = '<needs-view-child #q></needs-view-child>';
|
||||||
|
@ -882,6 +904,19 @@ class NeedsViewChild implements AfterViewInit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'needs-static-content-view-child',
|
||||||
|
template: `
|
||||||
|
<div text="viewFoo"></div>
|
||||||
|
`,
|
||||||
|
directives: [TextDirective]
|
||||||
|
})
|
||||||
|
class NeedsStaticContentAndViewChild {
|
||||||
|
@ContentChild(TextDirective) contentChild: TextDirective;
|
||||||
|
|
||||||
|
@ViewChild(TextDirective) viewChild: TextDirective;
|
||||||
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dir]'})
|
@Directive({selector: '[dir]'})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
class InertDirective {
|
class InertDirective {
|
||||||
|
@ -1119,6 +1154,7 @@ class HasNullQueryCondition {
|
||||||
NeedsContentChildren,
|
NeedsContentChildren,
|
||||||
NeedsViewChildren,
|
NeedsViewChildren,
|
||||||
NeedsViewChild,
|
NeedsViewChild,
|
||||||
|
NeedsStaticContentAndViewChild,
|
||||||
NeedsContentChild,
|
NeedsContentChild,
|
||||||
NeedsTpl,
|
NeedsTpl,
|
||||||
NeedsNamedTpl,
|
NeedsNamedTpl,
|
||||||
|
|
Loading…
Reference in New Issue