V
V
Victor P.2020-09-24 23:37:02
Angular
Victor P., 2020-09-24 23:37:02

How to write asynchronous js functions in html?

Hello!
As you know, there are several ways to work with asynchronous functions in js. For example, Angular is built on the rxjs library, which uses the Observable pattern with its .subscribe. Also in js, in one of the latest specifications, the async / await syntax, based on promises, appeared. And I like to write that way. More succinctly.
For example, we write an api call like this:

public async getUserRoles(userId: string) {
    return this.get<number[]>('GetRoles?userId=' + userId).toPromise();
  }

And then we can call in some components
this.userRoles = await this.service.getUserRoles(this.userId);


Next, I want to cut down the layer. Fold the call of some api call to the local storage into a global service. In order not to pull the apish every time. For example, a list of existing users:

export class UsersApiService extends BaseApiService {
  private storage: UserProfileModel[] = null;

  constructor(public http: HttpClient) {
    super('Users', http);
  }

  public async GetProfiles() {
    if (!this.storage) {
      this.storage = (await this.get<any[]>('GetProfiles').toPromise()).map(x => new UserProfileModel(x));
    }

    return this.storage;
  }

  public async getUser(userId: string) {
    const profiles = await this.GetProfiles();
    return profiles.find(x => x.id == userId);
  }

  public async getUserRoles(userId: string) {
    return this.get<number[]>('GetRoles?userId=' + userId).toPromise();
  }

  public async setUserRoles(userId: string, roles: number[]) {
    return this.post('SetRoles', { userId: userId, roles: roles }).toPromise();
  }
}

Storage, as we see here, is already normal, without promises and other things. Regular array. The logic is as follows: when the GetUser method is called, this method (via await) gets an array of all users. That is, it is loaded for the first time through api, but if it already contains some result, the api does not twitch, just find in the array returns the required user.
For reference, I will also give the UserProfileModel class. This model has a computed getter shortName
/** Пользователь */
export class UserProfileModel {
  /** GUID */
  public id: string;

  /** Логин */
  public userName: string;

  /** Эмейл */
  public email?: string;

  /** Телефон */
  public phone?: string;

  constructor(obj: any = {}) {
    this.id = obj.id;
    this.email = obj.email;
    this.phone = obj.phone;
    this.userName = obj.userName;
  }

  get shortName(): string {
    return this.userName || this.email || this.phone;
  }
}

There is no problem, of course, if I call the GetUser function in some asynchronous function in the component's .ts handler. But, if I try to get GetUser inside the html of any component, nothing happens at all. Even if I use pipe async. For example, I display a list of the Issue model, which has a field with a user ID:
<tr *ngFor="let el of dataSource">
        <td class="col">
          {{users.getUser(el.assignee).shortName | async}}
        </td>
        <td class="col">{{el.reporter}}
        </td>
        <td class="col">{{el.summary}}</td>
        <td class="col">{{el.description}}</td>
        <td>
          <a [routerLink]='["/issue/edit", el.id]'>
            <input type="submit" value="Edit" class="btn btn-primary">
          </a>
        </td>
      </tr>

The page just hangs in an endless loop. At the same time, if you look through the debugger, js regularly enters the shortName getter and, most importantly, gets the correct result the first time, but it doesn’t want to stop there at all, it hammers and hammers like a woodpecker.

Can you please tell me how to write in such situations?

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question