Directiva ngModel

  • Por
Explicaciones sobre la directiva ngModel de Angular, con ejemplos de uso diversos, con binding de una y dos direcciones. Importar FormsModule para su funcionamiento en Angular 4.

La directiva ngModel es un viejo conocido para las personas que vienen de las versiones antiguas del framework, cuando se llamaba AngularJS. Quizás para ellos no requiera tantas explicaciones de concepto, aunque sí será importante explicar cómo usarla, porque han cambiado bastantes cosas.

De todos modos, tanto para desarrolladores experimentados como para los más novatos, vamos a repasar en este artículo del Manual de Angular los aspectos básicos de ngModel y ver algunos ejemplos sencillos de funcionamiento en componentes.

Qué es ngModel

Pensando en las personas que son nuevas en Angular, tendremos que comenzar aclarando qué es ngModel. Básicamente se trata de un enlace, entre algo que tienes en la definición del componente con un campo de formulario del template (vista) del componente.

Nota: "model" en ngModel viene de lo que se conoce como el modelo en el MVC. El modelo trabaja con los datos, así que podemos entender que el enlace creado con ngModel permite que desde la vista pueda usar un dato. Sin embargo, con expresiones ya se podía usar un dato, volcando su valor en la vista como {{dato}}. La diferencia es que al usar ese dato en campos de formulario, debes aplicar el valor mediante la directiva ngModel.

Por ejemplo, tenemos un componente llamado "cuadrado", que declara una propiedad llamada "lado". La class del componente podría quedar así:

export class CuadradoComponent {
  lado = 4;
}

Si queremos que ese valor "lado" se vuelque dentro de un campo INPUT de formulario, tendríamos que usar algo como esto.

<input type="number" [ngModel]="lado">

Estamos usando la directiva ngModel como si fuera una propiedad del campo INPUT, asignándole el valor que tenemos declarado en el componente.

Dar soporte a la propiedad ngModel en el campo de formulario

Sin embargo, nuestro componente "cuadrado" no funcionaría todavía, porque Angular 4 en principio no reconoce ngModel como propiedad de un campo INPUT de formulario. Por ello, si ejecutas tu aplicación con el código tal como hemos hecho hasta ahora, obtendrás un mensaje de error como este: "Can't bind to 'ngModel' since it isn't a known property of 'input'."

La solución pasa por traernos esa propiedad, que está en el módulo "FormsModule".

Para ello tenemos que hacer la operativa tradicional de importar aquello que necesitamos. En este caso tendremos que importar FormsModule en el módulo donde vamos a crear aquel componente donde se quiera usar la directiva ngModule.

Por ejemplo, si nuestro componente "CuadradoComponent" está en el módulo "FigurasModule", este sería el código necesario para declarar el import de "FormsModule".

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CuadradoComponent } from './cuadrado/cuadrado.component';
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    CommonModule,
    FormsModule
  ],
  declarations: [CuadradoComponent],
  exports: [CuadradoComponent]
})
export class FigurasModule { }

Del código del módulo anterior, debes fijarte en dos cosas:

1.- El import de FormsModule, que viene de @angular/forms

import { FormsModule } from '@angular/forms';

2.- La declaración en el array de imports del módulo en cuestión:

imports: [
    [...]
    FormsModule
],

Con esta infraestructura ya somos capaces de usar ngModule en las vistas de los componentes Angular.

Enlace de una o de dos direcciones con ngModel

Tal como hemos dejado el código hasta el momento en la vista, el input estaba asociado al valor de una propiedad del componente, sin embargo, era un enlace de una única dirección.

<input type="number" [ngModel]="lado">

Con esa sintaxis en la vista, lo que haya en la propiedad "lado" se escribía como valor del input, pero no encontramos el doble binding. Y aunque esto es algo que ya se trató en el artículo de sintaxis de las vistas en Angular, queremos recordar que, para especificar el doble binding, se debe usar la sintaxis "banana in a box".

<input type="number" [(ngModel)]="lado">

Ahora, lo que se escriba en el campo INPUT también viajará hacia el modelo, actualizando el valor de la propiedad "lado" del componente.

Evento ngModelChange

