Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular rxjs BehaviorSubject.value sets value without next(), not immutable

While I was testing my code, I accidentally ran into unexpected mutation.... Or maybe... I am just doing it all wrong...

User

constructor(
public id: number,
public education: Education[]

){}

UserStateService

private user = new BehaviorSubject<User>(null);

setUser(user:User){
  // set by HttpClient or perform an update
  this.user.next(user);
}

getUserDetail(){
  return this.user.value; 
       // this.user.getValue();  => tried this as well same thing...
}

updateUserDetail(user:User){
  // Maybe perform some check method before the update
  this.user.next(user);
  // HttpClient to save on db
}

I have a form in my component that user will modify their own data. So my idea here is to call getUserDetail() thinking that the return object should be readonly. Once I have set the new value I would then updateUserDetail() to update the observable with next(), but I experienced otherwise...

Component

onSubmit(){

 let currentUser = this.userService.getUserDetail();

 console.log("Original User => ", currentUser);  // array(2) see screen shot highlighted

 currentUser.educations = this.userForm.value['educations'];

 console.log("Modify User => ", currentUser); // array(1)

 // this.userService.updateUserDetail(currentUser); 

}

enter image description here

currentUser.educations = this.userForm.value['educations']; I don't want this to automatically update the observable, because there will be times where I might need to validate information before making the change.... how can I achieve this?

Thank you

like image 558
Eric Huang Avatar asked Jun 26 '18 07:06

Eric Huang


1 Answers

I am not sure if this is the correct way... If you find this solution to be a bad practice please state the problem and also provide the solution. I am happy to change the marked answer.

But this is how I did it.

I basically have another variable to act sort of like a liaison to my subjects. This created numerous benefits such as validating data before .next() observable.

This is what the code looks like

private userSubject = new BehaviorSubject<User>(null);
user$ = this.userSubject.asObservable(); // -> prevent .next() be called on the component 
private user: User = null;

setUser(user:User){
  this.user = user
  // if(user) -> so some check if you desire
  this.userSubject.next({...this.user}); // use spread syntax to make a copy so it does not directly refere to this.user
}

// -> Don't need this any more. Just subscribe to user$
// getUserDetail(){
//  return this.user.value; 
       // this.user.getValue();  => tried this as well same thing...
//}

The following solution was inspired by this article https://codeburst.io/javascript-es6-the-spread-syntax-f5c35525f754

Document link for spread syntax https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

like image 76
Eric Huang Avatar answered Oct 20 '22 01:10

Eric Huang