首页 文章

使用Angular 2中的Observables获取数据一次

提问于
浏览
7

我有一个服务,很多我的Angular 2组件使用过多次 . 它从Web API获取客户数据并返回Observable:

getCustomers() {
   return this.http
        .get(this.baseURI + this.url)
        .map((r: Response) => {
            let a = r.json() as Customer[];                       
            return a;                         
        });               
}

我在我的根组件中注入了这个服务,在每个想要访问客户的组件中,我只订阅了Observable:

this.customerService.getCustomers().subscribe(v => this.items = v);

但是,订阅我的Observable的每个组件都会导致另一个HTTP请求的执行 . 但是只获取一次数据就足够了 . 如果我尝试share(),它不能解决我的问题:

getCustomers() {
   return this.http
        .get(this.baseURI + this.url)
        .map((r: Response) => {
            let a = r.json() as Customer[];                       
            return a;                         
        }).share();               
}

仍然是同一个问题 . 操作员必须使用的任何建议只能获取一次数据?

4 回答

  • 10

    1)您只需将下载的数据保存在您的服务中:

    export class CustomersService {
      protected _customers: Array<Customer>;
    
      constructor(public http: Http) {}
    
      public getCustomers(): Observable<Array<Customer>> {
        return new Observable(observer => {
          if (this._customers) {
            observer.next(this._customers);
            return observer.complete();
          }
          this.http
            .get(this.baseURI + this.url)
            .map((r: Response) => (r.json() as Array<Customer>))
            .subscribe((customers: Array<Customer>) => {
              this._customers = customers;
              observer.next(this.customers);
              observer.complete();
            });
        });
      }
    }
    

    2)更短的方法采取 refresh 参数:

    export class CustomersService {
      protected _customers: Array<Customer>;
    
      constructor(public http: Http) {}
    
      public getCustomers(refresh?: boolean): Observable<Array<Customer>> {
        if (!refresh && this._customers) {
          return Observable.of(this._customers);
        }
        return this.http
                .get(this.baseURI + this.url)
                .map((c: Response) => (c.json() as Array<Customer>))
                .do((customers: Array<Customer>) => {
                    this._customers = customers;
                });
        });
      }
    }
    

    3)利用 ReplaySubject

    export class CustomersService {
      protected _customers$: ReplaySubject<Array<Customer>> = new ReplaySubject(1);
      protected _customersInitialized: boolean;
    
      constructor(public http: Http) {}
    
      public getCustomers(refresh?: boolean): Observable<Array<Customer>> {
        if (refresh || !this._customersInitialized) {
          this._customersInitialized = true;
          return this.http
            .get(this.baseURI + this.url)
            .map((c: Response) => (c.json() as Array<Customer>))
            .subscribe((customers: Array<Customer>) => {
              this._customers$.next(customers);
            });
        }
        return this._customers$.asObservable().skip(+refresh).distinctUntilChanged();
      }
    }
    

    然后:

    this.customersService.getCustomers()
        .subscribe(customers => this.customers = customers);
    

    您还可以从 SomeService 公开始终最新的 customers 字段,仅供以下方式使用(如在模板中显示):

    public get customers(): ReadonlyArray<Customer> {
      return this._customers;
    }
    
  • 1

    如果您希望多个子节点订阅相同的observable,但只有在您执行以下操作后才执行observable .

    请注意,这确实遵循了observable的设计,因为我们正在执行服务层中的observable(Observable.fromPromis(stream.toPromise()),当应该从订阅组件执行时 . 查看https://www.bennadel.com/blog/3184-creating-leaky-abstractions-with-rxjs-in-angular-2-1-1.htm以获取更多信息 .

    //declare observable to listen to
      private dataObservable: Observable<any>;
    
      getData(slug: string): Observable<any> {
    
        //If observable does not exist/is not running create a new one
        if (!this.dataObservable) {
    
            let stream = this.http.get(slug + "/api/Endpoint")
                .map(this.extractData)
                .finally(() => {
                    //Clear the observable now that it has been listened to
                    this.staffDataObservable = null;
                });
    
            //Executes the http request immediately
            this.dataObservable = Observable.fromPromise(stream.toPromise());
    
        }        
    
        return this.staffDataObservable;
     }
    
  • 1

    我将创建一个父容器,获取一次数据,并使用@Input将其传递给子组件 .

    家长:

    @Component({
        selector: 'BarFooHttpCaller',
        template: ´<child *ngIf="data.length > 0" [data]></child>´
    })
    
    class BarFooHttpCaller {
        private data: any;
        constructor(private foobar:Foobar) {
            this.data = {};
        }
    
        ngOnInit() {
            this.foobar.getCustomers().subscribe(() => {       
                console.log('httpdone') 
            });
            this.foobar.dataStream.subscribe((data) => {
                console.log('new data', data);
                this.data = data;
            })
        }
    }
    

    儿童:

    import { Component, Input } from '@angular/core';
    
    @Component({
        selector: 'child',
        template: ´<div>{{data}}</div>´
    })
    
    export class Child {
        @Input() data: any;
    
    }
    
  • 2

    共享运算符可以使用多个观察者使用相同的流结果 . 它可能很好,但是每次调用 getCustomers() 时都会生成一个新的可观察流,因为您没有多次订阅此流,所以没有必要调用 share() .

    如果您想与多个观察者共享数据但只进行一次http调用,您只需要创建第二个流,由http包含数据 . 之后,您的所有组件都可以订阅它 .

    代码可能是这样的

    @Injectable()
    class FooBar {
    
        public dataStream:Subject<any> = new Subject();
    
        constructor(private http:Http) {}
    
        public getCustomers() {
            return this.http
            .get(this.baseURI + this.url)
            .map((response:Response) => response.json())
            .map((data) => {
                this.dataStream.next(data); 
                return data;
            })
        }
    
    }
    
    
    @Component({})
    class BarFooHttpCaller {
        constructor(private foobar:Foobar) {}
    
        ngOnInit() {
            this.foobar.getCustomers().subscribe(() => { console.log('http done') });
            this.foobar.dataStream.subscribe((data) => {
                console.log('new data', data);
            })
        }
    }
    
    @Component({})
    class OtherBarFoo {
        constructor(private foobar:Foobar) {}
    
        ngOnInit() {
            this.foobar.dataStream.subscribe((data) => {
                console.log('new data', data);
            })
        }
    }
    

相关问题