Skip to content

Commit 219445c

Browse files
crisbetoalxhub
authored andcommitted
fix(common): image placeholder not removed in OnPush component (#54515)
Fixes that the placeholder wasn't being removed when an optimized image is placed in an `OnPush` component. Fixes #54478. PR Close #54515
1 parent 69daa37 commit 219445c

File tree

2 files changed

+31
-20
lines changed

2 files changed

+31
-20
lines changed

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
ɵRuntimeError as RuntimeError,
3030
ɵSafeValue as SafeValue,
3131
ɵunwrapSafeValue as unwrapSafeValue,
32+
ChangeDetectorRef,
3233
} from '@angular/core';
3334

3435
import {RuntimeErrorCode} from '../../errors';
@@ -433,7 +434,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
433434
}
434435
}
435436
if (this.placeholder) {
436-
this.removePlaceholderOnLoad(this, this.imgElement, this.renderer);
437+
this.removePlaceholderOnLoad(this.imgElement);
437438
}
438439
this.setHostAttributes();
439440
}
@@ -648,22 +649,17 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy {
648649
return Boolean(placeholderConfig.blur);
649650
}
650651

651-
private removePlaceholderOnLoad(
652-
dir: NgOptimizedImage,
653-
img: HTMLImageElement,
654-
renderer: Renderer2,
655-
): void {
656-
const removeLoadListenerFn = renderer.listen(img, 'load', () => {
652+
private removePlaceholderOnLoad(img: HTMLImageElement): void {
653+
const callback = () => {
654+
const changeDetectorRef = this.injector.get(ChangeDetectorRef);
657655
removeLoadListenerFn();
658656
removeErrorListenerFn();
659-
dir.placeholder = false;
660-
});
657+
this.placeholder = false;
658+
changeDetectorRef.markForCheck();
659+
};
661660

662-
const removeErrorListenerFn = renderer.listen(img, 'error', () => {
663-
removeLoadListenerFn();
664-
removeErrorListenerFn();
665-
dir.placeholder = false;
666-
});
661+
const removeLoadListenerFn = this.renderer.listen(img, 'load', callback);
662+
const removeErrorListenerFn = this.renderer.listen(img, 'error', callback);
667663
}
668664

669665
/** @nodoc */

packages/common/test/directives/ng_optimized_image_spec.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {CommonModule, DOCUMENT, IMAGE_CONFIG, ImageConfig} from '@angular/common';
1010
import {RuntimeErrorCode} from '@angular/common/src/errors';
1111
import {PLATFORM_SERVER_ID} from '@angular/common/src/platform_id';
12-
import {Component, PLATFORM_ID, Provider, Type} from '@angular/core';
12+
import {ChangeDetectionStrategy, Component, PLATFORM_ID, Provider, Type} from '@angular/core';
1313
import {ComponentFixture, TestBed} from '@angular/core/testing';
1414
import {DomSanitizer, SafeResourceUrl} from '@angular/platform-browser';
1515
import {expect} from '@angular/platform-browser/testing/src/matchers';
@@ -1078,7 +1078,6 @@ describe('Image directive', () => {
10781078
fixture.detectChanges();
10791079
const nativeElement = fixture.nativeElement as HTMLElement;
10801080
const img = nativeElement.querySelector('img')!;
1081-
const styles = parseInlineStyles(img);
10821081
// Double quotes removed to account for different browser behavior.
10831082
expect(img.getAttribute('style')?.replace(/"/g, '').replace(/\s/g, '')).toBe(
10841083
`background-size:cover;background-position:50%50%;background-repeat:no-repeat;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEU);filter:blur(${PLACEHOLDER_BLUR_AMOUNT}px);`,
@@ -1100,6 +1099,19 @@ describe('Image directive', () => {
11001099
);
11011100
});
11021101

1102+
it('should replace the placeholder with the actual image on load', () => {
1103+
setupTestingModule();
1104+
const template = '<img ngSrc="path/img.png" width="400" height="300" placeholder="true" />';
1105+
const fixture = createTestComponent(template, ChangeDetectionStrategy.OnPush);
1106+
fixture.detectChanges();
1107+
const nativeElement = fixture.nativeElement as HTMLElement;
1108+
const img = nativeElement.querySelector('img')!;
1109+
expect(parseInlineStyles(img).has('background-image')).toBe(true);
1110+
img.dispatchEvent(new Event('load'));
1111+
fixture.detectChanges();
1112+
expect(parseInlineStyles(img).has('background-image')).toBe(false);
1113+
});
1114+
11031115
it('should use the placeholderResolution set in imageConfig', () => {
11041116
const imageConfig = {
11051117
placeholderResolution: 30,
@@ -2177,10 +2189,13 @@ function setUpModuleNoLoader() {
21772189
});
21782190
}
21792191

2180-
function createTestComponent(template: string): ComponentFixture<TestComponent> {
2181-
return TestBed.overrideComponent(TestComponent, {set: {template: template}}).createComponent(
2182-
TestComponent,
2183-
);
2192+
function createTestComponent(
2193+
template: string,
2194+
changeDetection = ChangeDetectionStrategy.OnPush,
2195+
): ComponentFixture<TestComponent> {
2196+
return TestBed.overrideComponent(TestComponent, {
2197+
set: {template, changeDetection},
2198+
}).createComponent(TestComponent);
21842199
}
21852200

21862201
function parseInlineStyles(img: HTMLImageElement): Map<string, string> {

0 commit comments

Comments
 (0)