I'm doing the following, which works as expected.
<ng-container
*ngComponentOutlet="
step.component | async;
inputs: {
someId: someId,
stepper: stepper,
step: step,
model: model,
};
outputs: { decision: onDecision($event) }
" />
Since it works, I infer that I'm using correct imports, correct packages, syntax etc. Now, I need to establish communication in the other direction, so I simply added outputs as shown below.
<ng-container
*ngComponentOutlet="
step.component | async;
inputs: {
someId: someId,
stepper: stepper,
step: step,
model: model,
};
outputs: { decision: onDecision($event) }
" />
It sort-of works but gives an error suggesting that on the initial run, the component isn't ready yet so the outputs aren't recognized. I was surprised because the inputs seem to be, so I went to the docs for NgComponentOutlet
and discovered that the outputs
isn't documented there. There's no examples nor mentioning it at all.
Where is that field documented and am I using something obsolete or magical at the moment?
I sense that "it works but", because (aside of the above), I should get the error on not yet initialized component due to the async pipe. So I'm sensing that there's something wrong deeper than I see.
As of 26/7/2025, I can see there is no such outputs
in ngComponentOutlet
.
There are only inputs, if you want to achieve it, you can pass either an observable or an output emitter as an input and listen for emissions.
We create the inputs to accept the observable or output emitter.
export class Dummy {
test = input<OutputEmitterRef<string>>();
test2 = input<Subject<string>>();
someId = input<string>('');
}
Then we can receive these emissions on the parent component.
ngOnInit() {
this.test.subscribe(this.onDecision.bind(this));
this.test2Obs$.subscribe(this.onDecision.bind(this));
}
import {
Component,
input,
output,
OutputEmitterRef,
viewChild,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { NgComponentOutlet } from '@angular/common';
import { Subject } from 'rxjs';
@Component({
selector: 'app-dummy',
template: `
<div>{{someId()}}</div>
<button (click)="test()?.emit('asdf')">clicker</button>
<button (click)="test2()?.next('qwerty')">clicker 2</button>
`,
})
export class Dummy {
test = input<OutputEmitterRef<string>>();
test2 = input<Subject<string>>();
someId = input<string>('');
}
@Component({
selector: 'app-root',
imports: [NgComponentOutlet],
template: `
<ng-container
*ngComponentOutlet="
dummy;
inputs: {
someId: someId,
test: test,
test2: test2
};
" />
`,
})
export class App {
someId = '123';
dummy = Dummy;
dummyChild = viewChild(Dummy);
test = output<string>();
test2 = new Subject<string>();
test2Obs$ = this.test2.asObservable();
onDecision(event: any) {
alert(event);
}
ngOnInit() {
this.test.subscribe(this.onDecision.bind(this));
this.test2Obs$.subscribe(this.onDecision.bind(this));
}
}
bootstrapApplication(App);