Angular 7: инъекция с зависимостями для ngComponentOutlet
Мне нужно использовать какой-то @Input()
декоратор для моего ngComponentOutlet
.
Но похоже, что в Angular этой функции нет. Вместо этого все вещи, которые я хочу передать в свои компоненты розетки, должны быть предоставлены черезInjector
.
И это нормально, если я хочу инициировать то, что я хочу предоставить внутри инъекционного класса. Но мне нужно погрузиться глубже и предоставить какую-то наблюдаемую переменную (Observeble<number>
типа например) при создании Injector step. Но я не могу получить наблюдаемую переменную внутри выходного компонента. Получение следующей ошибки:NullInjectorError: No provider for [object Object]!
.
Вот пример кода, я получил этот шаблон из угловых документов (https://angular.io/api/common/NgComponentOutlet) и немного изменил:
@Injectable()
export class Greeter {
suffix$: Observable<number> = null;
constructor(private _suffix$: Observable<number>) {
this.suffix$ = this._suffix$;
}
}
@Component({
selector: 'complete-component',
template: `Complete: {{ greeter.suffix$ | async }}`
})
export class CompleteComponent {
constructor(public greeter: Greeter) {
this.greeter.suffix$.subscribe(data => console.log('data', data));
// not working
}
}
@Component({
selector: 'ng-component-outlet-complete-example',
template: `
<ng-container *ngComponentOutlet="CompleteComponent;
injector: myInjector;"
})
export class NgTemplateOutletCompleteExample {
CompleteComponent = CompleteComponent;
myInjector: Injector;
suffix$: Observable<number> = of(3);
constructor(injector: Injector) {
this.myInjector =
Injector.create({providers: [{provide: Greeter, deps: [this.suffix$]}], parent: injector});
}
}
Итак, как мне получить и подписаться на это $suffix
переменная внутренняя составляющая выпуска.
PS Если я предоставлюNgTemplateOutletCompleteExample
в deps
массив и получить NgTemplateOutletCompleteExample.suffix$
внутри инъекционный компонент Greeter
- это сработает. Но дело в том, что у меня многоNgTemplateOutletCompleteExample
компоненты и эта вещь недопустима в моем случае.
3 ответа
Главное, что Инжектор - это статический инжектор. Итак, чтобы предоставить какие-либо данные внутри компонента oultet (или получить какие-либо обратные вызовы), я должен использоватьuseValue
вместо deps
.
Подрядчик в NgTemplateOutletCompleteExample
должно быть так:
constructor(injector: Injector) {
this.myInjector =
Injector.create({providers: [{provide: Greeter, deps: [], useValue: {suffix$: this.suffix$}], parent: injector});
}
У меня была аналогичная проблема с динамической инъекцией компонентов, принимающих входы и возвращающих выходы. Изначально Angular не поддерживает@Input()
а также @Output
на динамических компонентах.
Вы можете использовать пакет NgDynamicComponent:https://www.npmjs.com/package/ng-dynamic-component.
Поддержка синтаксиса NgComponentOutlet
Пакет поддерживает синтаксис NgComponentOutlet, как вы можете видеть здесь: https://www.npmjs.com/package/ng-dynamic-component
Из того, что я вижу в вашем коде, вы просите: поддержку NgComponentOutlet + Inputs.
Индивидуальный динамический инжектор для более сложных задач
Вы также можете создать собственный инжектор:https://www.npmjs.com/package/ng-dynamic-component
Чтобы понять, почему вы получаете эту ошибку, я бы рекомендовал взглянуть на поставщиков зависимостей из документации.
Ошибка исходит отсюда
Injector.create({providers: [{provide: Greeter, deps: [this.suffix$]}], parent: injector});
Короче говоря, deps
массив должен быть списком допустимых токенов DI, и токен не настроен дляthis.suffix$
.
Цитата из источников (Angular 8.0.0)
export interface FactorySansProvider {
/**
* A function to invoke to create a value for this `token`. The function is invoked with
* resolved values of `token`s in the `deps` field.
*/
useFactory: Function;
/**
* A list of `token`s which need to be resolved by the injector. The list of values is then
* used as arguments to the `useFactory` function.
*/
deps?: any[];
}
Вот один из способов решить эту проблему:
const OBS_TOKEN = new InjectionToken('obs');
@Injectable()
export class Greeter {
greeterProp = 'hello!';
constructor (@Inject(OBS_TOKEN) public suffix$: Observable<any>) {
console.log('[GREETER]', this.suffix$)
}
}
@Component({ /* ... */ })
export class NgTemplateOutletCompleteExample {
/* ... */
myInjector = Injector.create({
providers: [
{ provide: Greeter, deps: [OBS_TOKEN], },
{ provide: OBS_TOKEN, useValue: this.dummyObs$ },
],
parent: this.inj,
})
/* ... */
}