Enlace angular de dos vías, un poco más de comprensión

Del traductor
De un traductor : hace dos años comencé mi primer proyecto en Angular (2+), con una gran y exitosa experiencia en AngularJS. La transición requirió un formato notable de pensamiento, ya que demasiado en A1 y A2 + se hace "un poco diferente". El dolor de la transición redujo notablemente el blog de pensamiento para . Hace un año, recibí permiso para traducir este artículo "sobre primaria y fácilmente comprensible para todos". Pero son esas manos (sus artículos son incontables). Sorprendentemente, el artículo se traduce bien en Google translate. Pero algunos de los matices de esta traducción se perdieron, sin mencionar el estilo del autor. El estilo del autor no se ha conservado completamente en mi versión. Pero, espero, logré transmitir el estado de ánimo y los pensamientos del artículo.

Entiendo que Angular no es el tema más popular en Habré, pero espero que la traducción ayude a alguien, tal como el artículo original una vez me ayudó.

Eso es lo que causó el efecto wow en el viejo AngularJS, por lo que es un "enlace bidireccional". Esta magia se enamoró instantáneamente de AngularJS y rompió todas las ideas sobre la programación de páginas aburridas y (¡oh, horror!) Formularios web. Los cambios en los datos se muestran instantáneamente en la pantalla y viceversa. Aquellos que previamente desarrollaron aplicaciones jQuery percibieron que los enlaces caían en un cuento de hadas. Y los monstruos barbudos, aserrando clientes gordos antes de jQuery, comenzaron a contar frenéticamente los estúpidamente perdidos meses-hombre.

Y, además, la magia de la unión bidireccional estaba disponible no solo para anotaciones especiales y componentes seleccionados. Podríamos usarlo fácilmente en nuestras propias directivas y componentes (simplemente configurando el parámetro de configuración).

En Angular2 +, los creadores abandonaron el enlace de datos bidireccional incorporado (excepto a través de ngModel). Pero esto no significa que no podamos usar el enlace bidireccional en nuestras propias directivas ... Es solo que el obsequio ha terminado y ahora tenemos que hacer algo por nuestra cuenta. Y, preferiblemente, con una comprensión de cómo funciona en Angular.

Tabla de contenidos



Encuadernación bidireccional en pocas palabras


En A2 +, solo una directiva implementa el enlace de datos bidireccional: ngModel . Y a primera vista, esta es la misma magia que en AngularJS (solo en una notación diferente). ¿Pero qué hay debajo del capó?

Sorprendentemente, bajo el capó, todo es relativamente simple y lógico: la unión bidireccional se reduce a la unión de propiedades y la unión de eventos. ¿Dos enlaces unilaterales, en lugar de uno bilateral? Ok, vamos dos.

E inmediatamente un ejemplo:

<input [(ngModel)]="username"> <p>Hello {{username}}!</p> 

Sí, sí, esta es una hermosa y sorprendente demostración de Angular2 de 2009. No es broma, hermosa. Al cambiar el campo, el valor del nombre de usuario cae en el modelo y se refleja inmediatamente en el mensaje de bienvenida en el formulario.

¿Pero cómo funciona? Recuerde que el enlace bidireccional en Angular2 es un enlace de propiedad y un enlace de evento. Y sí, pueden estar disponibles simultáneamente en una directiva. Además, incluso sin ngModel , podríamos implementar fácilmente el enlace de datos bidireccional. Por ejemplo, así:

 <input [value]="username" (input)="username = $event.target.value"> <p>Hello {{username}}!</p> 

La salida {{username}} es clara, pero ¿qué se escribe allí en la entrada ? Vamos a entender:

  • [valor] = "nombre de usuario" - notación entre corchetes, asocia la expresión de nombre de usuario con la propiedad de valor
  • (input) = "expresión" - una notación con paréntesis, la expresión se adjunta al evento de entrada (sí, existe tal evento). En nuestro caso:
    • username = $ event.target.value : esta expresión se ejecutará en respuesta al evento de entrada
    • $ event es una variable sintética en eventos angulares que lleva una carga útil: en este caso, contiene información sobre lo que sucedió y sus alrededores

