NgModel: I want to believe.

Man from IT Space
Roonyx BNPL platform
4 min readApr 13, 2021

How to speed up the development of your Angular app with ngModel.

Angular gives us access to a strange world first but then turns into a surprising and logical one. Let’s start with the good things right away.

Any work with the input field (yes, those site users are constantly trying to enter some data) forces us to write handlers in order to get the data. Sometimes we are required to submit the data, and to do so, we also have to create a handler.

It would be great not to subscribe to the same action every time in order to respond to it. The built-in ngModel directive allows us to avoid all this unnecessary writing.

What is this directive, and how to cope with it? What are the advantages and dangers of it? Let’s dive into it and understand it.

NgModel

A few boring facts.
NgModel is a directive that allows two-way data binding. The easiest way to use it is to simply define a variable for it, with which it will work and where we want to see the results:

<input [(ngModel)]="name">
<div>Input value {{name}}</div>

Ok, let’s slow down a bit! What is this bunch of parentheses, and where did this variable come from?

It’s simple. We define a variable inside our component that will be available inside the template.

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

@Component({
selector: 'example',
template: `
<input [value]="name" (input)="name = $event.target.value">
<div>Input value {{name}}</div>
`,
})
export class SimpleBindingComponent {
name = '';
}

Now that it’s clear let’s talk about the parentheses around the parameters. They are not there for beauty, as they might at first seem.

This is our two-way binding mechanism. [] around any attribute or directive allows you to pass a value to that attribute. () allows you to subscribe to events generated inside our directive or any other events, such as a click.

...

<input [value]="value" [readonly]="isReadonly" (input)="valueChange()" (click)="clickMe()">

...

Parentheses, bindings, components, so far, things are only getting more and more complicated and confusing. Now let’s try without all this theory!

import { Component } from '@angular/core';
import { YourApiService } from './your-api.service';

@Component({
selector: 'example',
template: `
<input [(ngModel)]="name">
<div>Input value {{name}}</div>

<button type="submit" (submit)="submit()"></button>
`,
})
export class NgModelComponent {
name = 'Susan';

constructor(private yourApiService: YourApiService) {}

submit() {
this.yourApiService.submit(this.name);
}
}

Just a couple of lines and our data is in place. Everything is ready; you just need to send it to the server. But why go through all this trouble if we can make our own binding. Now let’s take and assign a value to our variable.

@Component({
selector: 'custom-binding',
template: `
<button (click)="setRed()">Set red</button>
<button (click)="setGreen()">Set green</button>
<div>Input value {{color}}</div>
`
})
export class CustomBindingComponent {
color = '';
@Output()
currentColorChange = new EventEmitter();
@Input()
get currentColor() {
return this.color;
}
set currentColor(value) {
this.color = value;
this.currentColorChange.emit(this.color);
}
setRed() {
this.currentColor = 'Red';
}
setGreen() {
this.currentColor = 'Green';
}
}
...<custom-binding [(currentColor)]="someValue"></custom-binding>
<div>{{someValue}}</div>
...

NgModel is not just about related data. Working with the form tag, we can also get additional information, such as the validity of the entered data.

import { Component } from '@angular/core';
import { YourApiService } from './your-api.service';

@Component({
selector: 'example',
template: `
<input [(ngModel)]="name" #ctrl="ngModel" required>
<div>Input value {{name}}</div>
<div>Input valid {{ctrl.valid}}</div>

<button type="submit" [disabled]="ctrl.invalid" (submit)="submit()"></button>
`,
})
export class NgModelComponent {
name = 'Susan';

constructor(private yourApiService: YourApiService) {}

submit() {
this.yourApiService.submit(this.name);
}
}

Therefore, we see that working with ngModel can get you a flexible and convenient mechanism for managing the entire form (in Angular, there is a separate mechanism for managing forms, but we will look at it another time). Now let’s do what it was all about. Create a form.

import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
import { YourApiService } from './your-api.service';

@Component({
selector: 'example',
template: `
<form #f="ngForm" (ngSubmit)="onSubmit(f)" novalidate>
<input name="first" ngModel required #first="ngModel">
<input name="last" ngModel>
<button>Submit</button>
</form>

<p>First name value: {{ first.value }}</p>
<p>First name valid: {{ first.valid }}</p>
<p>Form value: {{ f.value | json }}</p>
<p>Form valid: {{ f.valid }}</p>
`,
})
export class SimpleFormComponent {
onSubmit(f: NgForm) {
if (f.valid) {
this.yourApiService.submit(f.value);
}
}
}

Conclusion

Working with the Angular data model is easier and more convenient than it seems at first glance. NgModel allows us to easily link a template and component data using just a couple of code lines. This powerful and simple mechanism can significantly simplify and speed up the development of your application. And yes, it would be nice not to forget to add it to the imports section of the module. Although modern IDEs will most likely offer to do this automatically.

Do you have any questions? Feel free to ask here!

--

--

Man from IT Space
Roonyx BNPL platform

An inspired Angular & RoR virtuoso who tells about technologies and achievements of IT progress that are useful for developers and business.