type sirve script resumen que para lista etiquetas estructura ejemplos con codigos basicas javascript angular angular-components

javascript - sirve - Refactorización de componentes angulares de muchas entradas/salidas a un único objeto de configuración



resumen javascript pdf (5)

Mis componentes suelen comenzar teniendo varias propiedades @Input y @Output . A medida que agrego propiedades, parece más limpio cambiar a un único objeto de configuración como entrada.

Por ejemplo, aquí hay un componente con múltiples entradas y salidas:

export class UsingEventEmitter implements OnInit { @Input() prop1: number; @Output() prop1Change = new EventEmitter<number>(); @Input() prop2: number; @Output() prop2Change = new EventEmitter<number>(); ngOnInit() { // Simulate something that changes prop1 setTimeout(() => this.prop1Change.emit(this.prop1 + 1)); } }

Y su uso:

export class AppComponent { prop1 = 1; onProp1Changed = () => { // prop1 has already been reassigned by using the [(prop1)]=''prop1'' syntax } prop2 = 2; onProp2Changed = () => { // prop2 has already been reassigned by using the [(prop2)]=''prop2'' syntax } }

Modelo:

<using-event-emitter [(prop1)]=''prop1'' (prop1Change)=''onProp1Changed()'' [(prop2)]=''prop2'' (prop2Change)=''onProp2Changed()''> </using-event-emitter>

A medida que aumenta el número de propiedades, parece que cambiar a un único objeto de configuración podría ser más limpio. Por ejemplo, aquí hay un componente que toma un solo objeto de configuración:

export class UsingConfig implements OnInit { @Input() config; ngOnInit() { // Simulate something that changes prop1 setTimeout(() => this.config.onProp1Changed(this.config.prop1 + 1)); } }

Y su uso:

export class AppComponent { config = { prop1: 1, onProp1Changed(val: number) { this.prop1 = val; }, prop2: 2, onProp2Changed(val: number) { this.prop2 = val; } }; }

Modelo:

<using-config [config]=''config''></using-config>

Ahora solo puedo pasar la referencia del objeto de configuración a través de varias capas de componentes anidados. El componente que utiliza la configuración invocaría devoluciones de llamada como config.onProp1Changed(...) , lo que hace que el objeto de configuración realice la reasignación del nuevo valor. Así que parece que todavía tenemos un flujo de datos unidireccional. Además, agregar y eliminar propiedades no requiere cambios en las capas intermedias.

¿Hay algún inconveniente en tener un único objeto de configuración como entrada para un componente, en lugar de tener múltiples entradas y salidas? ¿Evitar @Output y EventEmitter este modo causará algún problema que pueda alcanzarme más tarde?


¿Hay algún inconveniente en tener un único objeto de configuración como entrada para un componente, en lugar de tener múltiples entradas y salidas?

Sí, cuando desea cambiar a la estrategia de detección de cambios en el impulso , que a menudo se necesita en proyectos más grandes para mitigar los problemas de rendimiento causados ​​por demasiados ciclos de procesamiento, angular no detectará los cambios que ocurrieron dentro de su objeto de configuración.

¿Evitar @Output y EventEmitter de este modo causará algún problema que pueda alcanzarme más tarde?

Sí, si comienza a alejarse de @Output y en su plantilla opera directamente en el propio objeto de configuración, entonces está causando efectos secundarios en su vista, que serán la raíz de los errores difíciles de encontrar en el futuro. Su vista nunca debe modificar los datos que se inyectan. Debe permanecer "puro" en ese sentido y solo informar al componente de control a través de eventos (u otras devoluciones de llamada) que algo sucedió.

Actualización: después de ver nuevamente el ejemplo en su publicación, parece que no quiso decir que quiere operar directamente en el modelo de entrada, sino que pasa a los emisores de eventos directamente a través del objeto de configuración. La @input devoluciones de llamadas a través de @input (que es lo que está haciendo implícitamente) también tiene sus inconvenientes , como:


Si desea agrupar los parámetros de entrada como un objeto, le sugiero que lo haga así:

export class UsingConfig implements OnInit { @Input() config: any; @Output() configChange = new EventEmitter<any>(); ngOnInit() { // Simulate something that changes prop1 setTimeout(() => this.configChange.emit({ ...this.config, prop1: this.config.prop1 + 1 }); ); } }

