fix(core): don’t throw if queries change during change detection.
Throwing on query changes would be a breaking change compared to v2. Also discovers a bug with querying manually projected content, see #15117. Related to #14748 Closes #14925
This commit is contained in:
parent
959a03a61f
commit
06fc42bc44
|
@ -318,12 +318,10 @@ function createViewNodes(view: ViewData) {
|
||||||
export function checkNoChangesView(view: ViewData) {
|
export function checkNoChangesView(view: ViewData) {
|
||||||
Services.updateDirectives(view, CheckType.CheckNoChanges);
|
Services.updateDirectives(view, CheckType.CheckNoChanges);
|
||||||
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
|
||||||
execQueriesAction(
|
|
||||||
view, NodeFlags.TypeContentQuery, NodeFlags.DynamicQuery, CheckType.CheckNoChanges);
|
|
||||||
Services.updateRenderer(view, CheckType.CheckNoChanges);
|
Services.updateRenderer(view, CheckType.CheckNoChanges);
|
||||||
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
execComponentViewsAction(view, ViewAction.CheckNoChanges);
|
||||||
execQueriesAction(
|
// Note: We don't check queries for changes as we didn't do this in v2.x.
|
||||||
view, NodeFlags.TypeViewQuery, NodeFlags.DynamicQuery, CheckType.CheckNoChanges);
|
// TODO(tbosch): investigate if we can enable the check again in v5.x with a nicer error message.
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkAndUpdateView(view: ViewData) {
|
export function checkAndUpdateView(view: ViewData) {
|
||||||
|
|
|
@ -536,6 +536,29 @@ export function main() {
|
||||||
expect(q.query.length).toBe(0);
|
expect(q.query.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not throw if a content template is queried and created in the view during change detection',
|
||||||
|
() => {
|
||||||
|
@Component(
|
||||||
|
{selector: 'auto-projecting', template: '<div *ngIf="true; then: content"></div>'})
|
||||||
|
class AutoProjecting {
|
||||||
|
@ContentChild(TemplateRef)
|
||||||
|
content: TemplateRef<any>;
|
||||||
|
|
||||||
|
@ContentChildren(TextDirective)
|
||||||
|
query: QueryList<TextDirective>;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [AutoProjecting]});
|
||||||
|
const template =
|
||||||
|
'<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>';
|
||||||
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
|
|
||||||
|
const q = view.debugElement.children[0].references['q'];
|
||||||
|
// This should be 1, but due to
|
||||||
|
// https://github.com/angular/angular/issues/15117 this is 0.
|
||||||
|
expect(q.query.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,37 +369,6 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('general binding behavior', () => {
|
describe('general binding behavior', () => {
|
||||||
it('should checkNoChanges', () => {
|
|
||||||
const {view} = createAndGetRootNodes(compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 3, 'div'),
|
|
||||||
...contentQueryProviders(),
|
|
||||||
anchorDef(NodeFlags.EmbeddedViews, null, null, 0, null, embeddedViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
|
||||||
aServiceProvider(),
|
|
||||||
])),
|
|
||||||
]));
|
|
||||||
|
|
||||||
Services.checkAndUpdateView(view);
|
|
||||||
Services.checkNoChangesView(view);
|
|
||||||
|
|
||||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
|
||||||
attachEmbeddedView(view, asElementData(view, 3), 0, childView);
|
|
||||||
|
|
||||||
let err: any;
|
|
||||||
try {
|
|
||||||
Services.checkNoChangesView(view);
|
|
||||||
} catch (e) {
|
|
||||||
err = e;
|
|
||||||
}
|
|
||||||
expect(err).toBeTruthy();
|
|
||||||
expect(err.message)
|
|
||||||
.toBe(
|
|
||||||
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'Query 1 not dirty'. Current value: 'Query 1 dirty'.`);
|
|
||||||
const debugCtx = getDebugContext(err);
|
|
||||||
expect(debugCtx.view).toBe(view);
|
|
||||||
expect(debugCtx.nodeIndex).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report debug info on binding errors', () => {
|
it('should report debug info on binding errors', () => {
|
||||||
class QueryService {
|
class QueryService {
|
||||||
set a(value: any) { throw new Error('Test'); }
|
set a(value: any) { throw new Error('Test'); }
|
||||||
|
|
Loading…
Reference in New Issue