angular - 订阅 Created Observable 再次调用 Api

标签 angular angular2-observables

我有一个 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/

相关文章:

javascript - 从 rxjs 存储中的状态获取最新的 2 个值

angular - undefined object 数组的 ionic 存储

rxjs5 - Observable.prototype.concatAll 似乎没有产生预期的结果

angular - 在 Angular 中路由之间共享数据时如何避免 Observable 的痛苦?

javascript - 从给定的长日期转换为适当的日期格式

Angular2 可观察定时器条件

Angular 2 : Using HTTP requests with Observables to search a database

angular - 使用 HTTP 请求测试 Angular 2.0.0 组件

html - Angular 路由器链接到页面中具有指定 id 的元素

Angular:如何导入所有其他模块都可以访问的全局模块。 (无需重新导入其他模块)