如何将可观察值传递给@Input() Angular 4

How to pass observable value to @Input() Angular 4

我是 angular 的新手,我有以下情况,即我有一个服务 getAnswers():Observable<AnswerBase<any>[]> 和两个相互关联的组件。

online-quote 组件在其 ngOnInit() 方法中调用服务 getAnswers():Observable<AnswerBase<any>[]> 并将其结果传递给组件 动态形式

为了说明情况,这是我的两个组件的代码:

在线-quote.component.html:

 <div>
    <app-dynamic-form [answers]="(answers$ | async)"></app-dynamic-form>
</div>

在线-quote.component.ts:

@Component({
  selector: 'app-online-quote',
  templateUrl: './online-quote.component.html',
  styleUrls: ['./online-quote.component.css'],
  providers:  [DynamicFormService]
})
export class OnlineQuoteComponent implements OnInit {

  public answers$: Observable<any[]>;

  constructor(private service: DynamicFormService) {

   }

  ngOnInit() {
    this.answers$=this.service.getAnswers("CAR00PR");
  }

}

动态-form.component.html:

<div *ngFor="let answer of answers">
 <app-question *ngIf="actualPage===1" [answer]="answer"></app-question>
</div>

动态-form.component.ts:

@Component({
  selector: 'app-dynamic-form',
  templateUrl: './dynamic-form.component.html',
  styleUrls: ['./dynamic-form.component.css'],
  providers: [ AnswerControlService ]
})
export class DynamicFormComponent implements OnInit {
  @Input() answers: AnswerBase<any>[];

  constructor(private qcs: AnswerControlService, private service: DynamicFormService) {  }

  ngOnInit() {

    this.form = this.qcs.toFormGroup(this.answers);

  }

我的问题是,如果结果信息为服务 getAnswers():Observable<AnswerBase<any>[]> 是可观察的。

我尝试了很多方法,但都没有用。我希望有人能帮我解决这个问题。非常感谢!

假设DynamicFormService.getAnswers('CAR00PR')异步(可能是),使用async Pipe传递异步结果是正确的方式,但你不能指望由于 Asynchonous,在创建 DynamicFormComponent 时(at ngOnInit)立即获取异步结果。当 运行 您的以下代码行时,结果尚未准备好。

this.form = this.qcs.toFormGroup(this.answers);

有多种方法可以解决您的问题。

1。在 ngOnChanges lifehook 处收听 @Input() answers 的 valueChange。

ngOnChanges(changes) {
  if (changes.answers) {
    // deal with asynchronous Observable result
    this.form = this.qcs.toFormGroup(changes.answers.currentValue);
  }
}

2。将 Observable 直接传递给 DynamicFormComponent 并订阅它以收听它的结果。

online-quote.component.html:

<app-dynamic-form [answers]="answers$"></app-dynamic-form>

dynamic-form.component.ts:

@Component({
  ...
})
export class DynamicFormComponent implements OnInit {
  @Input() answers: Observable<AnswerBase[]>;

  ngOnInit() {
    this.answers.subscribe(val => {
      // deal with asynchronous Observable result
      this.form = this.qcs.toFormGroup(this.answers);
    })
}

我有一个与 OP 几乎相同的用例,建议的解决方案也对我有用。

为简单起见,我想出了一个适用于我的案例的不同解决方案,而且看起来更简单一些。我之前在模板中应用异步管道作为 *ngIf 结构指令的一部分,并使用变量能够将已经评估的值传递给子组件。

<div *ngIf="answers$ | async as answers">
    <app-dynamic-form [answers]="answers"></app-dynamic-form>
</div>

我遇到了同样的问题,我创建了一个小型库,它提供 ObservableInput 装饰器来帮助解决这个问题:https://www.npmjs.com/package/ngx-observable-input。这种情况的示例代码是:

在线-quote.component.html:

<app-dynamic-form [answers]="answers$ | async"></app-dynamic-form>

动态-form.component.ts:

@Component({
  ...
})
export class DynamicFormComponent implements OnInit {
  @ObservableInput() Input("answers") answers$: Observable<string[]>;

  ...
}

我的方法和建议是使用 BehaviorSubject 进行以下操作 原因 这是来自文档:

One of the variants of Subjects is the BehaviorSubject, which has a notion of "the current value". It stores the latest value emitted to its consumers, and whenever a new Observer subscribes, it will immediately receive the "current value" from the BehaviorSubject.

我认为 OP 声明的子组件总是需要发出的最后一个值,因此我觉得 BehaviorSubject 更适合这个。

在线-quote.component

@Component({
  selector: 'app-online-quote',
  templateUrl: './online-quote.component.html',
  styleUrls: ['./online-quote.component.css'],
  providers:  [DynamicFormService]
})
export class OnlineQuoteComponent implements OnInit {

  public answers$: BehaviorSubject<any>;

  constructor(private service: DynamicFormService) {
       this.answers$ = new BehaviorSubject<any>(null);

   }

  ngOnInit() {
    
    this.service.getAnswers("CAR00PR").subscribe(data => {
            this.answer$.next(data); // this makes sure that last stored value is always emitted;
         });
  }

}

在 html 视图中,

<app-dynamic-form [answers]="answer$.asObservable()"></app-dynamic-form>

// 这会发出 subject 作为 observable

现在您可以订阅子组件中的值了。订阅答案的方式没有变化 动态-form.component.ts

@Component({
  ...
})
export class DynamicFormComponent implements OnInit {
  @Input() answers: Observable<any>;

  ngOnInit() {
    this.answers.subscribe(val => {
      // deal with asynchronous Observable result
      this.form = this.qcs.toFormGroup(this.answers);
    })
}

将可观察对象传递给输入是要避免的事情,原因是:当检测到新输入时,您如何处理对先前可观察对象的订阅?让我们澄清一下,一个 observable 可以被订阅,一旦你有订阅,你有责任完成 observable 或取消订阅。一般来说,这甚至被认为是将可观察对象传递给不是运算符的函数的反模式,因为您正在执行命令式编码,其中应该以声明方式使用可观察对象,将一个传递给组件也不例外。

如果你真的想这样做,你需要非常小心,不要忘记在你取消订阅后取消订阅。为此,您要么必须确保输入永远不会更改,要么专门完成对覆盖输入的任何先前订阅(好吧......就在它被覆盖之前)

如果不这样做,最终可能会出现泄漏,并且很难找到错误。因此,我会推荐以下 2 个备选方案:

  • 要么使用共享存储服务,要么为特定组件“提供”它,请查看 https://datorama.github.io/akita/ 以了解具体方法。在这种情况下,您根本不使用输入,您只会订阅注入的商店服务的查询。当多个组件需要异步写入和读取共享数据源时,这是一个干净的解决方案。

  • 为将在输入更改时发出的组件创建一个可观察对象(BehaviourSubject、ReplaySubject 或 Subject)。
@Component({
  selector: 'myComponent',
  ...
})
export class DynamicFormComponent implements OnInit {

  // The Subject which will emit the input
  public myInput$ = new ReplaySubject();

  // The accessor which will "next" the value to the Subject each time the myInput value changes.
  @Input()
  set myInput(value){
     this.myInput$.next(value);
  }

}

当然要让输入发生变化,您将使用管道异步

<myComponent [myInput]="anObservable | async"></myComponent>