Different ways to pass data to Angular components

Hello, Habr! I present to you the translation of the article “Different Ways to Pass Inputs to a Component in Angular” by Netanel Basal.

image

In this article, we will analyze three different ways of transferring data to a component. In the following example, we will use select as the main component, but the methods used in it are relevant in other components.

Create a select component that receives the following input - size and placement .

Using Inputs


The first method we are all familiar with is using the Input decorator .

@Component({
  selector: 'app-select',
  template: `
    <p><b>Size</b> {{ size }}</p>
    <p><b>Placement:</b> {{ placement }}</p>    
    `
})
export class SelectComponent {
  @Input() size: 'sm' | 'md' | 'lg' = 'md';
  @Input() placement: 'top' | 'bottom' | 'right' | 'left'  = 'bottom'
}

And this code works great, except that it is not so flexible. For example, we need to set the size variable to large for any select in our application. Therefore, we must allow the client to rewrite any input globally.

Using Injection Dependency


To do this, we can use the Angular dependency injection function.

import { InjectionToken, Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class SelectConfig {
  size: 'sm' | 'md' | 'lg' = 'md'
  placement: 'top' | 'bottom' | 'right' | 'left' = 'bottom';
}

export function setSelectConfig(config: Partial<SelectConfig>) {
  return {
    ...new SelectConfig(),
    ...config
  }
}

First we need to create a provider configuration. This provider can be used as token , type , and also set default values ​​for each input . We use this in our select component :

@Component({
  selector: 'app-select',
  template: `
    <p><b>Size</b> {{ size }}</p>
    <p><b>Placement:</b> {{ placement }}</p>    
    `
})
export class SelectComponent {
  @Input() size: SelectConfig['size'];
  @Input() placement: SelectConfig['placement'];

  constructor(private config: SelectConfig) {
    this.size = config.size;
    this.placement = config.placement;
  }
}

Now we can rewrite the selected inputs at the level of our application:

@NgModule({
  providers: [
    {
      provide: SelectConfig,
      useValue: setSelectConfig({
        size: 'lg'
      })
    }
  ]
  bootstrap: [AppComponent]
})
export class AppModule { }

But that's not all. We can also use this to pass various inputs at the component level. For example, we have a component that we use several times in the application. In each case, we need to use the small value :

@Component({
  selector: 'app-todos-page',
  template: `<app-select></app-select>`,
  providers: [
    {
      provide: SelectConfig,
      useValue: setSelectConfig({
        size: 'sm'
      })
    }
  ]
})
export class TodosPageComponent {}

We can add this to the providers component and this value will be used for all components declared and child in the templates. But we can still rewrite this variable directly as follows:

@Component({
  selector: 'app-todos-page',
  template: `
   <app-select></app-select>
   <app-select size="lg"></app-select>
`,
  providers: [
    {
      provide: SelectConfig,
      useValue: setSelectConfig({
        size: 'sm'
      })
    }
  ]
})
export class TodosPageComponent {}

The same strategy is used for lazy modules because it creates a new injection.

Using Directives


Suppose we have components with a lot of input. We set the same values ​​for most of them. In the example, it will look like this:

<!-- It can be in the same template or in different places in the app -->
<app-select size="lg" position="right" inputThree="someValue"></app-select>
<app-select size="lg" position="right" inputThree="someValue"></app-select>
<app-select size="lg" position="right  inputThree="someValue"></app-select>
<app-select size="lg" position="right" inputThree="someValue"></app-select>
<app-select size="lg" position="right" inputThree="someValue"></app-select>

We can repeat these values, but we can create a directive that will pass the necessary values ​​of all variables:

@Directive({
  selector: '[selectConfigOne]',
  providers: [{
    provide: SelectConfig,
    useValue: setSelectConfig({
      size: 'lg',
      placement: 'right',
      inputThree: 'someValue'
    })
  }]
})
export class SelectConfigOneDirective {}

And we can use this directive where we need:

<h1>Using Inputs</h1>
<app-select size="lg"></app-select>
<app-select placement="top"></app-select>

<h1>Using a Directive</h1>
<app-select selectConfigOne></app-select>

<app-todos-page></app-todos-page>

All Articles