mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(common): improve the NgOptimizedImage error message related to changing inputs (#47926)
This commit updates the error message thrown by the NgOptimizedImage directive, when it detects a situation when inputs change after initial rendering. The list of inputs was also updated to include all inputs added recently. PR Close #47926
This commit is contained in:
@@ -439,8 +439,17 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
|
||||
/** @nodoc */
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (ngDevMode) {
|
||||
assertNoPostInitInputChange(
|
||||
this, changes, ['ngSrc', 'ngSrcset', 'width', 'height', 'priority']);
|
||||
assertNoPostInitInputChange(this, changes, [
|
||||
'ngSrc',
|
||||
'ngSrcset',
|
||||
'width',
|
||||
'height',
|
||||
'priority',
|
||||
'fill',
|
||||
'loading',
|
||||
'sizes',
|
||||
'disableOptimizedSrcset',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,12 +696,20 @@ function assertUnderDensityCap(dir: NgOptimizedImage, value: string) {
|
||||
* the directive has initialized.
|
||||
*/
|
||||
function postInitInputChangeError(dir: NgOptimizedImage, inputName: string): {} {
|
||||
let reason!: string;
|
||||
if (inputName === 'width' || inputName === 'height') {
|
||||
reason = `Changing \`${inputName}\` may result in different attribute value ` +
|
||||
`applied to the underlying image element and cause layout shifts on a page.`;
|
||||
} else {
|
||||
reason = `Changing the \`${inputName}\` would have no effect on the underlying ` +
|
||||
`image element, because the resource loading has already occurred.`;
|
||||
}
|
||||
return new RuntimeError(
|
||||
RuntimeErrorCode.UNEXPECTED_INPUT_CHANGE,
|
||||
`${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` was updated after initialization. ` +
|
||||
`The NgOptimizedImage directive will not react to this input change. ` +
|
||||
`To fix this, switch \`${inputName}\` a static value or wrap the image element ` +
|
||||
`in an *ngIf that is gated on the necessary value.`);
|
||||
`The NgOptimizedImage directive will not react to this input change. ${reason} ` +
|
||||
`To fix this, either switch \`${inputName}\` to a static value ` +
|
||||
`or wrap the image element in an *ngIf that is gated on the necessary value.`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -641,34 +641,58 @@ describe('Image directive', () => {
|
||||
});
|
||||
|
||||
const inputs = [
|
||||
['ngSrc', 'new-img.png'], //
|
||||
['width', 10], //
|
||||
['height', 20], //
|
||||
['priority', true]
|
||||
['ngSrc', 'new-img.png'],
|
||||
['width', 10],
|
||||
['height', 20],
|
||||
['priority', true],
|
||||
['fill', true],
|
||||
['loading', true],
|
||||
['sizes', '90vw'],
|
||||
['disableOptimizedSrcset', true],
|
||||
];
|
||||
inputs.forEach(([inputName, value]) => {
|
||||
it(`should throw if an input changed after directive initialized the input`, () => {
|
||||
setupTestingModule();
|
||||
it(`should throw if the \`${inputName}\` input changed after directive initialized the input`,
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'test-cmp',
|
||||
template: `<img
|
||||
[ngSrc]="ngSrc"
|
||||
[width]="width"
|
||||
[height]="height"
|
||||
[priority]="priority"
|
||||
[fill]="fill"
|
||||
[loading]="loading"
|
||||
[sizes]="sizes"
|
||||
[disableOptimizedSrcset]="disableOptimizedSrcset"
|
||||
>`
|
||||
})
|
||||
class TestComponent {
|
||||
width = 100;
|
||||
height = 50;
|
||||
ngSrc = 'img.png';
|
||||
priority = false;
|
||||
fill = false;
|
||||
loading = false;
|
||||
sizes = '100vw';
|
||||
disableOptimizedSrcset = false;
|
||||
}
|
||||
|
||||
const template =
|
||||
'<img [ngSrc]="ngSrc" [width]="width" [height]="height" [priority]="priority">';
|
||||
// Initial render
|
||||
const fixture = createTestComponent(template);
|
||||
fixture.detectChanges();
|
||||
setupTestingModule({component: TestComponent});
|
||||
|
||||
expect(() => {
|
||||
// Update input (expect to throw)
|
||||
(fixture.componentInstance as unknown as {[key: string]: unknown})[inputName as string] =
|
||||
value;
|
||||
fixture.detectChanges();
|
||||
})
|
||||
.toThrowError(
|
||||
'NG02953: The NgOptimizedImage directive (activated on an <img> element ' +
|
||||
`with the \`ngSrc="img.png"\`) has detected that \`${inputName}\` was updated ` +
|
||||
'after initialization. The NgOptimizedImage directive will not react ' +
|
||||
`to this input change. To fix this, switch \`${inputName}\` a static value or ` +
|
||||
'wrap the image element in an *ngIf that is gated on the necessary value.');
|
||||
});
|
||||
// Initial render
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
const expectedErrorMessage = //
|
||||
`NG02953: The NgOptimizedImage directive (.*)? ` +
|
||||
`has detected that \`${inputName}\` was updated after initialization`;
|
||||
expect(() => {
|
||||
// Update input (expect to throw)
|
||||
(fixture.componentInstance as unknown as
|
||||
{[key: string]: unknown})[inputName as string] = value;
|
||||
fixture.detectChanges();
|
||||
}).toThrowError(new RegExp(expectedErrorMessage));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user