我有一个 Api,它返回一个对象数组并将其转换为 ng2-select2 的对象像这样
getRoles(): Observable<Array<Select2OptionData>> {
return this.authHttp.get(`${this.usersUrl}/GetRoles`)
.map(function (response) {
var result = response.json();
var temp = [];
result.forEach(function (dept) {
var d = {
'id': '0',
'text': dept.name,
'children': []
};
dept.roles.forEach(function (role) {
var r = {
'id': role.id.toString(),
'text': role.name
};
d.children.push(r);
}, this);
temp.push(d);
}, this);
return temp;
})
.catch(this.handleError);
}
返回的是一个包含子项的 Select2OptionData 数组
export interface Select2OptionData {
id: string;
text: string;
children?: Array<Select2OptionData>;
additional?: any;
}
为了加载下拉列表,我必须像这样将可观察对象传递给它
getRoles(): void {
this.roleDepartments = this.userService
.getRoles();
}
<select2 class="form-control" [data]="roleDepartments | async" (valueChanged)="changed($event)" [width]="300"></select2>
这按预期工作,下拉列表在加载时填充了数据。我现在希望能够捕获在更改时选择并受支持的值。问题是我需要遍历“roleDepartments”的值并获取所选值的整个对象。由于该对象是 Observable 对象,因此我无法对其进行迭代。我试过订阅它,将结果分配给一个变量并对其进行迭代。当我尝试这样做时,再次调用填充 Observable 的 Api。我打算以最好的方式做到这一点吗?如果没有,我应该做什么?
最佳答案
首先,再次订阅 observable 的原因是它是所谓的“冷 observable”。这意味着每个新订阅者都将获得一份新的可观察对象副本,并开始为创建该可观察对象所做的任何工作。在 Http
的情况下,它会发出一个新的请求。
因此,我们需要一种方法将其从“冷”可观察对象转变为在我们订阅时不会重新提交请求,而是立即返回最新结果的对象。我们可以通过添加 .publishReplay(1)
运算符,然后添加 .refCount()
运算符来做到这一点,以便第一个订阅者启动请求,最后一个取消订阅者取消订阅相关的可观察对象。
这样,在change()
方法中,您可以:
this.roleDepartments.take(1).subscribe(roles => {
let option: Select2OptionData;
roles.forEach(role => {
if (role.id === selectedId) {
option = role;
}
});
});
这将仅获取重播值(原始可观察对象发出的最新值),然后通过 .take(1)
取消订阅。
我相信,这种方法不太容易泄漏,因为没有机会留下您忘记取消订阅的 observable 的悬空订阅。这是因为使用 async
管道是安全的——Angular 负责为您正确订阅和取消订阅,而 change()
方法中的另一个订阅仅在初始订阅之后发生加载数据,然后立即自动取消订阅。
更简单的方法
一个更简单的解决方案(更容易搞砸并导致泄漏)是让您的组件在 ngOnInit()
Hook 中订阅一次可观察对象,存储 Angular 色数组以及订阅对象,然后在 ngOnDestroy()
Hook 中取消订阅。最后,在模板中,您根本不需要使用 async
管道。
更容易导致泄漏,因为如果您(或 future 的开发人员)忘记取消订阅,您将面临通过阻止清理 Controller 实例而引入内存泄漏的风险。
但是如果您愿意承担这个风险,那么它应该是这样的:
@Component({
// ...
})
export class MyComponent implements OnInit, OnDestroy {
private rolesSubscription: Subscription;
private roleDepartments: Select2OptionData[];
constructor(private userService: UserService) { }
ngOnInit(): void {
this.rolesSubscription = this.userService.getRoles()
.subscribe(roles => this.roleDepartments = roles);
}
ngOnDestroy(): void {
this.rolesSubscription.unsubscribe();
}
change(selectedId: string) {
// find correct role from this.roles and do something with it
}
}
然后在您的模板中,将其稍微更改为:
<select2 class="form-control" [data]="roleDepartments"
(valueChanged)="changed($event)" [width]="300">
</select2>
关于angular - 订阅 Created Observable 再次调用 Api,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41495729/