Angular Model Inputs: two-way binding inputs with Signals
Tags: angular, frontend, webdev, javascript
Published: 2024-04-09 07:00:00

Angular journey towards the full integration of Signals has moved forward with the latest release of **Angular v17.2.0**, introducing a new API to allow two-way binding with Signal Inputs: **Model Inputs**.

Before proceeding, I kindly suggest you to read my previous article “**[Angular Signal Inputs: road to Signal Components](https://dev.to/davidepassafaro/angular-signal-inputs-road-to-signal-components-32cl)**”, in which you will found a more exhaust explanation about the advantages of **Signal Inputs**:

{% embed https://dev.to/davidepassafaro/angular-signal-inputs-road-to-signal-components-32cl %}

This article is strictly connected to the previous, and is meant to fulfill the open point about the **two-way binding** with **Signal Inputs**.

Now that **Model Inputs** are available it’s time to deepen them.

---

## How to create a Model Input

Just like Signal Inputs, **Model Inputs** are meant to replace inputs created with the **@Input** decorator, exposing the input value as a Signal.

To create a **Model Input** you just need to use the **`model()`** function provided by **@angular/core** package:

```ts
import { Component, model, ModelSignal } from '@angular/core';

@Component({ ... })
export class MyComponent {
  myProp: ModelSignal<string | undefined> = model<string>();
}
```

Your input will be typed as **ModelSignal**, a special type of Signal that Angular internally updates whenever a new value is bound.

### The update( ) function

Two-way binding allows both parent and child components to update the value of a bonded input and to share then the same identical value.

To enable the update from the child component, **ModelSignal** API exposes a dedicated **`update()`** function to update an input based on the current value:

```ts
updateProp(): void {
  this.myProp.update((current) => current + ' - Updated!!!');
}
```

### The two-way binding

Using Angular’s **two-way binding** syntax when binding a value from the parent component to the child component, the **ModelSignal** update from the child component will therefore go back up to the parent component:

```html
<my-component
  [(myProp)]="parentProp"
  (myPropChange)="onPropChangeMethod($event)"
></my-component>
```

Both parent and child component will have the property value synced.
The parent component will also get an output notification named as the input name with “**Change**” appended, carrying the new value as event object.

---

## Model Inputs with default and required values

As you probably noticed in the previous examples, by creating a **Model Input** you can provide to the **`model()`** function a type argument to define the type of the **ModelSignal** value:

```ts
import { Component, model, ModelSignal } from '@angular/core';

@Component({ ... })
export class MyComponent {
  myProp: ModelSignal<string | undefined> = model<string>();
}
```

Due to the **optional** nature of the input value, **ModelSignal** values are typed as possibly **`undefined`**.

To get rid of those, you can provide a **default value** to the **`model()`** function:

```ts
myProp: ModelSignal<string> = model('default');
```

Alternatively, you can define your **Model Inputs** as required thought a dedicated **`model.required()`** function:

```ts
myRequiredProp: ModelSignal<string> = model.required<string>();
```

---

## Input options: alias and transform function

As classic Angular inputs, **Model Inputs** support also the **`alias`** option:

```ts
myProp = model('default-value', { alias: 'myPropAlias' });
```

Allowing us to decouple the child component input variable name and the DOM property name used by parent components to bound the input value:

```html
<my-component [(myPropAlias)]="parentProp"></my-component>
```

### Where is the transform( ) function?

Regarding the **`transform()`** function, the last classic inputs option we are familiar with, the **`model()`** function does not support it.

This make sense to me, just think about the conflicts between the two-way nature of **Model Inputs**, and the role of the **`transform()`** function to transform the input value only inside a component.

Luckily, you can overcome this absence using the **`computed()`** function, which allows you to define derived values starting from a Signal:

```ts
import { Component, computed, model, ModelSignal, Signal } from '@angular/core';

@Component({ ... })
export class MyComponent {
  myProp: ModelSignal<string> = model('default');

  myComposedProp: Signal<number> = computed(
      () => this.modelOptional()?.length || 0
    );
}
```

In this way the value defined using the **`computed()`** function is confined inside child component.

---

## Thanks for reading so far 🙏

I’d like to have your feedback so please leave a **comment**, **like** or **follow**. 👏

And if you really liked it please **[follow me on LinkedIn](https://www.linkedin.com/in/davide-passafaro/)**. 👋