4
votes

I have an App component with dynamically created components. Parent component has an <ng-content> element so that we can project our child components inside the Parent component.

App Component:

@Component({
  selector: 'my-app',
  template:`<ng-container #parentcontainer></ng-container>`,
})

Parent component:

@Component({
  selector: 'parent',
  template: `
  <ng-container #container></ng-container>
  <ng-content></ng-content>

                `
})

Child Component:

@Component({
  selector: 'child',
  template: `<label>
                <input type="radio">
              </label>
                `
})

My question is, is there any way to access the App component's ViewContainerRef (#parentcontainer) inside Child component ? Goal is to dynamically insert other components inside #parentcontainer, at a later stage, like on some button click from Child Component.

Here is the working sample on StackBlitz

1
not sure what your task is. if you want to pass a container, just use data binding. But if you want to insert dynamic components by action from child component into parent component using container, I'm not sure it's the correct approach. I'd just send an event upwards with relevant data and let the parent component add the component.Max Koretskyi

1 Answers

7
votes

You can create a new component from within the child component, and attach it's HTML element anywhere you like in the DOM

// remember to add YourComponent to entryComponents: [] of the module
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(YourComponent);
let componentRef = viewContainerRef.createComponent(componentFactory, index);

componentRef.instance.model = model; // where model is an input
componentRef.changeDetectorRef.detectChanges(); // update it's view
let htmlElement = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0] as HTMLElement;

// Now do what you want with the htmlElement e.g. document.querySelector('body').appendChild(htmlElement)

Would that work for you? Note, that the component will only stay alive for as long as the Child Component is alive

Alternatively, to keep the components alive for the app duration, create a service and provide it once at the AppComponent level. Note that this uses the ApplicationRef, which allows you to call attachView() - this might be a solution

constructor(
      private componentFactoryResolver: ComponentFactoryResolver,
      private appRef: ApplicationRef,
      private injector: Injector
  ) { }

  appendComponentToBody(component: any) {
    const componentRef = this.componentFactoryResolver
      .resolveComponentFactory(component)
      .create(this.injector);    
    this.appRef.attachView(componentRef.hostView);
    const domElem = (componentRef.hostView as EmbeddedViewRef<any>)
      .rootNodes[0] as HTMLElement;    
    // Append to body or wherever you want
    document.body.appendChild(domElem);
  }

There's more details on the Service example here

The best solution is probably to use the Angular Material CDK functionality for Portal and PortalHost. It's pretty new, but it's purpose is to create components from within an Angular app that run outside of the actual my-app element - this might be a better long term solution - it's used for making modals etc in their examples