0

I'm creating my component by Typescript and passing from there my inputs to my child.

parent TS

this.childComponent = this.viewContainerRef.createComponent(this.data.body).instance;
this.childComponent['childInput'] = 5;

child TS

@Input() childInput!: number;
ngOnChanges(changes: SimpleChanges): void {
    if(changes['childInput'].previousValue !== changes['childInput'].currentValue)
    console.log('change fired')
}

how can I fire from parent the OnChanges?

I tryed this.childComponent.onChanges(); but it was not working because I didn't past any params

thanks

Python
  • 31
  • 6

2 Answers2

1

If you are using Angular v14.1.0

You can use setInput method on componentRef to set Input dynamically. It will automatically trigger ngOnChanges whenever value changes

this.childComponent = this.viewContainerRef.createComponent(this.data.body);
this.childComponent.setInput('childInput',5);

Sample Working Example

Chellappan வ
  • 23,645
  • 3
  • 29
  • 60
0

Assuming the answer in this post is still correct (How do you use @Input with components created with a ComponentFactoryResolver?) you can't easily define the Input from the code behind since it has to be statically added to the component via the HTML.

However there are some options you can pursue:

  • You can use .setInput as Chellappan suggested, one thing to note is that since it is using an actual string value for the key if you ever do decide to change the input name you need to make sure you change it manually as replacing via the editor won't change string literal values
  • You can instead define an InjectionToken to provide to your component at time of creation which you can then tap into as part of its constructor. This approach is a bit heavier and more complex, but has the benefit of being statically typed so if you forget to add a field then the compiler will complain
  • Leverage the power of services and observables as defined here (Angular 6 add items into Observable) and simply listen to changes to that observable and react to it accordingly.

If you wanted to use an InjectionToken you could do something like so for example:

export const YOUR_INJECTION_TOKEN_NAME = new InjectionToken<IMyInjectionToken>('YOUR_INJECTION_TOKEN_NAME');

export interface IMyInjectionToken{
    //fields and methods can go here
}

Then in your creation code do the following:

const compInstance = viewRef.createComponent<YourComponent>(YourComponent, {
            injector: Injector.create({
                providers: [
                    {provide: YOUR_INJECTION_TOKEN_NAME ,
                    useValue: <your IMyInjectionToken interface data goes here>}
                ]
            })
        }).instance

Then in the component that was made you can tap into it via the constructor as so:

constructor(@Inject(YOUR_INJECTION_TOKEN_NAME ) public data : IMyInjectionToken) 

Edit: I do not see an equivalent in angular less than v14, however I made this very simple stackblitz to show a work around using observables and subscribing to them. (https://stackblitz.com/edit/angular-voymf5?file=src/app/app.component.html)

In a nut shell what I made here is two components, I assume that the set of components you create is probably very limited and thus you can predefine them ahead of time

  • ComponentOne
  • ComponentTwo

These are just two basic components that take an input, and display a very basic html string.

I added a model named ICustomComponentModel which contains a definition for the name of the component we are adding, and the id. The id is a number and acts as a sort of index for where it is located. The name acts as an identifier for the type of component to render.

The real magic happens in us using a Subject which we can subscribe to and use an async pipe in the HTML which listens to changes in the observable and triggers rendering the changes. Of note is that this does not trigger the onChanges, hence why we have to subscribe to the observable as our very specific change detection.

The HTML knows what to render using an [ngSwitch] directive, it works like any other switch statement. Make sure you look at the imports I have in the app.module.ts file. Namely for ngSwitch to work you need to import CommonModule

SomeStudent
  • 2,856
  • 1
  • 22
  • 36
  • - "you can't easily define the Input from the code behind since it has to be statically added to the component via the HTML." Is there a way to call the component created dynamically & passing the inputs by html? and even passing them dynamically also? :p Is there a way to do it like @Chellappan வ proposed but with a version 13.2? – Python Sep 12 '22 at 13:22
  • @Python I updated my answer with a working example of a slight work around – SomeStudent Sep 12 '22 at 14:15
  • I created a Dialog (using Material Dialog) which ask the components to display as child it's sharing the inputs & outputs, Every thing works well, the only problem I have is to fire the change and I can't call the child in HTML part because I would have problems with the module dependencies ,... and I have no mood to refactor all the stuff ^^. So your example won't save me :p. – Python Sep 12 '22 at 14:22
  • I also can't start to play with the injections in every child because it would take me to much time in refactor & those childeren are used also without the dialogs – Python Sep 12 '22 at 14:26
  • Well in the end I really like your example, the only problem is that I work with more that 40 different components so it will be a bit ... making a switch interface representing each one of them :/ – Python Sep 12 '22 at 14:38
  • I made another stackblitz, oddly had to also declare `entryComponents` in my app module for my dialog, probably something screwy with stackblit and mat dialog. But the idea here is the same, but we let the component tell us how many we need to create. Given that the dialog returns the results of what the user putted you can leverage that along with my original idea. Again, that is just an approach, and sadly without angular 14 you cannot set an input from the component.ts file and trigger the onchanges. https://stackblitz.com/edit/angular-mbkxen?file=src/app/app.module.ts – SomeStudent Sep 12 '22 at 14:56
  • @Python sadly sometimes in the world of development we have to bite the bullet, if my solution helped feel free to up vote it or mark it as the correct answer for you. Conversely, if you are already on angular 13, then the update to angular 14 might not be too bad and it might be something you should look into – SomeStudent Sep 12 '22 at 15:00
  • 1
    don't wry I already liked, but I'm to low level to increment your score. I will see to upgrade to 14. but when we aren't working alone on a project there are some dependencies to fix first ;-) – Python Sep 13 '22 at 06:36