Angular 7: внедрение с зависимостями для ngComponentOutlet

Мне нужно использовать какой-нибудь декоратор @Input() для моего ngComponentOutlet.

Но кажется, что Angular не имеет этой функции. Вместо этого все, что я хочу передать внутри своих компонентов выхода, должно быть предоставлено через Injector.

И это нормально, если я хочу инициировать то, что я хочу предоставить внутри инъекционного класса. Но мне нужно погрузиться глубже и предоставить какую-то наблюдаемую переменную (например, тип Observeble<number>) на этапе создания инжектора. Но я не могу получить наблюдаемую переменную внутри выходного компонента. Получение следующей ошибки: NullInjectorError: No provider for [object Object]!.

Вот пример кода, я получил этот шаблон из документов angular (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 внутри компонента выхода.

P.S. Если я передам NgTemplateOutletCompleteExample в массив deps и получу NgTemplateOutletCompleteExample.suffix$ внутри инжектируемого компонента Greeter - это сработает. Но дело в том, что у меня много компонентов NgTemplateOutletCompleteExample, и эта вещь не подходит для моего случая.


person hofshteyn    schedule 19.11.2019    source источник


Ответы (3)


У меня была аналогичная проблема с динамическим внедрением компонентов, принимающих входные данные и возвращающих выходные данные. Изначально Angular не поддерживает @Input() и @Output для динамических компонентов.

Вы можете использовать пакет NgDynamicComponent: https://www.npmjs.com/package/ng-dynamic-component.

Поддержка синтаксиса NgComponentOutlet

Пакет поддерживает синтаксис NgComponentOutlet, как показано здесь: https://www.npmjs.com/package/ng-dynamic-component#ngcomponentoutlet-support

Из того, что я вижу из вашего кода, это то, о чем вы просите: поддержка NgComponentOutlet + Inputs.

Пользовательский динамический инжектор для более продвинутых вещей

Вы также можете создать собственный инжектор: https://www.npmjs.com/package/ng-dynamic-component#extra

person Exomus    schedule 19.11.2019

Главное, что Инжектор - это статический инжектор. Итак, чтобы предоставить какие-либо данные внутри вывода компонента (или получить любые обратные вызовы), я должен использовать useValue вместо deps.

Конструктор в NgTemplateOutletCompleteExample должен быть таким:

constructor(injector: Injector) {
    this.myInjector =
        Injector.create({providers: [{provide: Greeter, deps: [], useValue: {suffix$: this.suffix$}], parent: injector});
}
person hofshteyn    schedule 19.11.2019
comment
Я не уверен, что понимаю ваше обновление. NgDynamicComponent не решает вашу проблему? - person Exomus; 19.11.2019

Чтобы понять, почему вы получаете эту ошибку, я бы рекомендовал взглянуть на Поставщики зависимостей. из документации.

Ошибка исходит отсюда

Injector.create({providers: [{provide: Greeter, deps: [this.suffix$]}], parent: injector});

Короче говоря, массив deps должен представлять собой список допустимых токенов внедрения зависимостей, и для 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,
  })

 /* ... */
}

StackBlitz

person Andrei Gătej    schedule 20.11.2019