update Angular component with BehaviorSubject or Subject don't work correctly

834 Views Asked by At

everybody, I have an angular 14 app and I have a service called UserService and a component called UserListComponent and in the service i create a function to get data from API and send it to Component with BehaviorSubject.

This is user.service.ts

// userChanged = new BehaviorSubject<UserDto[]>([]);

userChanged = new BehaviorSubject<any>(null);

constructor(private httpClient: HttpClient) {
}

getAll(): void {
  this.httpClient.get<UserDto[]>(`${this.BASEURL}/user`).subscribe({
    next: (res: UserDto[]) => {
      console.log('next data');
      console.log(res);
      this.userChanged.next(res);
    },
    error: err => console.log(err)
  });
}

`

this is user-list.component.ts

export class UserListComponent implements OnInit, OnDestroy {
  users: UserDto[] = [];
  loading: boolean = false;
  userSubscription: Subscription;

  constructor(private userService: UserService) {
  }

  ngOnInit(): void {
    this.getAllUsers();
  }

  loadData() {
    this.userService.getAll();
  }

  getAllUsers() {
    this.loading = true;
    this.userSubscription = this.userService.userChanged.subscribe({
      next: res => {
        console.log('users received----------------------------------------------');
        console.log(res);
        this.users = res;
        console.log(this.users);
        this.loading = !res;
      },
      error: err => console.error(err),
      complete: () => console.log('complete')
    });
  }

  ngOnDestroy(): void {
    this.userSubscription.unsubscribe();
  }

}

this is user-list.component.html

 <button (click)="loadData()">load data</button>
<tr *ngFor="let user of users">
          <td>
            <div class="form-check form-check-sm form-check-custom form-check-solid">
              <input class="form-check-input widget-9-check" type="checkbox" value="1"/>
            </div>
          </td>
          <td>
            <span class="text-dark d-block mb-1 fs-7" href="#">{{user.firstName}} {{user.lastName}}</span>
          </td>
          <td>
            <span class="text-dark d-block mb-1 fs-7" href="#">{{user.userName}}</span>
          </td>
          <td>
            <a class="text-dark text-hover-primary d-block mb-1 fs-7" href="#">{{user.email}}</a>
          </td>
          <td>
            <a class="text-dark  text-hover-primary d-block mb-1 fs-7" href="#">
              {{user.isActive}}
            </a>
          </td>
          <td class="text-end">
            <a class="btn btn-icon btn-bg-light btn-active-color-primary btn-sm me-1" href="#">
              <i class="fa-solid fa-shield-halved"></i>
            </a>
            <a class="btn btn-icon btn-bg-light btn-active-color-primary btn-sm me-1" href="#">
              <i class="fa-solid fa-pencil"></i>
            </a>
            <a class="btn btn-icon btn-bg-light btn-active-color-primary btn-sm" href="#">
              <i class="fa-solid fa-pencil"></i>
            </a>
          </td>
        </tr>

in oninit i subscribe my BehaviorSubject and when i click load data i get data from api and send data with BehaviorSubject,but don't work correctly and when click load data button again(twice) dat is loaded. i don't want to use resolver. thank u.

i expect to know why i click twice load data button to load data and what is my wrong?

2

There are 2 best solutions below

2
Enver Usta On

For simple answer: I think you should add your loadData method to ngOnInit. Because it's the actual method which brings the users that you want. However, I have some recommendations for you.

  • Don't subscribe inside your service, use your components instead (definitely you can do further research about it)
  • delete getAllUsers method and in loadData add subscribe method after getAll() and inside the subscribe assign all your users.
  • If you would like to cache your users or somehow want to store them in your service you can still keep them in your service as well.
0
Bittu On

try this with angular, I have attached different example for this.

/* subject initialise without initial value and behavior subject initialise with initial value. step 1: initialize subject step 2: subscribe it step 3: get data by using next and pass value in next,
in behavior subject, no mandatory of using next to get data, because it hold initial data, but in subject to get data you need to use next, because it doesnot hold any initial value. */ subject =new Subject(); subject1= new Subject(); behavSubject =new BehaviorSubject({id: 1, fullname: "tom", checkedIn: true});
subjectData: PassengerI; subject_1_Data: any; behavSubjectData: PassengerI; constructor(){ this.subject.subscribe({ next: (data)=>{ this.subjectData= data console.log("next block", data) }, error: (error)=>{ console.log("error block", error) }, complete:()=>{ console.log("complete block", this.subjectData) } }) this.subject.next({id: 1, fullname: "tom", checkedIn: true})

      this.subject1.subscribe({
        next:(data)=>{
          this.subject_1_Data= data
          console.log("next block", data)
        }, 
        error:(error)=>{
          console.log("error block", error)
        },
        complete:()=>{
          console.log("complete block", this.subject_1_Data)
        }
      })
      this.subject1.next("vvb")
    
      this.behavSubject.subscribe({
        next: (data)=>{
          this.behavSubjectData= data
          console.log("next block", data)
        }, 
        error: (error)=>{
          console.log("error block", error)
        },
        complete:()=>{
          console.log("complete block", this.behavSubject)
        }
      })
       this.subject.next({id: 1, fullname: "tom", checkedIn: true})  // try uncommenting thienter code heres line then also you will get some data, which we set it in beginning while initializing the behaviour subject. 

https://angular-practice-start-sfa8tx.stackblitz.io