Si lo deseas, también tienes disponible un evento llamado "ngModelChange" que se ejecuta cuando cambia el valor en la propiedad asociada a un ngModel, con el que podríamos conseguir un comportamiento idéntico al visto en el punto anterior del doble binding, pero sin usar el binding doble.

Tienes que trabajar con ngModelChange en conjunto con la directiva ngModel. Mediante ngModel asocias la propiedad que quieres asociar y entonces tendrás la posibilidad de asociar un manejador de evento a ngModelChange, cada vez que esa propiedad cambia.

Dentro del manejador de evento podemos además usar una variable llamada $event, en la que recibimos el nuevo valor escrito, que podríamos volcarla de nuevo a la propiedad por medio de una asignación, para conseguir el mismo efecto del binding en las dos direcciones. El código te quedaría como esto:

<input type="number" [ngModel]="lado" (ngModelChange)="lado = $event">
Nota: Esta sintaxis no aporta ninguna ventaja en términos de rendimiento, por lo que en principio no se suele usar mucho. Generalmente vamos a preferir usar la sintaxis del binding de dos direcciones [(ngModel)], por ser más concisa. De todos modos, podría ser interesante disponer de ngModelChange en el caso que, cuando cambiase el modelo, necesitases realizar otras acciones, adicionales a la simple asignación de un nuevo valor en la propiedad del componente.

Ejemplo realizado en este artículo

Ahora para la referencia, voy a dejar el código realizado para ilustrar el comportamiento y uso de la directiva ngModel. Estará compuesto por varios archivos.

Vista del componente:

Comenzamos con la vista del componente, archivo cuadrado.component.html, que es lo más importante en este caso, ya que es el lugar donde hemos usado ngModel.

He creado varias versiones de enlace al input, como puedes ver a continuación.

<p>
  Tamaño del lado {{lado}}
</p>
<p>
  1 way binding <input type="number" [ngModel]="lado">
</p>
<p>
  2 way binding: <input type="number" [(ngModel)]="lado">
</p>
<p>
    Evento ngModelChange: <input type="number" [ngModel]="lado" (ngModelChange)="cambiaLado($event)">
</p>

Otra cosa que apreciarás en la vista es que el evento ngModelChange no tiene escrito el código del manejador en la propia vista, por considerarlo un antipatrón. Es más interesante que el código se coloque dentro de un método del componente. En este caso tendrás que enviarle al método el valor $event, para que lo puedas usar allí.

Código TypeScript del componente:

El código del componente estará en el archivo cuadrado.component.ts y no tiene ninguna complicación en especial.

import { Component } from '@angular/core';

@Component({
  selector: 'dw-cuadrado',
  templateUrl: './cuadrado.component.html',
  styleUrls: ['./cuadrado.component.css']
})
export class CuadradoComponent {
  lado = 1;

  cambiaLado(valor) {
    this.lado = valor;
  }
}

Módulo donde se crea el componente:

Por último recuerda que es esencial que importes en el módulo donde estés creando este componente el propio módulo del core de Angular donde se encuentra la directiva ngModule. Esto lo hemos descrito en un bloque anterior, pero volvemos a colocar aquí el código fuente:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CuadradoComponent } from './cuadrado/cuadrado.component';
import { FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    CommonModule,
    FormsModule
  ],
  declarations: [CuadradoComponent],
  exports: [CuadradoComponent]
})
export class FigurasModule { }

Por supuesto, para poder usar este componente "CuadradoComponent" en otro módulo tendrás que hacer el correspondiente import, pero esto es algo que ya se trató en el artículo de los módulos en Angular.

Nota: Además de la aplicación de ngModel que hemos conocido en este artículo, relacionada directamente con el sistema de binding de Angular, ngModel también sirve para definir qué campos de formulario deben ser tratados al generarse los objetos ngForm. Estos objetos se crean automáticamente y Angular los pone a disposición para poder controlar el formulario de una manera muy precisa. Ese uso lo veremos más adelante cuando hablemos en detalle de los formularios de Angular.

Autor

Miguel Angel Alvarez

Miguel es fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Comenzó en el mundo del desarrollo web en el año 1997, transformando su hobby en su trabajo.

Compartir