代码之家  ›  专栏  ›  技术社区  ›  Karan

角度5-保留数据并避免随后的API调用

  •  2
  • Karan  · 技术社区  · 7 年前

    “@angular/core”:“^5.2.0”

    “rxjs”:“^5.5.6”

    我是Angular框架的新手,目前正在使用它创建一个可注入的服务类,这将使WebAPI调用从后端数据库中提取数据。

    我希望持久化或避免来自使用此服务类的组件的后续调用。因为它和一次又一次被调用的数据是一样的。

    我觉得我犯了一个很小的错误,无法抓住它。我想避免第二个电话的往返。 在第一次调用过程中,我希望将数据保存在\dyndata对象中。 但不知怎么的,对于每个服务调用,它都在打印console.log(“fresh call”)语句。

    请建议我在哪里出错。我想使用内置的角度变化检测功能。

    我的服务级别如下:

    @Injectable()
    
    export class DynamicDataService {
    
        private _dyndata: DynamicContent[];
    
        constructor(private _httpObj: HttpClient, private http: Http) {
        }
    
        headers = new HttpHeaders({
            'Content-Type': 'application/json'
        });
    
        getdynamiccontent(): Observable<DynamicContent[]> {
    
            if (this._dyndata) {
                console.log('data exists!')
                return of(this._dyndata);
            }
    
            console.log('Fresh call');
    
            return this._httpObj.get<DynamicContent[]>(environment.apiHost + 'DynamiContentforPage')
                .pipe(tap(data => this._dyndata = data)
                ); 
        }
    

    使用此服务类的组件如下所示:

      import { Component, ElementRef, Input, Renderer, OnInit, Inject, DoCheck } from '@angular/core';
        import { DynamicDataService } from './dynamicdata.service';
        import 'rxjs/add/operator/filter';
        import { DynamicContent } from '../interfaces/IDynamicContent';
    
    @Component({
        selector: 'CMSBlock',
        template: `<div [innerHTML]="_dynmarkup | keepHtml"></div>`,
        providers: [DynamicDataService]
    })
    
    export class DynamicDataComponent {
        public markupcollection: DynamicContent[];
        public savedmarkup: DynamicContent[];
        @Input('key') key: string;
        public _dynmarkup: any = '';
    
        constructor(private dynamicdataService: DynamicDataService) {
            console.log('cstor :: dynamicdata comp');
            this.getCMSData();
        }
    
        public getCMSData() {
            this.dynamicdataService.getdynamiccontent()
                .subscribe(px => {
                    this.setUsersArray(px);
                    console.log(this.markupcollection);
                });
    
        }
    
        setUsersArray(data) {
            this.savedmarkup = data;
            if (this.savedmarkup != undefined) {
                var item = this.savedmarkup.find(fx => fx.Key == this.key);
                this._dynmarkup = item.Text
    
            }
        }
    
    }
    
    3 回复  |  直到 7 年前
        1
  •  1
  •   Mark Hughes    7 年前

    从对问题的注释中,您似乎正在实例化组件的两个实例,它们都是在完成请求之前启动请求的。

    因此,要解决这个问题,您需要重新构造服务处理请求的方式。我建议在服务上使用BehaviorSubject来提供详细信息,如下所示:

    @Injectable()
    export class DynamicDataService {
    
        private _dyndata: BehaviorSubject<DynamicContent[]> = new BehaviourSubject<DynamicContent[]>(null);
        private _requested: boolean = false;
    
        constructor(private _httpObj: HttpClient, private http: Http) {
        }
    
        headers = new HttpHeaders({
            'Content-Type': 'application/json'
        });
    
        getdynamiccontent(): Observable<DynamicContent[]> {
            if (!this._requested) {
                this._requested = true;
                console.log('Fresh call');
                this._httpObj.get<DynamicContent[]>(environment.apiHost + 'DynamiContentforPage')
                .subscribe(data => this._dyndata.next(data));
            } else {
                console.log('Request already made');
            }
            return this._dyndata.asObservable().pipe(filter((e) => e != null), take(1));
        }
    }
    
        2
  •  1
  •   Християн Христов    7 年前

    你遇到了一个非常有趣的案例。

    你解决问题的简短答案是 去除 这个 DynamicDataService 来自组件提供商和 包括它 在更合适的地方,比如 root .

    现在让我们深入研究这个问题,当以角度使用服务时,我们将它们包括在我们想要使用它们的模块/组件的提供者中(在您的案例中, CMSBlock )这样,您就可以访问服务,并可以调用 API 并加载所需的数据。但据我们所知,服务就是这样 一旦提供 可访问大厅应用程序 所以我们可以问自己当我们 使用同一令牌提供多个服务 (姓名)或 同一服务多次 (与以相同名称提供不同服务具有类似效果)。答案是 最后一个赢了 或者换句话说, 最后的 提供的服务将 重写 提供服务的函数 token .

    在您的情况下,当您使用 多次 这个 CMSB 组件,您正在提供/初始化 两次 这个 动态数据服务 也就是说,每次调用服务时, _dyndata 是处于初始状态,在你的情况下是 空的 多亏了这一点,才有了一个“新电话”。

    解决此问题的最佳方法是在 关闭父模块 (如果在延迟加载模块中使用服务)或 如果您正在使用模块 在一个热切的加载 .

    编辑:

    另外,第二次调用可能发生在之前,第一次调用完成后,由于检查可用 γ数据 会失败。

    下面是一个现场演示,演示了我所讨论的内容: CodeSandbox

        3
  •  1
  •   martin    7 年前

    您看到的问题是,您可能有多个组件调用 this.dynamicdataService.getdynamiccontent() 在很短的一段时间内,在其中任何一个HTTP请求完成并将任何值设置为之前同时创建多个HTTP请求 this._dyndata .

    对于RXJS,最好的解决方案是使用 publishReplay(1) take(1) 运营商:

    private cachedRequest = this._httpObj.get(...).pipe(
      shareReplay(1),
      take(1),
    )
    
    getdynamiccontent() {
      return this.cachedRequest;
    }
    

    您甚至不需要使用任何中间变量来存储数据。这个 shareReplay 操作员始终只保留一个订阅 this._httpObj.get(...) 虽然 取(1) 确保在响应就绪时正确完成链。由于 取(1) 缓存在里面的 shareReplay(1) .

    现场演示: https://stackblitz.com/edit/rxjs6-demo-zanygw?file=index.ts

    推荐文章