¿Se está aclarando? Lo arreglamos

Vinculamos la propiedad de nombre de usuario del modelo angular a la propiedad de valor del elemento de entrada del navegador (enlace unidireccional del modelo a la vista).

También vinculamos una expresión al evento de entrada de nuestro elemento. Que asigna el valor de $ event.target.value a la propiedad de nombre de usuario del modelo.

¿Qué es $ event.target.value ? Como ya se mencionó, $ event está lleno de información útil sobre el evento. En este caso, es un InputEventObject en el que la propiedad de destino se refiere al elemento DOM que activó el evento (es decir, nuestro elemento de entrada).

Entonces, todo lo que esencialmente hacemos es leer el contenido ( valor ) del elemento de entrada ( $ event.target ) cuando el usuario ingresa un valor. Y cuando asignamos este valor de nombre de usuario, los datos de la vista se enviarán al modelo.

Eso es todo Esto es "enlace bidireccional en pocas palabras" . Belleza?

¿Pero cuándo entra en juego ngModel ? El escenario de trabajar con elementos de entrada es muy común y muy demandado. Y por alguna razón, quiero tener una directiva que oculte la implementación y guarde las pulsaciones de teclas adicionales.

Entendiendo ngModel


Si observa el origen, puede asegurarse de que ngModel también tenga un enlace a la propiedad y al evento. Así es como se ve nuestro ejemplo ngModel, pero sin usar la sintaxis abreviada:

 <input [ngModel]="username" (ngModelChange)="username = $event"> <p>Hello {{username}}!</p> 

Casi todo es igual. El enlace de la propiedad [ngModel] se encarga de actualizar el valor del elemento de entrada. Un enlace de evento (ngModelChange) notifica al mundo que se están produciendo cambios en el DOM.

Y notó que la expresión del controlador usa solo $ event , no $ event.target.value . ¿Hay algo mal aquí? En absoluto Como se indicó anteriormente, $ event es una variable sintética que lleva una carga útil . Angular toma la decisión de lo que se considera útil. En otras palabras, ngModelChange se encarga de extraer target.value del evento $ interno y simplemente nos da lo que queremos, sin empaque ni pandereta. Para ser técnicamente precisos, estos son los de DefaultValueAccessor : es él quien extrae los datos y los transfiere al objeto DOM base, aunque ... simplemente no puede pensar en eso).

Por último, pero no menos importante, ya que escribir el nombre de usuario y ngModel dos veces sigue siendo redundante, Angular permite el uso de la sintaxis abreviada [()] , también llamada "banana en una caja". Que es similar al ejemplo anterior, y nos devuelve al ejemplo desde el principio de la sección, pero con una comprensión de la implementación de ngModel . Proporcionando el mismo enlace bidireccional.

 <input [(ngModel)]="username"> <p>Hello {{username}}!</p> 


Crea tus propios enlaces de datos bidireccionales


Ahora sabemos lo suficiente como para crear nuestros propios enlaces de datos bidireccionales. Todo lo que necesita hacer es simplemente seguir las mismas reglas que ngModel , a saber:

  • Ingrese un enlace de propiedad (por ejemplo: [foo] )
  • Enlace a un evento con el mismo nombre y sufijo Change (por ejemplo: (fooChange) )
  • Asegúrese de que el enlace del evento se encarga de recuperar la propiedad (si es necesario)

¿Te das cuenta de que crear un enlace de datos bidireccional requiere mucho más trabajo que AngularJS? Esto podría ser muy frustrante para nosotros ... si intentáramos utilizar nuestro propio enlace bidireccional siempre que sea posible. En la vida real, siempre debe considerar si necesitamos un enlace bidireccional y, si es necesario, es más fácil aprovechar ngModel. Esto último, por ejemplo, tiene lugar cuando se crean controles de formulario personalizados .