  • Está creando un nuevo objeto de configuración al cambiar una propiedad.
  • Está utilizando un evento de salida para emitir el objeto de configuración modificado.

Ambos puntos garantizan que ChangeDetection funcionará correctamente (suponiendo que utilice la estrategia OnPush más eficiente). Además, es más fácil seguir la lógica en caso de depuración.

Edición : Aquí está la parte obvia dentro del componente padre.

Modelo:

<using-config [config]="config" (configChange)="onConfigChange($event)"></using-config>

Código:

export class AppComponent { config = {prop1: 1}; onConfigChange(newConfig: any){ // if for some reason you need to handle specific changes // you could check for those here, e.g.: // if (this.config.prop1 !== newConfig.prop1){... this.config = newConfig; } }


Yo diría que podría estar bien usar objetos de configuración simples para las Input s, pero debería atenerse a las Output s en todo momento. Input define lo que su componente requiere desde el exterior y algunos de ellos pueden ser opcionales. Sin embargo, los Output son totalmente un negocio de componentes y deben definirse dentro. Si confía en que los usuarios pasen esas funciones, debe verificar si hay funciones undefined o simplemente continuar y llamar a las funciones como si SIEMPRE se hayan pasado dentro de la configuración, lo que puede ser incómodo usar su componente si hay demasiados eventos Definir incluso si el usuario no los necesita. Por lo tanto, siempre tenga su Output s definida dentro de su componente y emita lo que necesite para emitir. Si los usuarios no unen una función a esos eventos, está bien.

Además, creo que tener una config única para la Input s no es la mejor práctica. Oculta las entradas reales y es posible que los usuarios tengan que buscar en su código o en los documentos para averiguar qué deben pasar. Sin embargo, si sus Input se definen por separado, los usuarios pueden obtener alguna inteligencia con herramientas como el Servicio de idiomas.

Además, creo que también puede romper la estrategia de detección de cambios.

Echemos un vistazo al siguiente ejemplo.

@Component({ selector: ''my-comp'', template: ` <div *ngIf="config.a"> {{config.b + config.c}} </div> ` }) export class MyComponent { @Input() config; }

Vamos a usarlo

@Component({ selector: ''your-comp'', template: ` <my-comp [config]="config"></my-comp> ` }) export class YourComponent { config = { a: 1, b: 2, c: 3 }; }

Y para entradas separadas.

@Component({ selector: ''my-comp'', template: ` <div *ngIf="a"> {{b + c}} </div> ` }) export class MyComponent { @Input() a; @Input() b; @Input() c; }

Y vamos a usar este

@Component({ selector: ''your-comp'', template: ` <my-comp [a]="1" [b]="2" [c]="3"> </my-comp> ` }) export class YourComponent {}

Como dije anteriormente, debe mirar el código de YourComponent para ver en qué valores se le está pasando. Además, debe escribir config todas partes para usar esas Input . Por otro lado, puede ver claramente qué valores se están transmitiendo en el segundo ejemplo mejor. Incluso puede obtener algo de inteligencia si utiliza el Servicio de idiomas.

Otra cosa es, el segundo ejemplo sería mejor escalar. Si necesita agregar más Input , debe editar la config todo el tiempo, lo que puede romper su componente. Sin embargo, en el segundo ejemplo, es fácil agregar otra Input y no necesitará tocar el código de trabajo.

Por último, pero no menos importante, no puede proporcionar enlaces de doble sentido con su camino. Probablemente sepa que si tiene en Input llamada data y en Output llamada dataChange , los consumidores de su componente pueden usar sintaxis de enlace de azúcar bidireccional y tipo simple

<your-comp [(data)]="value">

Esto actualizará el value en el componente principal cuando emita un evento usando

this.dataChange.emit(someValue)

Espero que esto aclare mis opiniones sobre la Input individual

Editar

Creo que hay un caso válido para una sola Input que también tiene alguna function definida dentro. Si está desarrollando algo como un componente de gráfico que a menudo requiere opciones / configuraciones complejas, es mejor tener una Input única. Es porque esa entrada se establece una vez y nunca cambia y es mejor tener opciones de su gráfico en un solo lugar. Además, el usuario puede pasar algunas funciones para ayudarlo a dibujar leyendas, información sobre herramientas, etiquetas del eje x, etiquetas del eje y, etc. Al igual que tener una entrada como la siguiente sería mejor para este caso

export interface ChartConfig { width: number; height: number; legend: { position: string, label: (x, y) => string }; tooltip: (x, y) => string; } ... @Input() config: ChartConfig;


personalmente, si veo que necesito más de 4 entradas + salidas, verificaré mi enfoque para crear mi componente nuevamente, tal vez debería ser más de un componente y estoy haciendo algo mal. De todos modos, incluso si necesito esa cantidad de entradas y salidas no lo haré en una configuración, por las siguientes razones:

1- Es más difícil saber qué debería haber dentro de las entradas y salidas, como esto: (considere un componente con elementos y etiquetas de entradas html)

imagínese si solo tiene 3 componentes de este componente y debería regresar para trabajar en este proyecto después de 1 o 2 meses, ¡o alguien más colaborará con usted o usará su código !. Es muy difícil entender tu código.

2- Falta de rendimiento. Es más barato para un ángulo observar una sola variable en lugar de mirar una matriz u objeto. Además de considerar el ejemplo que te di al principio, ¿por qué deberías forzar el seguimiento de las etiquetas en las que nunca cambien junto con los valores que siempre están cambiando?

3- Más difíciles de rastrear variables y depurar. angular en sí mismo viene con errores confusos que es difícil de depurar, ¿por qué debería hacerlo más difícil? Rastrear y corregir cualquier entrada o salida incorrecta una por una es más fácil para mí que hacerlo en una variable de configuración que contiene muchos datos.

Personalmente, prefiero romper mis componentes lo más pequeño posible y probar cada uno. luego haga componentes más grandes con los pequeños en lugar de tener solo un componente grande.

Actualización: uso este método para ingresar una vez y no modificar datos (como etiqueta)

@Component({ selector: ''icon-component'', templateUrl: ''./icon.component.html'', styleUrls: [''./icon.component.scss''], inputs: [''name'', ''color''] }); export class IconComponent implements OnInit { name: any; color: any; ngOnInit() { } }

Html:

<icon-component name="fa fa-trash " color="white"></icon-component>

con este método angular no rastreará ningún cambio dentro o fuera de su componente. pero con el método @input si su variable cambia en el componente principal, también obtendrá un cambio dentro del componente.


