简体   繁体   中英

Angular - async/await Observable toPromise update broadcast new BehaviorSubject data return Promise

I have a project with this code that I inherited and for the life of me, I cannot understand what exactly is going on and there are some bugs with the teacher profile so I'm trying to sort out what the developer may have been trying to do. If you can please help review and provide any tips or issues that you see. I would like to make this code follow best practices and hope to document for the next guys how to do it.

So my questions are:

  1. Does it make sense to create a new BehaviorSubject upon each call to the getTeacherProfile(), getTeacherProfileInfo(), getTeacherProfileByProfileId() because doesn't that create a new variable and any previous subscribe won't be notified?

  2. Would the correct best pattern be to initialize once in constructor like

this.broadCast = new BehaviorSubject<TeacherProfileModel>();

and then in inside each method that wants to update, push, send any new data to call:

this.broadCast.next(data);
  1. Instead of a subscribe inside the getter methods of Promise.then(), isn't it standard practice to make all the.subscribe((data) => { from inside the ngOnInit() one time for the component life cycle?

  2. Also there is no ngOnDestroy() or unsubscribe, which I believe is standard practice when subscribing to any BehaviorSubject because if you do not it can create memory leaks and unexpected results?

  3. Could it be caused by the order in which the methods are called by the related code that is making an inconsistent user experience from the dependent code that is subscribing to BehaviorSubjects which may be changed/reassigned and forgotten causing unexpected and unpredictable results?

Thanks for your help in advance!

CODE EXCERPTS

teacher.service.ts

  async getTeacherProfile(): Promise<TeacherProfileModel> {
    if (this.teacherProfileId > 0) {
      var data = await this.crudService.get<TeacherProfileModel>("/tProfile/GetTeacherProfileData",
        new HttpParams().set('teacherProfileId', this.teacherProfileId.toString())).toPromise();
      this.broadCast = new BehaviorSubject<TeacherProfileModel>(data);
      this.teacherProfile = data;
      this.personalizedURL = this.teacherProfile.PersonalizedUrl;
    }
    else {
      if (this.teacherProfileId === 0) {
        this.initialiseTeacherProfile();
        this.commonService.isDefaultImageFlag = true;
      }
      this.broadCast = new BehaviorSubject<TeacherProfileModel>(this.teacherProfileData);
    }

    return this.teacherProfile;
}

async getTeacherProfileInfo(url: string): Promise<TeacherProfileModel> {
    var data = await this.crudService.get<TeacherProfileModel>("/teacherProfile/GetTeacherProfileInfo",
      new HttpParams().set('personlizedUrl', url)).toPromise();
    this.broadCast = new BehaviorSubject<TeacherProfileModel>(data);
    this.teacherProfile = data;
    this.personalizedURL = this.teacherProfile.PersonalizedUrl;
    return this.teacherProfile;
} 

async getTeacherProfileByProfileId(teacherProfileId:any): Promise<TeacherProfileModel> {
    this.teacherProfileId = teacherProfileId;
    this.imageUrl = localStorage.getItem("userImageUrl");
  
    var data = await this.crudService.get<TeacherProfileModel>("/teacherProfile/GetTeacherProfileData",
      new HttpParams().set('teacherProfileId', this.teacherProfileId.toString())).toPromise();
    this.broadCast = new BehaviorSubject<TeacherProfileModel>(data);
    this.teacherProfile = data;
    this.personalizedURL = this.teacherProfile.PersonalizedUrl;
    return this.teacherProfile;
}

profile.component.ts

ngOnInit() {
    this.teacherService.getTeacherProfile().then((response: any) => {
         this.teacherService.broadCast.subscribe(data => {
              if(data !== undefined) { 
                   //do something  
              }
         });
    });
}

teacher-profile-edit.component.ts


  previewChanges() {
    this.commonService.userProfilePic = localStorage.getItem('userImageUrl');
    this.commonService.isStudentUser = false;
    this.teacherService.broadCast.next(this.teacherProfile);
  }


  private getTeacherProfileData() {
    this.teacherService.getTeacherProfile().then((response: any) => {
      this.teacherService.broadCast.subscribe(data => {
        if(data !== undefined) {
          this.teacherProfile = data;
          this.onProfileStatusChange(this.teacherProfile.IsProfileStatusPrivate);
          this.shortIntroLength = this.introLength - data.ShortIntroduction.length;
          if (data.TeacherQualifications.length === 0) {
            this.addQualification();
          }
        } else {
          var err = 'Error'; this.toastr.error('Oops! Something went wrong! '+err); throw err;
        }
      });
    });
  }

  1. First of all you don't need to create new Behavior Subject every time you can just pass null with.next(null) method and also where you have subscribed this you can put condition that response from Behavior Subject should not null.

  2. Defiantly that's the right method of BehaviorSubject use. even you can checkout official site https://rxjs-dev.firebaseapp.com/api/index/class/BehaviorSubject

  3. I didn't get this but i suggest you to go for Subscription and put all your subscriptions into Subscription and on component destroy hook make all unsubscribe. https://medium.com/thecodecampus-knowledge/the-easiest-way-to-unsubscribe-from-observables-in-angular-5abde80a5ae3

  4. go through Ans 3. Yes, if you don't put that its just working in background if its not destroyed.

Well, whatever you said is correct,

The reason the I assume he was re-initializing was due to the following code:

this.teacherService.getTeacherProfile().then((response: any) => {
     this.teacherService.broadCast.subscribe(data => {
          if(data !== undefined) { 
               //do something  
          }
     });
});

getTeacherProfile() returns the Promise , and it then subscribes to the BehaviorSubect , essentially subscribing to the new BehaviorSubject . Of course, shouldn't be done this way.

Assuming that crudService.get() is simply a Http call, you could simply return the Observable , and any operation you need can be done by (leveraging rxjs) piping it and tapping into it, like caching the response.

teacherProfileModelResponseCache;
getTeacherProfile(): Observable<TeacherProfileModel> {
    if (this.teacherProfileId > 0) {
        return this.crudService.get<TeacherProfileModel>("/tProfile/GetTeacherProfileData",
    new HttpParams().set('teacherProfileId', this.teacherProfileId.toString()))
        .pipe(
            tap(x=>{
                this.teacherProfileModelResponseCache = x;
                this.personalizedURL = x.PersonalizedUrl;
            })
        )
    }
    else {
        if (this.teacherProfileId === 0) {
            this.initialiseTeacherProfile();
            this.commonService.isDefaultImageFlag = true;
        }
    }

    return of(teacherProfileModelResponseCache);
}

And on your teacher-profile-edit.component.ts, you would simply subscribe to it and do what is required.

private getTeacherProfileData() {
    this.teacherService.getTeacherProfile().subscribe(data => {
        if(data !== undefined) {
            this.teacherProfile = data;
            this.onProfileStatusChange(this.teacherProfile.IsProfileStatusPrivate);
            this.shortIntroLength = this.introLength - data.ShortIntroduction.length;
            if (data.TeacherQualifications.length === 0) {
                this.addQualification();
            }
        } else {
            var err = 'Error'; this.toastr.error('Oops! Something went wrong! '+err); throw err;
        }
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM