Lo siento si esto ya se ha respondido aquí, pero no pude encontrar ninguna coincidencia para nuestro escenario específico, ¡así que aquí va!
Tuvimos una discusión en nuestro equipo de desarrollo con respecto a las llamadas a funciones en plantillas angulares. Ahora, como regla general, estamos de acuerdo en que no debe hacer esto. Sin embargo, hemos tratado de discutir cuándo podría estar bien. Déjame darte un escenario.
Digamos que tenemos un bloque de plantilla que está envuelto en un ngIf, que verifica múltiples parámetros, como aquí:
<ng-template *ngIf="user && user.name && isAuthorized"> ... </ng-template>¿Habría una diferencia significativa en el rendimiento en comparación con algo como esto:
Modelo:
<ng-template *ngIf="userCheck()"> ... </ng-template>Mecanografiado:
userCheck(): boolean { return this.user && this.user.name && this.isAuthorized; }Entonces, para resumir la pregunta, ¿la última opción tendría algún costo de rendimiento significativo?
Preferiríamos usar el segundo enfoque, en situaciones en las que necesitamos verificar más de 2 condiciones, pero muchos artículos en línea dicen que las llamadas a funciones SIEMPRE son malas en las plantillas, pero ¿es realmente un problema en este caso?
También traté de evitar las llamadas a funciones en las plantillas en la medida de lo posible, pero su pregunta me inspiró a hacer una investigación rápida:
Agregué otro caso con el almacenamiento en caché de los resultados de userCheck()
*ngIf="isUserChecked" ... // .ts isUserChecked = this.userCheck()Preparé una demostración aquí: https://stackblitz.com/edit/angular-9qgsm9
Sorprendentemente parece que no hay diferencia entre
*ngIf="user && user.name && isAuthorized"Y
*ngIf="userCheck()" ... // .ts userCheck(): boolean { return this.user && this.user.name && this.isAuthorized; }Y
*ngIf="isUserChecked" ... // .ts isUserChecked = this.userCheck() Parece que esto es válido para una verificación de propiedad simple, pero definitivamente habrá una diferencia si se trata de acciones async , captadores que están esperando alguna API, por ejemplo.
Esta es una respuesta bastante obstinada.
El uso de funciones como esta es perfectamente aceptable. Hará que las plantillas sean mucho más claras y no causará ninguna sobrecarga significativa. Como dijo JB antes, también establecerá una base mucho mejor para las pruebas unitarias.
También creo que cualquier expresión que tenga en su plantilla será evaluada como una función por el mecanismo de detección de cambios, por lo que no importa si la tiene en su plantilla o en la lógica de su componente.
Simplemente mantenga la lógica dentro de la función al mínimo. Sin embargo, si tiene dudas sobre cualquier impacto en el rendimiento que pueda tener una función de este tipo, le recomiendo encarecidamente que ponga su ChangeDetectionStrategy en OnPush , que se considera una buena práctica de todos modos. Con esto, la función no se llamará cada ciclo, solo cuando cambie una Input , ocurra algún evento dentro de la plantilla, etc.
(usando, etc., porque ya no sé la otra razón) .
Personalmente, nuevamente, creo que es incluso mejor usar el patrón Observables, luego puede usar la tubería async , y solo cuando se emite un nuevo valor, la plantilla se vuelve a evaluar:
userIsAuthorized$ = combineLatest([ this.user$, this.isAuthorized$ ]).pipe( map(([ user, authorized ]) => !!user && !!user.name && authorized), shareReplay({ refCount: true, bufferSize: 1 }) );A continuación, puede simplemente utilizar en la plantilla de esta manera:
<ng-template *ngIf="userIsAuthorized$ | async"> ... </ng-template> Otra opción más sería usar ngOnChanges , si todas las variables dependientes del componente son Entradas, y tiene mucha lógica para calcular una determinada variable de plantilla (que no es el caso que mostró):
export class UserComponent implements ngOnChanges { userIsAuthorized: boolean = false; @Input() user?: any; @Input() isAuthorized?: boolean; ngOnChanges(changes: SimpleChanges): void { if (changes.user || changes.isAuthorized) { this.userIsAuthorized = this.userCheck(); } } userCheck(): boolean { return this.user && this.user.name && this.isAuthorized || false; } }Que puedes usar en tu plantilla así:
<ng-template *ngIf="userIsAuthorized"> ... </ng-template>No es recomendable por muchas razones el principal:
Para determinar si userCheck() debe volver a procesarse, Angular debe ejecutar la expresión userCheck() para verificar si su valor de retorno ha cambiado.
Debido a que Angular no puede predecir si el valor de retorno de userCheck() ha cambiado, debe ejecutar la función cada vez que se ejecuta la detección de cambios.
Entonces, si la detección de cambios se ejecuta 300 veces, la función se llama 300 veces, incluso si su valor de retorno nunca cambia.
Explicación extendida y más problemas https://medium.com/showpad-engineering/why-you-should-never-use-function-calls-in-angular-template-expressions-e1a50f9c0496
El problema surge cuando si su componente es grande y asiste a muchos eventos de cambio, si su componente es pequeño y solo asiste a algunos eventos, no debería ser un problema.
Ejemplo con observables
user$; isAuth$ userCheck$; userCheck$ = user$.pipe( switchMap((user) => { return forkJoin([of(user), isAuth$]); } ) .map(([user, isAuthenticated])=>{ if(user && user.name && isAuthenticated){ return true; } else { return false; } }) );Luego puede usarlo como observable con tubería asíncrona en su código.