  • El punto de tener la Input además de su funcionalidad obvia, es hacer que su componente sea declarativo y fácil de entender.

  • Poner todas las configuraciones en un objeto masivo, que crecerá definitivamente (confía en mí) es una mala idea, por todas las razones anteriores y también para las pruebas.

  • Es mucho más fácil probar el comportamiento de un componente con una propiedad de input simple, en lugar de suministrar un objeto confuso gigante.

  • Va hacia atrás y piensa de la manera en que solían trabajar los complementos de jQuery, donde llamaría a una función llamada init y luego proporcionaría un montón de configuraciones que ni siquiera recordará si debería proporcionarlas o no, y luego sigue copiando este objeto desconocido y en constante crecimiento a través de sus componentes donde probablemente ni siquiera los necesiten

  • Crear valores predeterminados es extremadamente fácil y claro con las Input simples, mientras que se vuelve un poco desordenado con los objetos a los valores predeterminados creados.

Si tiene demasiadas Input y Output similares, puede considerar a continuación:

1- Puede crear una clase Base y colocar todas las Input/Output que sean similares y luego extender todos sus componentes.

export class Base{ @Input() prop1: number; @Output() prop1Change = new EventEmitter<number>(); @Input() prop2: number; @Output() prop2Change = new EventEmitter<number>(); } @Component({}) export class MyComponent extends from Base{ constructor(){super()} }

2- Si no te gusta esto, puedes usar la composición y crear una mezcla reutilizable y aplicar todas tus Input/Output esa manera.

A continuación se muestra un ejemplo de una función que se puede usar para aplicar mixins. Es posible que NOTA no sea necesariamente lo que desea, y que necesita ajustarlo a sus necesidades.

export function applyMixins(derivedCtor: any, baseCtors: any[]) { baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { derivedCtor.prototype[name] = baseCtor.prototype[name]; }); }); }

Y luego crea tus mixins:

export class MyMixin{ @Input() prop1: number; @Output() prop1Change = new EventEmitter<number>(); @Input() prop2: number; @Output() prop2Change = new EventEmitter<number>(); } applyMixins(MyComponent, [MyMixin]);

3- Puede tener propiedades predeterminadas para las entradas, por lo que solo las anulará si necesita:

export class MyComponent{ @Input() prop1: number = 10; // default }