Skip to content

Commit a055196

Browse files
IvanNavarroCodethePunderWoman
authored andcommitted
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
1 parent 5915c72 commit a055196

File tree

5 files changed

+44
-1
lines changed

5 files changed

+44
-1
lines changed

goldens/public-api/common/errors.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export const enum RuntimeErrorCode {
1919
// (undocumented)
2020
NG_FOR_MISSING_DIFFER = -2200,
2121
// (undocumented)
22+
NGSRCSET_WITHOUT_LOADER = 2963,
23+
// (undocumented)
2224
OVERSIZED_IMAGE = 2960,
2325
// (undocumented)
2426
PARENT_NG_SWITCH_NOT_FOUND = 2000,

packages/common/src/directives/ng_optimized_image/ng_optimized_image.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
385385
assertNoComplexSizes(this);
386386
}
387387
assertNotMissingBuiltInLoader(this.ngSrc, this.imageLoader);
388+
assertNoNgSrcsetWithoutLoader(this, this.imageLoader);
388389
if (this.priority) {
389390
const checker = this.injector.get(PreconnectLinkChecker);
390391
checker.assertPreconnect(this.getRewrittenSrc(), this.ngSrc);
@@ -953,3 +954,17 @@ function assertNotMissingBuiltInLoader(ngSrc: string, imageLoader: ImageLoader)
953954
}
954955
}
955956
}
957+
958+
/**
959+
* Warns if ngSrcset is present and no loader is configured (i.e. the default one is being used).
960+
*/
961+
function assertNoNgSrcsetWithoutLoader(dir: NgOptimizedImage, imageLoader: ImageLoader) {
962+
if (dir.ngSrcset && imageLoader === noopImageLoader) {
963+
console.warn(formatRuntimeError(
964+
RuntimeErrorCode.NGSRCSET_WITHOUT_LOADER,
965+
`${imgDirectiveDetails(dir.ngSrc)} the \`ngSrcset\` attribute is present but ` +
966+
`no image loader is configured (i.e. the default one is being used), ` +
967+
`which would result in the same image being used for all configured sizes. ` +
968+
`To fix this, provide a loader or remove the \`ngSrcset\` attribute from the image.`));
969+
}
970+
}

packages/common/src/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ export const enum RuntimeErrorCode {
3333
OVERSIZED_IMAGE = 2960,
3434
TOO_MANY_PRELOADED_IMAGES = 2961,
3535
MISSING_BUILTIN_LOADER = 2962,
36+
NGSRCSET_WITHOUT_LOADER = 2963,
3637
}

packages/common/test/directives/ng_optimized_image_spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,24 @@ describe('Image directive', () => {
11831183
expect(consoleWarnSpy.calls.count()).toBe(0);
11841184
});
11851185

1186+
it('should warn if there is no image loader but `ngSrcset` is present', () => {
1187+
setUpModuleNoLoader();
1188+
1189+
const template = `<img ngSrc="img.png" ngSrcset="100w, 200w" width="150" height="50">`;
1190+
const fixture = createTestComponent(template);
1191+
const consoleWarnSpy = spyOn(console, 'warn');
1192+
fixture.detectChanges();
1193+
1194+
expect(consoleWarnSpy.calls.count()).toBe(1);
1195+
expect(consoleWarnSpy.calls.argsFor(0)[0])
1196+
.toBe(
1197+
'NG02963: The NgOptimizedImage directive (activated on an <img> element ' +
1198+
'with the `ngSrc="img.png"`) has detected that the `ngSrcset` attribute is ' +
1199+
'present but no image loader is configured (i.e. the default one is being used), ' +
1200+
`which would result in the same image being used for all configured sizes. ` +
1201+
'To fix this, provide a loader or remove the `ngSrcset` attribute from the image.');
1202+
});
1203+
11861204
it('should set `src` using the image loader provided via the `IMAGE_LOADER` token to compose src URL',
11871205
() => {
11881206
const imageLoader = (config: ImageLoaderConfig) => `${IMG_BASE_URL}/${config.src}`;

packages/core/test/bundling/image-directive/e2e/oversized-image/oversized-image.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {NgOptimizedImage} from '@angular/common';
9+
import {IMAGE_LOADER, ImageLoaderConfig, NgOptimizedImage} from '@angular/common';
1010
import {Component} from '@angular/core';
1111

12+
const imageLoader = {
13+
provide: IMAGE_LOADER,
14+
useFactory: () => (config: ImageLoaderConfig) => config.src
15+
};
16+
1217
@Component({
1318
selector: 'oversized-image-passing',
1419
standalone: true,
1520
imports: [NgOptimizedImage],
21+
providers: [imageLoader],
1622
template: `
1723
<!-- Image is rendered within threshold range-->
1824
<div style="width: 500px; height: 500px">
@@ -33,6 +39,7 @@ export class OversizedImageComponentPassing {
3339
selector: 'oversized-image-failing',
3440
standalone: true,
3541
imports: [NgOptimizedImage],
42+
providers: [imageLoader],
3643
template: `
3744
<!-- Image is rendered too small -->
3845
<div style="width: 300px; height: 300px">

0 commit comments

Comments
 (0)