Saltar al contenido

Angular: * ngIf frente a llamadas de funciones simples en la plantilla

Luego de de una prolongada compilación de datos solucionamos esta pregunta que pueden tener ciertos de nuestros usuarios. Te brindamos la respuesta y esperamos servirte de gran apoyo.

Solución:

También traté de evitar llamadas a funciones en plantillas tanto como sea posible, pero su pregunta me inspiró a hacer una investigación rápida:

Agregué otro caso con almacenamiento en caché. userCheck() resultados

*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 alguna async acciones, 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 desconfía de cualquier impacto en el rendimiento que pueda tener una función de este tipo, le recomiendo encarecidamente que ponga su ChangeDetectionStrategy para OnPush, que se considera la mejor práctica de todos modos. Con esto, la función no será llamada cada ciclo, solo cuando un Input cambia, ocurre 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 puedes usar el async tubería, 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:


 ...


Otra opción sería utilizar ngOnChangessi 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  changes.isAuthorized) 
      this.userIsAuthorized = this.userCheck();
    
  

  userCheck(): boolean  false;
  

Que puedes usar en tu plantilla así:


 ...

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.

Al final de todo puedes encontrar las anotaciones de otros administradores, tú también eres capaz insertar el tuyo si te apetece.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *