From cefa3de3de09c4d89d660bb7797285b02c43163e Mon Sep 17 00:00:00 2001 From: Andrew Kushnir Date: Mon, 5 Jun 2023 18:55:41 -0700 Subject: [PATCH] refactor(common): use `transform` functions in NgOptimizedImage inputs (#50580) This commit refactors the code of NgOptimizedImage directive to switch from getter/setter approach to convers inputs to use the `transform` function instead. PR Close #50580 --- goldens/public-api/common/index.md | 32 ++++---- .../ng_optimized_image/ng_optimized_image.ts | 78 ++++--------------- .../directives/ng_optimized_image_spec.ts | 8 +- 3 files changed, 36 insertions(+), 82 deletions(-) diff --git a/goldens/public-api/common/index.md b/goldens/public-api/common/index.md index 9292ea53702..94166ece2ed 100644 --- a/goldens/public-api/common/index.md +++ b/goldens/public-api/common/index.md @@ -599,20 +599,24 @@ export abstract class NgLocalization { // @public export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { - set disableOptimizedSrcset(value: string | boolean | undefined); - // (undocumented) - get disableOptimizedSrcset(): boolean; - set fill(value: string | boolean | undefined); - // (undocumented) - get fill(): boolean; - set height(value: string | number | undefined); - // (undocumented) - get height(): number | undefined; + disableOptimizedSrcset: boolean; + fill: boolean; + height: number | undefined; loaderParams?: { [key: string]: any; }; loading?: 'lazy' | 'eager' | 'auto'; // (undocumented) + static ngAcceptInputType_disableOptimizedSrcset: unknown; + // (undocumented) + static ngAcceptInputType_fill: unknown; + // (undocumented) + static ngAcceptInputType_height: unknown; + // (undocumented) + static ngAcceptInputType_priority: unknown; + // (undocumented) + static ngAcceptInputType_width: unknown; + // (undocumented) ngOnChanges(changes: SimpleChanges): void; // (undocumented) ngOnDestroy(): void; @@ -620,15 +624,11 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { ngOnInit(): void; ngSrc: string; ngSrcset: string; - set priority(value: string | boolean | undefined); - // (undocumented) - get priority(): boolean; + priority: boolean; sizes?: string; - set width(value: string | number | undefined); + width: number | undefined; // (undocumented) - get width(): number | undefined; - // (undocumented) - static ɵdir: i0.ɵɵDirectiveDeclaration; + static ɵdir: i0.ɵɵDirectiveDeclaration; // (undocumented) static ɵfac: i0.ɵɵFactoryDeclaration; } diff --git a/packages/common/src/directives/ng_optimized_image/ng_optimized_image.ts b/packages/common/src/directives/ng_optimized_image/ng_optimized_image.ts index 03c226fad0b..ffaa82d348e 100644 --- a/packages/common/src/directives/ng_optimized_image/ng_optimized_image.ts +++ b/packages/common/src/directives/ng_optimized_image/ng_optimized_image.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, inject, InjectionToken, Injector, Input, NgZone, OnChanges, OnDestroy, OnInit, PLATFORM_ID, Renderer2, SimpleChanges, ɵformatRuntimeError as formatRuntimeError, ɵRuntimeError as RuntimeError} from '@angular/core'; +import {booleanAttribute, Directive, ElementRef, inject, InjectionToken, Injector, Input, NgZone, numberAttribute, OnChanges, OnDestroy, OnInit, PLATFORM_ID, Renderer2, SimpleChanges, ɵformatRuntimeError as formatRuntimeError, ɵRuntimeError as RuntimeError} from '@angular/core'; import {RuntimeErrorCode} from '../../errors'; import {isPlatformServer} from '../../platform_id'; @@ -247,7 +247,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { * Image name will be processed by the image loader and the final URL will be applied as the `src` * property of the image. */ - @Input() ngSrc!: string; + @Input({required: true}) ngSrc!: string; /** * A comma separated list of width or density descriptors. @@ -272,30 +272,14 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { * For responsive images: the intrinsic width of the image in pixels. * For fixed size images: the desired rendered width of the image in pixels. */ - @Input() - set width(value: string|number|undefined) { - ngDevMode && assertGreaterThanZero(this, value, 'width'); - this._width = inputToInteger(value); - } - get width(): number|undefined { - return this._width; - } - private _width?: number; + @Input({transform: numberAttribute}) width: number|undefined; /** * For responsive images: the intrinsic height of the image in pixels. * For fixed size images: the desired rendered height of the image in pixels.* The intrinsic * height of the image in pixels. */ - @Input() - set height(value: string|number|undefined) { - ngDevMode && assertGreaterThanZero(this, value, 'height'); - this._height = inputToInteger(value); - } - get height(): number|undefined { - return this._height; - } - private _height?: number; + @Input({transform: numberAttribute}) height: number|undefined; /** * The desired loading behavior (lazy, eager, or auto). @@ -308,14 +292,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { /** * Indicates whether this image should have a high priority. */ - @Input() - set priority(value: string|boolean|undefined) { - this._priority = inputToBoolean(value); - } - get priority(): boolean { - return this._priority; - } - private _priority = false; + @Input({transform: booleanAttribute}) priority = false; /** * Data to pass through to custom loaders. @@ -325,14 +302,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { /** * Disables automatic srcset generation for this image. */ - @Input() - set disableOptimizedSrcset(value: string|boolean|undefined) { - this._disableOptimizedSrcset = inputToBoolean(value); - } - get disableOptimizedSrcset(): boolean { - return this._disableOptimizedSrcset; - } - private _disableOptimizedSrcset = false; + @Input({transform: booleanAttribute}) disableOptimizedSrcset = false; /** * Sets the image to "fill mode", which eliminates the height/width requirement and adds @@ -340,14 +310,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { * * @developerPreview */ - @Input() - set fill(value: string|boolean|undefined) { - this._fill = inputToBoolean(value); - } - get fill(): boolean { - return this._fill; - } - private _fill = false; + @Input({transform: booleanAttribute}) fill = false; /** * Value of the `src` attribute if set on the host `` element. @@ -381,6 +344,12 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { assertNonZeroRenderedHeight(this, this.imgElement, this.renderer); } else { assertNonEmptyWidthAndHeight(this); + if (this.height !== undefined) { + assertGreaterThanZero(this, this.height, 'height'); + } + if (this.width !== undefined) { + assertGreaterThanZero(this, this.width, 'width'); + } // Only check for distorted images when not in fill mode, where // images may be intentionally stretched, cropped or letterboxed. assertNoImageDistortion(this, this.imgElement, this.renderer); @@ -548,7 +517,7 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { } private shouldGenerateAutomaticSrcset(): boolean { - return !this._disableOptimizedSrcset && !this.srcset && this.imageLoader !== noopImageLoader && + return !this.disableOptimizedSrcset && !this.srcset && this.imageLoader !== noopImageLoader && !(this.width! > FIXED_SRCSET_WIDTH_LIMIT || this.height! > FIXED_SRCSET_HEIGHT_LIMIT); } @@ -568,20 +537,6 @@ export class NgOptimizedImage implements OnInit, OnChanges, OnDestroy { /***** Helpers *****/ -/** - * Convert input value to integer. - */ -function inputToInteger(value: string|number|undefined): number|undefined { - return typeof value === 'string' ? parseInt(value, 10) : value; -} - -/** - * Convert input value to boolean. - */ -function inputToBoolean(value: unknown): boolean { - return value != null && `${value}` !== 'false'; -} - /** * Sorts provided config breakpoints and uses defaults. */ @@ -778,9 +733,8 @@ function assertGreaterThanZero(dir: NgOptimizedImage, inputValue: unknown, input if (!validNumber && !validString) { throw new RuntimeError( RuntimeErrorCode.INVALID_INPUT, - `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` has an invalid value ` + - `(\`${inputValue}\`). To fix this, provide \`${inputName}\` ` + - `as a number greater than 0.`); + `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` has an invalid value. ` + + `To fix this, provide \`${inputName}\` as a number greater than 0.`); } } diff --git a/packages/common/test/directives/ng_optimized_image_spec.ts b/packages/common/test/directives/ng_optimized_image_spec.ts index 7865f706b61..8d19d0d41a1 100644 --- a/packages/common/test/directives/ng_optimized_image_spec.ts +++ b/packages/common/test/directives/ng_optimized_image_spec.ts @@ -377,7 +377,7 @@ describe('Image directive', () => { .toThrowError( 'NG02952: The NgOptimizedImage directive (activated on an ' + 'element with the `ngSrc="img.png"`) has detected that `width` ' + - 'has an invalid value (`0`). To fix this, provide `width` as ' + + 'has an invalid value. To fix this, provide `width` as ' + 'a number greater than 0.'); }); @@ -392,7 +392,7 @@ describe('Image directive', () => { .toThrowError( 'NG02952: The NgOptimizedImage directive (activated on an ' + 'element with the `ngSrc="img.png"`) has detected that `width` ' + - 'has an invalid value (`10px`). To fix this, provide `width` ' + + 'has an invalid value. To fix this, provide `width` ' + 'as a number greater than 0.'); }); @@ -424,7 +424,7 @@ describe('Image directive', () => { .toThrowError( 'NG02952: The NgOptimizedImage directive (activated on an ' + 'element with the `ngSrc="img.png"`) has detected that `height` ' + - 'has an invalid value (`0`). To fix this, provide `height` as a number ' + + 'has an invalid value. To fix this, provide `height` as a number ' + 'greater than 0.'); }); @@ -439,7 +439,7 @@ describe('Image directive', () => { .toThrowError( 'NG02952: The NgOptimizedImage directive (activated on an element ' + 'with the `ngSrc="img.png"`) has detected that `height` has an invalid ' + - 'value (`10%`). To fix this, provide `height` as a number greater than 0.'); + 'value. To fix this, provide `height` as a number greater than 0.'); }); it('should throw if `ngSrc` value is not provided', () => {