I fount this similar question. angular 4+ assign @Input for ngComponentOutlet dynamically created component
But it has been about a month. Has anything changed?
Basically, I followed this guide and created a dynamic component: https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
With this approach, I can assign a value to the dynamic component: (<AdComponent>componentRef.instance).data = adItem.data;
Is it still true I can't assign a value to the dynamic component with NgComponentOutlet out of the box? ( https://angular.io/docs/ts/latest/api/common/index/NgComponentOutlet-directive.html )
You can pass in a custom injector like this - https://github.com/angular/angular/issues/16373#issuecomment-306544456
This was kind of a hack so we ended up using this library-
https://www.npmjs.com/package/ng-dynamic-component
Worked like a charm!
In my case, i use ngBaseDef.inputs.data at component.
See example:
import {Component, Injectable, Input} from '@angular/core';
import {NgbActiveModal, NgbModal} from "@ng-bootstrap/ng-bootstrap";
import {ExampleComponent } from "../components/modals/moder-info/example.component";
@Component({
selector: 'modal-template',
template: `
<div class="modal-header">
<h4 class="modal-title" [innerText]="title"></h4>
<button type="button" class="close" aria-label="Close" (click)="close()">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ng-content *ngComponentOutlet="childComponent;"></ng-content>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="close()">Close</button>
</div>
`
})
export class ModalTemplate {
@Input() title;
@Input() onClose;
@Input() childComponent;
constructor(public activeModal: NgbActiveModal) {}
close() {
this.activeModal.dismiss('close');
this.onClose();
}
}
export interface ModalServiceOptions {
data: any;
title: string
onClose: any,
componentName: string
}
@Injectable({
providedIn: 'root'
})
export class ModalService {
constructor(private mService: NgbModal) {}
open(options: ModalServiceOptions) {
let types = {'example': ExampleComponent };
let modal = this.mService.open(ModalTemplate, {size: 'lg'});
modal.componentInstance.title = options.title;
modal.componentInstance.onClose = options.onClose;
let component = types[options.componentName];
component.ngBaseDef.inputs.data = options.data;
modal.componentInstance.childComponent = types[options.componentName];
}
}
ExampleComponent
import {Component, Input, OnInit} from '@angular/core';
@Component({
selector: 'example-component',
templateUrl: './example.component.html',
styleUrls: ['./example.component.sass']
})
export class ExampleComponent implements OnInit {
@Input() data;
constructor() {
let dataOfConstructor: any = this.constructor;
let data = dataOfConstructor.ngBaseDef.inputs.data;
console.log('data:', data); // Here is my data, i injected it above
}
ngOnInit() {
}
}
Have a nice day! Hope it helps!
Basically, there are 2 ways to make the dynamic injection happen.
inject values through the constructor, using a custom (or global) injector: *ngComponentOutlet="myComponent; injector: myInjector
, where myInjector
is the injector you want to use (if default injector is OK -- take it from your parent's context by adding public myInjector: Injector
in your parent component's constructor). That wasn't an option for me as I wanted to keep my dynamic (child) component's constructor clean.
make a callback that's called when the component is created, and inside that callback assign your @Input
values and subscribe your @Output
s manually. Now, ngComponentOutlet
does not provide any callback functionality, so you may have to reinvent the wheel - create an ngComponentOutlet
directive that supports callbacks. And that's exactly what this solution is all about.
I created a custom directive myComponentOutlet
( https://github.com/angular/angular/issues/15360#issuecomment-1070420494 ) - a customizable analogue for ngComponentOutlet
. Hopefully, it will eventually make its way to the Angular's src.
Here's the directive:
import {
AfterViewInit,
ComponentFactoryResolver,
ComponentRef,
Directive,
ElementRef,
EventEmitter,
Injector,
Input,
OnDestroy,
Output,
Type,
ViewContainerRef
} from '@angular/core';
/*
USAGE:
<div myComponentOutlet
[component]="inner.component"
[injector]="inner.injector"
(create)="componentCreated($event)"></div>
*/
@Directive({
selector: '[myComponentOutlet]',
})
export class MyComponentOutletDirective<T> implements AfterViewInit, OnDestroy {
@Input() component: Type<T>;
@Input() injector: Injector;
@Output() create = new EventEmitter<ComponentRef<T>>();
@Output() destroy = new EventEmitter<ComponentRef<T>>();
private componentRef: ComponentRef<T>;
constructor(
private viewContainerRef: ViewContainerRef,
private resolver: ComponentFactoryResolver,
private elRef: ElementRef,
private globalInjector: Injector
) {
this.injector = globalInjector;
}
ngAfterViewInit() {
const injector = this.injector || this.globalInjector;
const factory = this.resolver.resolveComponentFactory(this.component);
this.componentRef = this.viewContainerRef.createComponent(factory, 0, injector);
this.elRef.nativeElement.appendChild(this.componentRef.location.nativeElement);
this.create?.emit(this.componentRef);
}
ngOnDestroy(): void {
this.destroy?.emit(this.componentRef);
}
}
And here's the usage:
<div myComponentOutlet
[component]="inner.component"
[injector]="inner.injector"
(create)="componentCreated($event)"></div>
Adjust it to your own needs
It's not as robust as <div *ngComponentOutlet="myComponent; inputs:[...]; outputs:[...]"></div>
, but it's something and it's quite clean and it works.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.