【问题标题】:Shared service variable is undefined?共享服务变量未定义?
【发布时间】:2018-06-26 10:41:38
【问题描述】:

我有一个具有以下方法的服务:

public GetCaseData(){

const url =  environment.api_url;

const headers = new Headers();
headers.append('Content-Type', 'application/json');
return this._http.get(url, { headers: headers })
    .map(response => {
        return response.json();
    });
}

在我的 app.component.ts 中,我这样称呼它:

  constructor(private _caseService : CaseService) {}

  ngOnInit(): void {

    this._caseService.GetCaseData().subscribe(data => {
      this._caseService.caseData = data;

    });

  }

我想将this._caseService.caseData 分享给我的其他组件HomeContact,但在这些组件的OnInit 中,this._caseService.caseData 是未定义的。 以家为例,

 constructor(private _caseService: CaseService) { }

  ngOnInit() {
    console.log(this._caseService.caseData); //is undefined here because it
                                            // is called before the data comes back
}

我知道为什么(因为组件在数据返回之前已初始化),但我不知道如何解决?

我在家里放了一个吸气剂,我看到了渲染的数据。返回数据后,getter 是否会在生命周期的后期被调用:

  ngOnInit() {
    this.data = this._caseService.caseData;
    console.log('this.data ', this.data) //undefined
  }

  get caseData()
  {
    return this._caseService.caseData; //data rendered on html page
  }

当我执行以下操作时,它说它需要 : 而不是 data. 它还抱怨应该有一个 , ngOnInit 关闭 }

ngOnInit() { this.concatCaseData$ = this._caseService.caseData$.map(data => { 返回 { data.OfficialCaseName} }

}

【问题讨论】:

    标签: angular


    【解决方案1】:

    要添加到 Elseo 的答案,Getter 会解决您的问题,因为它会不断被调用。

    另一种我认为更好的解决方案是使用 BehaviorSubject。因为你可以给它一个初始状态并订阅它。

    例如,您的CaseService

    private caseDataStore = new BehaviorSubject<any>({});
    public caseData$ = this.caseDataStore.asObservable();
    
    public GetCaseData(){
    
    const url =  environment.api_url;
    
    const headers = new Headers();
    headers.append('Content-Type', 'application/json');
    return this._http.get(url, { headers: headers })
        .map(response => {
            return response.json();
        }).toPromise().then(res => {
        caseDataStore.next(res);
    });
    }
    

    在您的应用组件上,您将调用GetCaseData 来触发其余调用并订阅主题。

    public caseData; 
    ngOninit() {
        this._caseService.GetCaseData()
      // you can either subscribe and put it into local state, or you can pipe 
      // async into the template directly. 
        this._caseService.caseData$.subscribe( res => {
        this.caseData = res;
    }) 
    }
    

    最后,在您的其他组件上,您可以订阅。

    this._caseService.caseData$.subscribe( res => {
            this.caseData = res;
        }) 
    

    UPDADE:详细说明异步管道。

    异步管道太棒了!它是一个方便的助手,允许我们订阅 observable 而不必担心取消订阅。在我的代码中减少了很多样板。要在您的示例中使用 async 管道:

    而不是在组件上这样做

     this._caseService.caseData$.subscribe( res => {
            this.caseData = res;
     }) 
    

    你可以在你的 html 中有这个

     <div> {{ _caseService.caseData$ | async }} </div> 
    

    但请注意,每次您使用 async 时,它都会创建一个新订阅。因此以下将创建重复订阅。

     <div> {{ (_caseService.caseData$ | async)?.prop1 }} </div>
     <div> {{ (_caseService.caseData$ | async)?.prop2 }} </div>
     <div> {{ (_caseService.caseData$ | async)?.prop3 }} </div>
    

    要摆脱这种情况,您可以使用

      <div *ngIf="_caseService.caseData$  | async as caseData">
          <div> {{caseData.prop1}} <div>
          <div> {{caseData.prop2}} <div>
          <div> {{caseData.prop3}} <div>
      </div>
    

    UDPATE 2:如果您已经在使用 Angular 5,请使用 httpclient!它会自动将您的 REST 响应格式化为 JSON。不再需要映射它。

    this._http.get(url, { headers: headers }).toPromise().then(res => {
       caseDataStore.next(res);
    });
    

    我在服务中使用了toPromise,因为这是一次性的,并且不想在之后取消订阅。或者,我看到其他人使用.first()


    更新 3:如果您只想选择从组件中获取的属性,您可以从 observable 中mappluck 它。

    假设您已经在联系人组件中使用异步管道。

    public contactCaseData$ = this._caseService.caseData$.map( data => { 
        return {data.prop1, data.prop2}
    }
    

    现在像往常一样订阅。如果你想更进一步!您可以在应用程序中编写地图管道。

    import { Pipe, PipeTransform } from '@angular/core';
    import { Observable } from "rxjs/Observable";
    import "rxjs/add/operator/map";
    
    @Pipe({
      name: 'map'
    })
    export class MapPipe implements PipeTransform {
    
      transform(o$, propsNames): any {
        return o$.map(res => {
          return propsNames.reduce((acc, cur) => {
            acc[cur] = res[cur];
            return acc;
          }, {})
        })
      }
    
    }
    

    演示如何工作:https://stackblitz.com/edit/angular-ftxyns?file=app%2Fmap.pipe.ts

    【讨论】:

    • 谢谢,我有几个问题: 1) this.sidebarStatusStore.AsObservable() 是什么? 2) 在这个例子中 toPromise 做了什么? 3)你能详细说明一下本地状态与异步管道吗?
    • 啊,我复制并粘贴了我正在处理的项目。更新
    • 更新了!异步管道只是一个帮助器,可以让您编写更少的代码。我更喜欢它而不是本地状态,因为我不必编写取消订阅逻辑。 RxJs 非常强大,如果通过链接你的操作符来正确使用它,它通常会将视图层减少到只是一个订阅。
    • 哇,谢谢我让它与当地国家合作......我现在要尝试异步。 BehaviorSubject 和 Subject 有什么区别?
    • 行为主体处于初始状态,每次订阅都会自动获取最后一个值。与主题相反,它只有在订阅已经建立时才会获得价值。
    【解决方案2】:

    您可以在组件中使用 getter

    //You can use in your .html
    //{{caseData}}
    
    get caseData()
    {
        return this._caseService.caseData;
    }
    

    另一个想法是订阅你的服务的 Observable。 注意:最好使用 HttpClient 而不是“旧”http

    【讨论】:

    • 是的,实际上我昨天刚刚升级到 Angular 5,但还没有完成。你能给我一个关于 getter 和订阅的更详细的例子吗?非常感谢。
    • 我很着急,但是使用httpClient,你不需要map json。因此,使用 Angular5,您不需要转换为 Promise,只需使用“do”:return this.httpClient.get(url).do(res=> {caseDataStore.next(res);})
    猜你喜欢
    • 2017-06-08
    • 1970-01-01
    • 1970-01-01
    • 2018-11-16
    • 1970-01-01
    • 2014-07-01
    • 2015-03-13
    • 2022-12-05
    • 2023-03-28
    相关资源
    最近更新 更多