如何将可观察值传递给@Input() Angular 4
Posted
技术标签:
【中文标题】如何将可观察值传递给@Input() Angular 4【英文标题】:How to pass observable value to @Input() Angular 4 【发布时间】:2018-09-14 16:25:47 【问题描述】:我是 Angular 新手,我有以下情况,即我有一个服务 getAnswers():Observable<AnswerBase<any>[]>
和两个相互关联的组件。
online-quote 组件在其ngOnInit()
方法中调用服务getAnswers():Observable<AnswerBase<any>[]>
,并将其结果传递给组件dynamic-form。
为了说明情况,这是我的两个组件的代码:
在线报价.component.html:
<div>
<app-dynamic-form [answers]="(answers$ | async)"></app-dynamic-form>
</div>
在线报价.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");
动态表单.component.html:
<div *ngFor="let answer of answers">
<app-question *ngIf="actualPage===1" [answer]="answer"></app-question>
</div>
动态表单.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>[]>
的结果信息是可观察的,那么将信息从 online-quote 传递到 dynamic-form 的正确方法是什么.
我已经尝试了很多方法,但它不起作用。我希望有人能帮我解决这个问题。非常感谢!
【问题讨论】:
【参考方案1】:假设DynamicFormService.getAnswers('CAR00PR')
是异步(可能是),使用async Pipe
传递异步结果是正确的方法,但是当@987654323时你不能指望现在得到异步结果@ 被创建(在 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
并订阅它以收听它的结果。
在线报价.component.html:
<app-dynamic-form [answers]="answers$"></app-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);
)
【讨论】:
我真的很喜欢你介绍了两种处理这种情况的可行方法。值得一提的是,虽然不是基于示例的问题,但“订阅”是在组件代码中处理的,没有取消订阅机制。同样,这里不是问题(除了不是问题的重点),我们知道原始可观察源来自 http 调用,该调用在调用后完成。对于其他 observable,这很快就会成为有问题的内存泄漏。【参考方案2】:我有一个与 OP 几乎相同的用例,建议的解决方案也对我有用。
为了简单起见,我想出了一个适用于我的案例的不同解决方案,并且看起来更简单一些。我之前在模板中应用了异步管道作为 *ngIf 结构指令的一部分,并使用变量能够将已经评估的值传递给子组件。
<div *ngIf="answers$ | async as answers">
<app-dynamic-form [answers]="answers"></app-dynamic-form>
</div>
【讨论】:
看起来不错,但是如何在 app-dynamic-form 中处理它? 在 app-dynamic-form 内部,它不再是 Observable,而是一个 AnswerBase[]。我相信 OP 不必更改其中的任何代码。 如果他只是处理原始数据对象,他可能不需要做任何进一步的事情,这是一个可行的解决方案。但是,他正在获取答案数据并将其转换为表单组,因此接受的答案中提到的 ngOnChanges 将很好地补充此解决方案。 作为一个魅力,你可以在这里看到一个实现:angular-ivy-z9shje.stackblitz.io【参考方案3】:我遇到了同样的问题,我创建了一个小型库,提供 ObservableInput
装饰器来帮助解决这个问题:https://www.npmjs.com/package/ngx-observable-input。该案例的示例代码为:
在线报价.component.html:
<app-dynamic-form [answers]="answers$ | async"></app-dynamic-form>
动态表单.component.ts:
@Component(
...
)
export class DynamicFormComponent implements OnInit
@ObservableInput() Input("answers") answers$: Observable<string[]>;
...
【讨论】:
【参考方案4】:我的方法和建议是使用 BehaviorSubject 进行以下操作 原因 这是来自文档:
Subjects 的变体之一是 BehaviorSubject,它有一个 “当前值”的概念。它存储发出的最新值 它的消费者,并且每当有新的观察者订阅时,它将 立即从 BehaviorSubject 接收“当前值”。
我认为 OP 声明的子组件总是需要发出的最后一个值,因此我觉得 BehaviorSubject 会更适合这个。
在线报价.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>
// 这会发出可观察的主题
现在您可以订阅子组件中的值。订阅答案的方式没有变化 动态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);
)
【讨论】:
【参考方案5】:将可观察对象传递给输入是要避免的,原因是:当检测到新输入时,您如何处理对前一个可观察对象的订阅?让我们澄清一下,可以订阅 observable,一旦您订阅,您有责任完成 observable 或取消订阅。一般来说,这甚至被认为是一种反模式,将 observables 传递给不是运算符的函数,因为您正在执行命令式编码,其中 observables 应该以声明方式使用,将一个传递给组件也不例外。
如果你真的想这样做,你需要非常小心,不要忘记取消订阅 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>
【讨论】:
以上是关于如何将可观察值传递给@Input() Angular 4的主要内容,如果未能解决你的问题,请参考以下文章
将可写 StringBuilder 数组从 C# 传递给 C++