如何在 Angular 8 中使用服务实现行为主体

Posted

技术标签:

【中文标题】如何在 Angular 8 中使用服务实现行为主体【英文标题】:How to implement behavior subject using service in Angular 8 【发布时间】:2019-08-05 08:48:33 【问题描述】:

我是 Angular 新手,但遇到了问题。

我正在创建一个包含多个同级组件的应用。当我更新一个组件中的值时,其他组件不会更新。我知道要解决这个问题,我应该使用 behaviour subject。但是如何使用我的服务、组件和所有模板来实现它?

这是我的代码 -

------------------------我的服务---------------- ------------

//import


@Injectable() 
export class CoachService 

    apiURL = environment.apiURL;

    constructor(private http: HttpClient )  

    coachProfile(token :string)
      
    return this.http.post<any>(this.apiURL+'/coach/profile_infos',
      token: token
      )        
    

    updateProfile(info: any, token: string, us_id: string) 
      return this.http.post<any[]>(this.apiURL + '/coach/update_profile', 
        token: token,
        us_id: us_id,
        us_lang: info.us_lang,
        us_firstname: info.us_firstname,
        us_lastname: info.us_lastname,
        us_sex: info.us_sex,
        us_birthdate: info.us_birthdate,
        us_national_number : info.us_national_number,
        us_email: info.us_email,
        us_gsm: info.us_gsm,        
        online_profile: info.online_profile,          
        us_address: info.us_address,
        us_zip: info.us_zip,
        us_city: info.us_city,
        country:
          id: info.country.id
        
        )

     


---------一个组件.ts--------

//import
//component decorator

export class CoordonneesComponent implements OnInit, OnDestroy 

private coachProfile;
  token: string = localStorage.getItem('token');
  us_id : string;
  us_lang: string; 
  infos_profile: any;
  online: any;


  constructor(private translate: TranslateService,private coachService: CoachService, private router: Router)  

  ngOnInit() 

    this.coachProfile=this.coachService.coachProfile(this.token)
      .subscribe((data) => 
        this.infos_profile = data.results;
        this.online = this.infos_profile.online_profile;
        this.translate.use(this.infos_profile.us_lang)
        this.infos_profile.lang= this.infos_profile.us_lang;

      );

   .....
  


updateCoordonees() 
  this.coachService.updateProfile(this.infos_profile, this.token, this.us_id)
    .subscribe((data: any) => 

      if(data.success && data.msg!=null)
       
  // do something
      
      else
      
       // do something
      

    ,
      (err) => 
        // do something
      );

  



  ngOnDestroy() 
    this.countrieList.unsubscribe();
    this.coachProfile.unsubscribe();  
  




【问题讨论】:

【参考方案1】:

我将向你展示一个简单的方法:

@Injectable() 
export class ProfileService 

    private profileObs$: BehaviorSubject<Profile> = new BehaviorSubject(null);

    getProfileObs(): Observable<Profile> 
        return this.profileObs$.asObservable();
    

    setProfileObs(profile: Profile) 
        this.profileObs$.next(profile);
    

现在,当您在应用程序的任何位置更新某些内容时,您可以通过 ProfileService 设置该更改,并且每个订阅者都会收到更改。我建议你订阅ngOnInit

ngOnInit() 
  this.profileService.getProfileObs().subscribe(profile => this.profile = profile);

永远不要忘记取消订阅 observables 以防止内存泄漏!

您可以通过多种方式做到这一点 --> 在ngOnDestroy() 中使用订阅和取消订阅,或者使用其他主题并将其交付给 takeUntil,如下所示:

unsubscribe$: Subject<boolean> = new Subject();

...

ngOnInit()     
  this.profileService.getProfileObs()
                     .pipe(takeUntil(this.unsubscribe$))
                     .subscribe(profile => this.profile = profile);


ngOnDestroy() 
  this.unsubscribe$.next(true);
  this.unsubscribe$.complete();

【讨论】:

感谢您的解释。我会关闭这个主题 为什么必须将响应转换为 asObservable()?不是继承吗? 这是对您问题的方便回答:***.com/questions/36986548/…【参考方案2】:

首先创建一个 BehaviourSubject

this._source = new BehaviourSubject<yourType>(initialValue);
this.source = this._source.asObservable();

