Home > Enterprise >  Angular Getting value of referenced id in ngFor
Angular Getting value of referenced id in ngFor

Time:10-09

I am currently working on a personal project with Angular and .NET API with SQL Server for the backend. Now I did not want to use Foreign Keys, because then you are dependent on the database. So my models do have referenced id's which refer to other objects.

The main purpose of the app is creating a task list which says if the task is done, and who the task is assigned to. So the task model has a attribute called: "responsible_user_id": 1 for example. Number 1 means that the user with id = 1 is responsible for this task. But a 1 doesn't mean much on the frontend, I want to show a name of the user.

Now I have the following:

<div class="container">
    <div class="row" >
      <div class="col-4" *ngFor="let task of categoryTasks">
        <div class="card" style="width: 18rem;">
          <div class="card-body">
            <h5 class="card-title">{{task.item_title}} </h5>
    ----->>>  <h5 class="card-title">Reponsible user: {{task.responsible_user_id}} </h5>
            <h5 *ngIf="(task.item_status)"> <i  style="color:rgb(34, 216, 34);" class="bi bi-check-circle"></i>
            </h5>
            <h5 *ngIf="(!task.item_status)"> <i style="color:red;" class="bi bi-x"></i>
            </h5>
              <br>
            <button (click)="setDone(task.item_id)"  class="btn btn-primary">Done</button>
        </div>
      </div>
      </div>
    </div>
  </div>

I placed the arrows at the line where I want the username to be shown. Currently I can only show the id, but there is another new API call needed to get the referenced user. Everything I tried turns out in a lot of errors untill the program crashed.

A method I tried was creating a method in the component.ts file:

  public GetResponsibleUser(id){
    var user:any;

    this.userService.getUser(id).pipe(first())
    .subscribe(res => user = res);

    var name = user.username;

    return name;
  }

which I would call from the line above with the arrows before it but that didn't work.

Is there anyone who knows how to solve this problem?

Thanks in advance!!

Kind regards, Julius

CodePudding user response:

Your function GetResponsibleUser is not working like this as you would have to return the Observable:

NOT-WORKING!
    public GetResponsibleUser(id): Obserable<User> {
        return this.userService.getUser(id).pipe(first())
    }

And in the HTML you would need to add a async pipe:

  <h5 class="card-title">Reponsible user: {{GetResponsibleUser(task.responsible_user_id) | async }}</h5>

But this would still lead into your huge amount of calls to the backend as every time the obserable is triggered (call finished), angulars change-detection would run and create the next call to this method. -> Ends up in an endless call.

You could create kind of cache in your component or even better in a Service.
Just as example:

  private cache = new Map<string, string>();

  GetResponsibleUser(userId: string): Observable<string> {
    if (this.cache.get(userId)) {
      return of(this.cache.get(userId));
    } else {
      return this.userService.getUser(userId)
        .pipe(
          first(),
          map(user => user.getUsername()),
          tap(username => this.cache.set(userId, username))
        );
    }
  }

CodePudding user response:

You can do next this way:

getUsersName(id) {
   return this.usersService.getUser().pipe(
     map(user => user.Name)
   )
}

and in template:

 {{ getUsersName(user.id) }}

BUT If you put Observable of http request into template by using async pipe - app will do 7 requests for each change detector iteration and responses will changes data and fire change detector -> detector refresh data and fire pipes -> async pipe runs(subscribes) streams and fire detector again because Ng Zone call detector when http call happened - cycle!!!**

If you can't add "Name" field for users list see worked example: stackblitz-example with users and their posts in your case names

**why .pipe(first()) won't help ? - because app will create new stream with first pipe every time when call method that returns stream. So it works only for streams that return data in multiple iteration. Also it can be used for streams with one iteration - to unsubscribe stream after response - but if response will be too long you can leave component and subscription call back may be try to use non-existed properties of died component.

  • Related