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
CodePudding user response:
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);
CodePudding user response:
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 actualstring
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