定义一个函数来“更新” BehaviourSubject

updateSource(newValue) 
    this._source.next(newValue)

现在在您的组件中订阅源代码

this.service.source.subscribe();

注意 behaviorSubject 总是需要一个初始值并发出最后一个

文档:https://www.learnrxjs.io/subjects/behaviorsubject.html

如果你想从 httpRequest 共享数据,你应该使用 shareReplay() 操作符,你可以从不同的组件订阅 httpRequest,请求将被发出一次,数据将被共享

文档:https://www.learnrxjs.io/operators/multicasting/sharereplay.html

【讨论】:

【参考方案3】:

有几种方法可以做到这一点。此处描述了其中之一。

1) 像这样构建您的服务:

// ReplaySubject is more flexible than BehaviorSubject, as it
// allows you to define how many past emissions should be available.
// But you can get an equivalent code with BehaviorSubject by
// coding like this:
// private _coachProfile$: BehaviorSubject<any | null> = 
//    new BehaviorSubject<any | null>(null);
private _coachProfile$: ReplaySubject<any> = new ReplaySubject<any>(1);

coachProfile(token :string)
  
  return this.http.post<any>(this.apiURL+'/coach/profile_infos',
    token: token,
  ).subscribe((profile) => this._coachProfile$.next(profile));        


subscribeToGetCoachProfile$()
  
  return this._coachProfile$.asObservable();       

2) 在你的组件中:

ngOnInit() 
  this.coachService.subscribeToGetCoachProfile$()
    .subscribe((profile) => this.coachProfile = profile);

您还可以想到其他方法,但鉴于您粘贴在问题上的示例代码,我认为这是更简单的方法。

附带说明:如果您在 *** 上进行一些搜索,您会发现此问题(或类似问题)已在此处被多次询问。看一下,例如在另一种方法中:Multiple subscriptions without recalculate common part

【讨论】:

感谢您对重播主题的解释!但我用的是行为主题而不是它。 @kkD97 和 BehaviorSubject 的逻辑是一样的【参考方案4】:

以下是使用行为主题解决问题的方法:

@Injectable()
export class CoachService 
  apiURL = environment.apiURL;

  constructor(private http: HttpClient)  

  updateProfile(info, token, us_id): Observable<any> 
    return Observable.create((behaviorSubject: BehaviorSubject<any>) => 
      const requestData = 
        token: token,
        us_id: us_id,
        us_lang: info.us_lang,
        us_firstname: info.us_firstname,
        us_lastname: info.us_lastname,
        us_sex: info.us_sex,
        us_birthdate: info.us_birthdate,
        us_national_number: info.us_national_number,
        us_email: info.us_email,
        us_gsm: info.us_gsm,
        online_profile: info.online_profile,
        us_address: info.us_address,
        us_zip: info.us_zip,
        us_city: info.us_city,
        country: 
          id: info.country.id
        
      ;
      const url = [this.apiURL, '/coach/update_profile'].join('');

      return this.http.post(url, requestData).subscribe(
        data => 
          behaviorSubject.next(data);
        ,
        err => 
          behaviorSubject.error(err);
          if (err && err.status === 401) 
            // Do some err handling
          
        
      );
    );
  


现在,当您想要发布数据并订阅您的 Behavior Subject 的结果时,比如说您在此处拥有的组件,您只需:

 updateCoordonees() 
  this.coachService.updateProfile(this.infos_profile, this.token, this.us_id)
    .subscribe((data: any) => 

      if (data.success && data.msg != null) 
        // do something on success
      

    ,
      (err) => 
        // do some err handling
      );
 

【讨论】:

以上是关于如何在 Angular 8 中使用服务实现行为主体的主要内容,如果未能解决你的问题,请参考以下文章

在 Angular 8 中,如何从浏览器控制台访问注入的服务?

下面的代码在 Angular 8 中运行。但我需要在 Angular 6 中执行它。我该如何实现呢?

如何使用带有xml的angular+8的soap asmx服务

如何检查数据是不是已完成加载以使用异步管道 Angular 8

如何使用 Keycloak 保护 Angular 8 前端和使用网关、eureka 的 Java Spring Cloud 微服务后端

如何解决 NullInjectorError: No provider for HttpClient! Ionic 4(Angular 8)中的问题