3

I've manually created an instance of a class. This class has a @Component decorator. How do I render its template?

@Component({ template: `hello {{ who }}` })
class Greeting {
  constructor (public who: string) { }
}

@Component({ template: `<ng-somehow-render [instance]="greeting"/>` })
class OtherComponent {

  public greeting: Greeting

  constructor () {
    this.greeting = new Greeting('world')
  }

}
  • ComponentFactoryResolver will give me an instance of the component. I already have the instance and I want to render it.
  • ngComponentOutlet will internally create the instance. My instance is then useless.
1
  • What are you trying to achieve exactly? You cannot just 'manually' instantiate a component instance without using ComponentFactoryResolver, which will give you not only the instance of the component, but also all related objects, including the hostView, which you need to include it in the DOM. Commented Apr 28, 2020 at 7:52

1 Answer 1

1
+500

Looks like you need a middleware component to render instances of another components.

it could be this one

@Component({
    selector: 'ng-somehow-render',
    template: '',
})
export class NgSomehowRenderComponent implements OnInit {
    @Input() instance: any;

    constructor (
        private cfr: ComponentFactoryResolver,
        private vcr: ViewContainerRef,
        private cdr: ChangeDetectorRef,
    ) {
    }

    public ngOnInit(): void {
        const componentFactory = this.cfr.resolveComponentFactory(this.instance.constructor);

        this.vcr.clear();
        const componentRef = this.vcr.createComponent(componentFactory);
        for (const key of Object.getOwnPropertyNames(this.instance)) {
            componentRef.instance[key] = this.instance[key];
        }
        this.cdr.detectChanges();
    }
}

Simply add its declaration into your modules.

BTW the Greeting component should be declared in your modules in entryComponents.

@NgModule({
  declarations: [AppComponent, Greeting, NgSomehowRenderComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [Greeting]
})

Because Angular will try to solve constructor dependencies you need to add @Optional() decorator there.

@Component({
  template: `hello {{ who }}`
})
export class Greeting {
  constructor(@Optional() public who: string) {}
}

profit.

You can check live demo here: https://codesandbox.io/s/jolly-villani-32m5w?file=/src/app/app.component.ts

All the things from the official doc, so you can use it without problems: https://angular.io/guide/dynamic-component-loader#resolving-components

Sign up to request clarification or add additional context in comments.

2 Comments

Any idea why ng-content isn't preserved? codesandbox.io/s/charming-golick-uonzg
I think what you do differs from the original task. You try to rerender a rendered component. Not sure it is possible, because how lifecycle hooks should work then? ngOnInit has been executed already, but to render the component again - we need to execute it again...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.