paint-brush
How to Use Angular @Input to Pass Data to Dynamically Created Componentsby@briantreese
873 reads
873 reads

How to Use Angular @Input to Pass Data to Dynamically Created Components

by Brian TreeseFebruary 27th, 2024
Read on Terminal Reader
Read this story w/o Javascript

Too Long; Didn't Read

Angular 16 allows you to pass data between dynamically created components from the parent to the child. Previously, we had to provide the info in the parent and then inject it within the child component. Now, we can do this using the `@Input` decorator instead.
featured image - How to Use Angular @Input to Pass Data to Dynamically Created Components
Brian Treese HackerNoon profile picture

If you’re using dynamically created components in Angular, you’ve probably found it difficult to pass information between the parent and child components when needed. You need to provide the info in the parent, and then inject it within the child component. While it’s not necessarily difficult to do, it results in a lot of extra boilerplate code.


It would be so much better if we could just use the @Input decorator like we’re used to. Well, guess what? Angular supports doing this exact thing as of version sixteen. In this post, I’ll show you how. Alright, let’s get to it.

Passing Data the Old Way Using the Angular Injector

First, let’s look at how we used to pass data to dynamically created components from the parent. Here, in this example, within the component template, we have our *ngComponentOutlet, and we’re passing it to our player component.


In this case, we’re also passing a custom child injector, and this is how we’d inject the data for the child component before.

main.ts

@Component({
    selector: 'app-root',
    template: `
        <ng-container *ngComponentOutlet="playerComponent; injector: childInjector"></ng-container>
    `
})
export class App {
    protected playerComponent = PlayerComponent;
}


And to create this child injector, we needed to create a property so that it could be set and then used within the template.


export class App {
    protected childInjector?: Injector;
    ...
}


Then, we needed to inject in the Injector from the angular core within the constructor.


export class App {
    ...
    constructor(private injector: Injector) {
    }
}


After that, we needed to set the child injector using the create method and the providers’ array to provide our player token to the child.


export class App {
    ...
    constructor(private injector: Injector) {
        this.childInjector = Injector.create({
            providers: [
                { provide: PlayerToken, useValue: this.player }
            ],
            parent: this.injector
        });
    }
}


And finally, over in the child component, our player component, we needed to set our player property with the inject method from the angular core.

player.component.ts

export class PlayerComponent {
    protected player?: Player = inject(PlayerToken);
}


So, all of this is just to pass a simple player object to a dynamic component. I mean, if this wasn’t a dynamic component, we’d simply make this an @Input and just bind the player data object to the @Input in the parent. But this is a dynamically created component, so we can’t do this, right?


Well, as of Angular sixteen, we actually can use @Inputs instead, and it’s much simpler than what we’ve seen so far.

Passing Data the New Way Using the *ngComponentOutlet Inputs Object

We start by changing this player property to an @Input instead. And, it’s simply typed into our player interface.

player.component.ts

export class PlayerComponent {
    @Input() player?: Player;
}


Now, we can remove the inject method and player token imports since they’re no longer needed. Then, back over in our parent, in the template, we can remove the injector, and instead, replace it with an inputs object. We can pass this object on any number of inputs. So, if we had five inputs, we’d just include their names, and then pass each one whatever data we need.


But in this case, we have only one input on our child component, player, so that’s all we need to pass it.

main.ts

@Component({
    selector: 'app-root',
    template: `
        <ng-container *ngComponentOutlet="playerComponent; inputs: { player }"></ng-container>
    `
})


Now, we can remove the child injector. This means we can also remove the constructor altogether. And finally, we can remove the Injector from the angular core and the player token import since we’re no longer using them.


export class App {
    protected player: Player = {
        name: 'LeBron James',
        games: 1421,
        points: 38652,
        fieldGoalPercentage: 0.505,
        threePointPercentage: 0.345,
        imageName: 'lebron-james'
  };
  protected playerComponent = PlayerComponent;
}


And that’s it. We can see everything works just as it used to. So, quite a bit easier to pull off and less code too which is always great.


One bummer though is that while @Inputs are supported, @Outputs aren’t. Kind of a bummer, but it is what it is. Hopefully, they’ll add support in the future because this would be very handy as well, but we’ll just have to wait and see.

Want to See It in Action?

Check out the demo code and examples of these techniques in the Stackblitz example below. If you have any questions or thoughts, don’t hesitate to leave a comment.