Pero supongamos que creamos un componente de contador personalizado (y no queremos usar un control de formulario personalizado).

 @Component({ selector: 'custom-counter', template: ` <button (click)="decrement()">-</button> <span>{{counter}}</span> <button (click)="increment()">+</button> ` }) export class CustomCounterComponent { counterValue = 0; get counter() { return this.counterValue; } set counter(value) { this.counterValue = value; } decrement() { this.counter--; } increment() { this.counter++; } } 

Tenemos la propiedad del componente contador para mostrar el valor actual del contador. Para proporcionarle un enlace bidireccional, lo primero que debe hacer es convertirlo en un parámetro de entrada . Para esto, el decorador @Input () es muy útil:

 @Component() export class CustomCounterComponent { counterValue = 0; @Input() get counter() { return this.counterValue; } ... } 

Esto ya le permite vincular la propiedad del componente al consumidor de la siguiente manera:

 <custom-counter [counter]="someValue"></custom-counter> 

Ahora tenemos que configurar el evento @Output () con el mismo nombre ( contador ) y el sufijo Change (resulta counterChange). Queremos plantear este evento cada vez que cambie el contador . ¿Por qué agregar la propiedad @Output () ? Y terminamos, en un par de captadores, el contador del contador, en el que interceptaremos el cambio en el valor y descartaremos el evento con el valor del contador actual:

 @Component() export class CustomCounterComponent { ... @Output() counterChange = new EventEmitter(); set counter(val) { this.counterValue = val; this.counterChange.emit(this.counterValue); } ... } 

Esto es todo! Ahora podemos enlazar la expresión a esta propiedad usando la sintaxis de enlace de datos bidireccional:

 <custom-counter [(counter)]="someValue"></custom-counter> <p>counterValue = {{someValue}}</p> 

¡Mira la demo y pruébalo!

Nuevamente, tenga en cuenta que un componente como un contador personalizado se implementa mejor con un control de formulario personalizado y aproveche ngModel para implementar el enlace de datos bidireccional, como se describe en este artículo .

Conclusión


Angular ya no viene con enlace de datos bidireccional incorporado. En cambio, hay API en el cuadro que le permiten implementar el enlace completo como propiedades y eventos de enlace.

ngModel viene como una directiva de enlace bidireccional incorporada en FormsModule (recuerde agregarlo a la sección de importaciones de la declaración @NgModule : aprox. por). Se debe preferir vincular a través de ngModel al crear componentes que sirvan como controles de formulario personalizados. De lo contrario, todo depende de tu imaginación.

PD del traductor: la implementación vinculante en A2 + se ha vuelto más moderna. Ahora, los "set" gratuitos se utilizan para monitorear los cambios por "feng shui" (aunque está claro que los mecanismos para la verificación sucia permanecen, al menos para los componentes de usuario de alto nivel). Esto permitió abandonar a 100,500 observadores (procedimientos que monitorean los cambios en "sus" datos). Lo que en A1 le encantaba crear una carga maliciosa en el navegador y requería manos directas inusualmente al planificar páginas interactivas ricas.

Con componentes diseñados adecuadamente, A2 listo para usar se ha vuelto significativamente más receptivo. Dejar a expensas del trabajo de los programadores. Ahora puede colocar una legión de componentes en la página y no preocuparse por los recursos del procesador.

La otra cara de la moneda fue el costo inicial del "proceso de entrada" en A2 +, que afectó la popularidad del marco. Pero A1 también tuvo un alto costo de entrada, solo que fue relegado a la liga mayor. Debido a la falta de comprensión de cómo organizar grandes aplicaciones, muchos prototipos "despegaron" en A1, luego se "desmoronaron" y correspondieron a React y Vue.

Espero que con este artículo ayude a reducir ligeramente el umbral para la entrada inicial a A2 +, que sigue siendo muy demandada (lo que sé de primera mano).

Source: https://habr.com/ru/post/453696/


All Articles