Pass Observable data from Parent Component to a Child Component created with componentFactoryResolver in Angular 2+
Asked Answered
D

1

4

I need help as I am a bit lost right now. So, I have a component that dynamically injets a child component into its template using componentFactoryResolver , here is my HTML

<div class="dialog">
    <div #container></div>
    <button (click)="move('back')">Back</button>
    <button (click)="move('forwards')">Forwards</button>
</div>

also in my component I have an observable that captures the click of the buttons like so, here is my (edited / reduced) code

// parent-component.ts

@ViewChild('container', {read: ViewContainerRef})
public dialogContainer: ViewContainerRef;

public navigationClick$: Observable<string> = new Subject<string>();

// click event on buttons
public move(direction): void {
    this.navigationClick$.next(direction);
}

// code to inject child dynamic component, please ignore the args / variables

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.data.component);
this.componentRef = this.dialogContainer.createComponent(componentFactory);
this.embeddedcomponent = this.componentRef.instance as IWizardDialog;
this.embeddedcomponent.data = this.data.data;

Now I would like to pass the latest observable value from navigationClick$ to the child component I amend the code in my parent component thus...

const componentFactory = this.componentFactoryResolver.resolveComponentFactory(this.data.component);
this.componentRef = this.dialogContainer.createComponent(componentFactory);

// NEW CODE
// here I subscribe to the observable and try to pass the value to the child component
this.navigationClick$.subscribe((data: string) => {
  this.componentRef.instance.direction = data;
});

this.embeddedcomponent = this.componentRef.instance as IWizardDialog;
this.embeddedcomponent.data = this.data.data;

The subscribe is working in the parent as I would expect however I am unsure how I would capture / pass the subscribe data to the child component, for example can I just declare this as a Input()

// child-component.ts

@Input() public direction: string;

However this will just be undefined and isn't what I need. How do I pass the direction data from the subscribe to the child component or what code / feature do I need to receive the event / direction string? Any advice is appreciated.

If my wording is bad or confusing please say and I will rework the question.

Distichous answered 29/9, 2017 at 10:49 Comment(3)
How do you whant the application to behave if the user hits the browser backbutton? Do you want it to do the same as <button (click)="move('back')">Back</button>. Or do you want it to go to the last page the user visited. If you want it to step one 'dialog' back you should use angular routing. Move back and forward could be a [routerLink] to the same page with a different url parameter.Parahydrogen
The behaviour doesn't matter, I am navigating data not the window, I just want to pass the string to the child, everything is taken care of.Distichous
Ok answer comming upParahydrogen
P
3

I would use a service. Not ViewChild. With a service the components would not need to know of each other

 @Injectable()
  export class YourService {
  move$: Observable<any>;
  private moveSubject: Subject<any> = new Subject();

  constructor() {
    this.move$ = this.moveSubject.asObservable();
  }

  public move(direction) {
     this.moveSubject.next(direction);
  }


}

Usage in parent

contructor(public yourService:YourService){
}

html in parent

<button (click)="yourService.move('back')">Back</button>

Usage in child

YourChild implements OnInit, OnDestroy {
    private subscriptions: Subscription = new Subscription();
    constructor(private yourService:YourService) {

    }
    ngOnInit(): void {
        this.subscriptions.add(this.yourService.move$.subscribe(direction => {
            //Do something
        }));        
    }
    ngOnDestroy(): void {
    this.subscriptions.unsubscribe();
}

}

Parahydrogen answered 29/9, 2017 at 12:5 Comment(8)
That's great thanks, however the child this.yourService.move$.subscribe doesn't seem to pick up the nextbeing fired, although I can catch this in the service itself so the .next() is working... I'll keep tryingDistichous
Maybe its <button (click)="yourService.move('back')">Back</button>. Add a method in the component onButtonClick and call move from there instead? What version of Angular do you use?Parahydrogen
Thanks, I did that already... I'll keep fiddling and try to make this work... I'll set up a plunker / jsbinDistichous
I'll check up my code again. This is working for me. I'm on angular 4.Parahydrogen
Hi there, sorry for the delay... even when I make a dummy project I am doing something wrong, I have placed it here if you would like to check it: github.com/MikeSav/experimentalDistichous
Looked at you code. Remove the Provider inside you components. The MoveService should only be define in the app.module. I think Angular create a new instance everytime you add the service as a provider.Parahydrogen
And. Remove all referenses to ViewContainerRef. You only need to use ViewContainerRef if your doing something special. Instead use the selector you defined in your child component. ex in Parent html: <app-child></app-child><button (click)="moveThis('forwards')">Click me</button>Parahydrogen
And in your child component. dont forget to Implement OnDestoy and unsubscribe or you might get a memory leakParahydrogen

© 2022 - 2024 — McMap. All rights reserved.