我有一个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;
}
为了加载Dropdown,我必须像它那样传递observable
getRoles(): void {
this.roleDepartments = this.userService
.getRoles();
}
<select2 class="form-control" [data]="roleDepartments | async" (valueChanged)="changed($event)" [width]="300"></select2>
这可以按预期工作,并在下载时使用数据填充下拉列表。我现在希望能够捕获在更改时选择的值以及支持的值。问题是我需要迭代&#34; roleDepartments&#34;并获取所选值的整个对象。由于对象是Observable,我不能迭代它。我已经尝试订阅它,将结果分配给变量并迭代它。当我尝试这个时,再次调用填充Observable的Api。我是不是最好的方式呢?如果没有,我该怎么办?
答案 0 :(得分:1)
首先,再次订阅observable的原因是它被称为“冷可观察”。这意味着每个新订阅者都将获得observable的新副本,并启动任何创建该observable的工作。在Http
的情况下,它会发出新请求。
因此,我们需要一种方法将其从“冷”可观察对象转换为在我们订阅时不会重新提交请求的方法,而是立即返回最近的结果。我们可以通过添加.publishReplay(1)
运算符后跟.refCount()
运算符来执行此操作,以便第一个订阅者启动请求,并且最后一个取消订阅者取消订阅相关的observable。
这样,在change()
方法中,您可以执行以下操作:
this.roleDepartments.take(1).subscribe(roles => {
let option: Select2OptionData;
roles.forEach(role => {
if (role.id === selectedId) {
option = role;
}
});
});
这将只获得重放值(原始observable发出的最新值),然后通过.take(1)
取消订阅。
我认为,这种方法不易发生泄漏,因为没有机会让您忘记取消订阅的悬挂订阅。这是因为使用async
管道是安全的 - Angular负责正确订阅和取消订阅,change()
方法中的其他订阅仅在初始加载数据后发生,然后立即自动取消订阅
一个更简单的解决方案(更容易搞砸并导致泄漏)是让你的组件在ngOnInit()
钩子中订阅一次observable,存储角色数组以及订阅对象,以及然后在ngOnDestroy()
钩子中取消订阅。最后,在模板中,您根本不需要使用async
管道。
更容易导致泄漏,因为如果您(或未来的开发人员)忘记取消订阅,您可能会通过阻止清理控制器实例来引入内存泄漏。
但如果您愿意承担这种风险,那就是它的样子:
@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>