如何将可观察值传递给@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>
我是 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>