26

I'm new to Angular and I'm having an issue.

I'm creating an app with several sibling components. When I update a value in one component other components don't update. I know that to resolve this issue I should use behaviour subject. But how do I implement it with my service, the components and all the templates?

Here is my code -

----------------------My service---------------------------

//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
        }
        })

    } 

}

----------one component.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();  
  }


}

4 Answers 4

50

I'm going to show you a simple way:

@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);
    }
}

Now when you update something anywhere in the application, you can set that change by the ProfileService and each subscriber is receiving the change. I recommend you subscribe in ngOnInit.

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

Never forget to unsubscribe from the observables to prevent memory leaks!

There are many ways u can do that --> Use a subscription and unsubscribe in ngOnDestroy() or use another subject and deliver it to takeUntil like this:

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();
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks foryour explanation. I'll close this subject
Why you have to convert the response to asObservable()? is it not inherit?
This is a convenient answer to your question: stackoverflow.com/questions/36986548/…
5

First create a BehaviourSubject

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

Define a function to "update" the BehaviourSubject

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

Now subscribe in your components to the source

this.service.source.subscribe();

Note the behaviourSubject always needs an initial value and emits the last one

DOCS: https://www.learnrxjs.io/subjects/behaviorsubject.html

If you want to share the data from a httpRequest you should use shareReplay() operator instead, you can subscribe to the httpRequest from different components and the request will be made once and the data will be shared

DOCS: https://www.learnrxjs.io/operators/multicasting/sharereplay.html

Comments

1

There are a few ways to do that. One of them is described here.

1) Build your service like this:

// 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) And in your components:

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

Well there are other approaches you can think of, but I think this is the simpler one given the sample code you pasted on your question.

As a side note: if you do some search on stackoverflow, you'll see that this question (or similar questions) has been asked many times here. Take a look, for example in this other approach: Multiple subscriptions without recalculate common part

2 Comments

Thanks for your explanation on Replay subject! But I used Behavior Subject instead of it.
@kkD97 , it's the same logic as with BehaviorSubject
0

Here's how to solve your problem using a behavior subject:

@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
          }
        }
      );
    });
  }

}

Now when you want to post data and subscribe to the result of your Behavior Subject from, say to the in the component you have here, you simply:

 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
      });
} 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.