2

I have three components: userlist page, userdetails page, and inside userdetails there is a subcomponent (Child) that displays a list of users like (Related), when I enter user1 I see his name and email and on the other side there is a that will list the rest of the usernames, when I select another user to see the user details, the URL change without any problem but the user Details does not change Until I reload page after that the new user data appears on user Details.

UserListComponent:

  users!: any;

  constructor(private userService: UserService) { }

  ngOnInit(): void {
     // To show All users
      this.userService.getAllUsers().subscribe((data) => {
      this.users = data;
    }, (error) => {
      console.warn("Error");
    })
  }

  // UserListComponent.html
  <div *ngFor="let user of users">
      <h3><a [routerLink]="['users', user.id]"> {{user.name}}</a></h3>
  </div>

UserDetailsComponent.ts:

  public id!: any;
  public user!: any;

  constructor(private actRouted: ActivatedRoute, private userServise: UserService) {
    this.id = this.actRouted.snapshot.paramMap.get('id');
   }

  ngOnInit(): void {
    this.actRouted.params.subscribe(
      (params: Params) => {
        this.id = params['id'];
      }
    )
    // To show Selected user
    this.userServise.getUserById(this.id).subscribe((data) => {
      this.user = data;
    },(error) => {
      console.warn("error");
    })
  }

    //UserDetailsComponent.html
    <h3>{{user.email}}</h3>
    <h3>{{user.name}}</h3>
    <div class="center">
        // this child will pass name of selected User
        <app-child-details [passTCh]="this.user"></app-child-details>
    </div>

ChildDetailsComponent.ts:

 @Input() passTCh: any;

  data: any;
  filtered_array: any;

  constructor(private userServise: UserService) { }

  ngOnInit(): void {
    // console.log(this.passTCh.id);

    const Observable$ = new Observable((observer) => {
      this.userServise.getAllUsers().subscribe((data) => {
        this.data = data; 
        // I Filter userSelcetd !==  id 
        this.filtered_array = this.data.filter((value: any) => value.id != this.passTCh.id);
        console.log("detail***********",  this.filtered_array);
        observer.next(observer);
      }, (error) => {
        console.warn("Error", error);
      })
    });

    Observable$.subscribe(d => {
      console.log(d)
    })
  }
     //ChildDetailsComponent.html
     <h6>{{passTCh.name}}</h6>
     <hr />
     <div *ngFor="let user of this.filtered_array">
        <h6><a [routerLink]="['/users', user.id]"> {{user.name}}</a></h6>
     </div>

UserService


      getAllUsers() {
        return this.http.get(this.baseUrl);
        
      }
    
    
      getUserById(id:any) {
        return this.http.get(this.baseUrl + id);
      }

I want these components to change dynamically without reload page

Rakh
  • 33
  • 3

2 Answers2

0

I may not be understanding your needs exactly, but in general your UsersService should get the data and save it, and then return that saved data (as a Subject or Observable) whenever something else requests it so that you don't have to many HTTP requests.

Whenever you modify that data you can call the next()on the Subject to trigger anything that subscribes to get the new copy of the data.

as a side note, you should avoid using the any type since that defeats the entire purpose of using TypeScript in the first place.

user.model.ts

export interface IUser {
  id: number;
  firstName: string;
  lastName: string;
  email: string;
}

user.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { tap } from 'rxjs/operators';
import { IUser } from './user.model';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  usersChanged$ = new Subject<IUser[]>;
  usersList: IUser[] = [];

  getAllUsers(): void {
    if(this.usersList.length === 0){
      this.http.get(this.baseUrl)
        .pipe(tap(users => {
           //Save the data
           this.usersList = users;
           //emit a copy of the data we just got
           this.usersChanged$.next(users.splice());
         })
       )
    } else {
      //We already have the data, emit a copy of it
      this.usersChanged$.next(this.usersList.splice());
    }
  }

  addUser(newUser: IUser): void {
    this.usersList.push(newUser);
    this.usersChanged$.next(this.usersList.splice());
  }

  deleteUser(userId: number): void {
    this.usersList = this.usersList.filter(u=>u.id !== userId);
    this.usersChanged$.next(this.usersList.splice());
  }
}

UserList.component.ts

users: IUser[] = [];

ngOnInit(): void {
  this.userService.usersChanged$.subscribe((data) => {
    this.users = data;
  }, (error) => {
    console.warn("Error");
  })

  this.userService.getAllUsers(); //trigger the request
}

The above is untested, but I think it gives you the right idea at can at least put you on the right path.

Chris Barr
  • 29,851
  • 23
  • 95
  • 135
  • Sorry, but could you please explain more how to put it inside UserService. what is supposed to be put inside the next(), I've updated the question and added UserService so you can see my service – Rakh Jun 29 '23 at 14:30
  • Ah, I didn't realise you were just returning raw HTTP requests. See my updated answer now – Chris Barr Jun 29 '23 at 15:07
  • Do this code need the `usersChanged$` subject? Could this code use `shareReplay(1)` instead and let RxJS cache and return the response? – DeborahK Jun 29 '23 at 17:55
  • Yes you probably could do that, but the point of `usersChanged$` is so that when your data changes, it comes through there too. I just updated my answer again to show how you might handle adding or removing a user from the list. Those changes would now come through in the subject. – Chris Barr Jun 29 '23 at 18:28
0

Your data isn't updating because Angular does not call ngOnInit() again if the same component is routed to. So in your code below:

  ngOnInit(): void {
    this.actRouted.params.subscribe(
      (params: Params) => {
        this.id = params['id'];
      }
    )
    // To show Selected user
    this.userServise.getUserById(this.id).subscribe((data) => {
      this.user = data;
    },(error) => {
      console.warn("error");
    })
  }

userServise.getUserById() only gets called once, but not when the route param changes. Since you are subscribing to the route params, your this.id value does get updated whenever the route changes, you just need to make sure to call getUserById() each time that happens.

You can accomplish this with switchMap:

  ngOnInit(): void {
    this.actRouted.params.pipe(
      map(params => params['id']),
      switchMap(id => this.userServise.getUserById(id))
    ).subscribe(
      data => this.user = data,
      error => console.warn("error")
    )
  }

Now, whenever actRouted.params emits, the value is passed into switchMap which will subscibe to userService.getUserById() and emit the result.


Instead of subscribing, you could define an observable that emits your user data and then use the async pipe in the template:

class UserDetailsComponent {

  public user$ = this.actRouted.params.pipe(
    map(params => params['id']),
    switchMap(id => this.userServise.getUserById(id))
  );

  constructor(private actRouted: ActivatedRoute, private userServise: UserService) { }

}
<ng-container *ngIf="user$ | async as user">
    <h3> {{ user.email }} </h3>
    <h3> {{ user.name }} </h3>
    <div class="center">
        <app-child-details [passTCh]="user"></app-child-details>
    </div>
</ng-container>

Notice the user$ observable will emit result of getUserById whenever actRouted.params emits a value. Having defined this declaratively, we don't even need ngOnInit at all.

BizzyBob
  • 12,309
  • 4
  • 27
  • 51