fix(common): warn if using ngSrcset without a configured image loader (#48804)

Warn the user in the console in case the `ngSrcset` is present and no
loader is configured. In this case, the default loader is used and
it ignores this attribute.

PR Close #48804
This commit is contained in:
Iván Navarro
2023-01-23 00:06:45 +01:00
committed by Jessica Janiuk
parent 5915c7236e
commit a055196c55
5 changed files with 44 additions and 1 deletions
+2
View File
@@ -19,6 +19,8 @@ export const enum RuntimeErrorCode {
// (undocumented)
NG_FOR_MISSING_DIFFER = -2200,
// (undocumented)
NGSRCSET_WITHOUT_LOADER = 2963,
// (undocumented)
OVERSIZED_IMAGE = 2960,
// (undocumented)
PARENT_NG_SWITCH_NOT_FOUND = 2000,
@@ -385,6 +385,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
assertNoComplexSizes(this);
}
assertNotMissingBuiltInLoader(this.ngSrc, this.imageLoader);
assertNoNgSrcsetWithoutLoader(this, this.imageLoader);
if (this.priority) {
const checker = this.injector.get(PreconnectLinkChecker);
checker.assertPreconnect(this.getRewrittenSrc(), this.ngSrc);
@@ -953,3 +954,17 @@ function assertNotMissingBuiltInLoader(ngSrc: string, imageLoader: ImageLoader)
}
}
}
/**
* Warns if ngSrcset is present and no loader is configured (i.e. the default one is being used).
*/
function assertNoNgSrcsetWithoutLoader(dir: NgOptimizedImage, imageLoader: ImageLoader) {
if (dir.ngSrcset && imageLoader === noopImageLoader) {
console.warn(formatRuntimeError(
RuntimeErrorCode.NGSRCSET_WITHOUT_LOADER,
`${imgDirectiveDetails(dir.ngSrc)} the \`ngSrcset\` attribute is present but ` +
`no image loader is configured (i.e. the default one is being used), ` +
`which would result in the same image being used for all configured sizes. ` +
`To fix this, provide a loader or remove the \`ngSrcset\` attribute from the image.`));
}
}
+1
View File
@@ -33,4 +33,5 @@ export const enum RuntimeErrorCode {
OVERSIZED_IMAGE = 2960,
TOO_MANY_PRELOADED_IMAGES = 2961,
MISSING_BUILTIN_LOADER = 2962,
NGSRCSET_WITHOUT_LOADER = 2963,
}
@@ -1183,6 +1183,24 @@ describe('Image directive', () => {
expect(consoleWarnSpy.calls.count()).toBe(0);
});
it('should warn if there is no image loader but `ngSrcset` is present', () => {
setUpModuleNoLoader();
const template = `<img ngSrc="img.png" ngSrcset="100w, 200w" width="150" height="50">`;
const fixture = createTestComponent(template);
const consoleWarnSpy = spyOn(console, 'warn');
fixture.detectChanges();
expect(consoleWarnSpy.calls.count()).toBe(1);
expect(consoleWarnSpy.calls.argsFor(0)[0])
.toBe(
'NG02963: The NgOptimizedImage directive (activated on an <img> element ' +
'with the `ngSrc="img.png"`) has detected that the `ngSrcset` attribute is ' +
'present but no image loader is configured (i.e. the default one is being used), ' +
`which would result in the same image being used for all configured sizes. ` +
'To fix this, provide a loader or remove the `ngSrcset` attribute from the image.');
});
it('should set `src` using the image loader provided via the `IMAGE_LOADER` token to compose src URL',
() => {
const imageLoader = (config: ImageLoaderConfig) => `${IMG_BASE_URL}/${config.src}`;
@@ -6,13 +6,19 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NgOptimizedImage} from '@angular/common';
import {IMAGE_LOADER, ImageLoaderConfig, NgOptimizedImage} from '@angular/common';
import {Component} from '@angular/core';
const imageLoader = {
provide: IMAGE_LOADER,
useFactory: () => (config: ImageLoaderConfig) => config.src
};
@Component({
selector: 'oversized-image-passing',
standalone: true,
imports: [NgOptimizedImage],
providers: [imageLoader],
template: `
<!-- Image is rendered within threshold range-->
<div style="width: 500px; height: 500px">
@@ -33,6 +39,7 @@ export class OversizedImageComponentPassing {
selector: 'oversized-image-failing',
standalone: true,
imports: [NgOptimizedImage],
providers: [imageLoader],
template: `
<!-- Image is rendered too small -->
<div style="width: 300px; height: 300px">