Как установить временную переменную внутри ngFor с помощью углового 11?

#angular #typescript

Вопрос:

У меня есть следующий html-код, который вызывает эти: getMetaForXXX методы несколько раз внутри ngFor блока. Я чувствую, что это не очень хорошая практика, и надеялся, что найдется способ упростить эту логику.

   <div *ngFor="let i of state.data" class="carousel-cell" tabindex="-1">
    <div class="carousel-cell-primary">
      <span class="large">{{getMetaForVendor(i.vendor).display}}</span>
      <span class="large">{{i.name}}</span>
      <span>{{i.issued | monthYear}} amp;mdash; {{i.expiry | monthYear}}</span>
      <span>Credential ID: {{i.credential || "Not Applicable"}}</span>
      <span>
        <mat-slider
          aria-label="units"
          [disabled]="true"
          [max]="4"
          [min]="0"
          [value]="getMetaForLevel(i.level).rank"
        >
        </mat-slider>
        {{getMetaForLevel(i.level).display}}
      </span>
    </div>
  </div>
 

component.html

   state: CertificationState;

  getMetaForLevel(value: string): MetaData {
    return this.state.meta.levels.find(meta => meta.name === value);
  }

  getMetaForVendor(value: string): MetaData {
    return this.state.meta.vendors.find(meta => meta.name === value);
  }
 

компонент.ts

 export interface CertificationState {
  data: Certification[];
  meta: CertificationMeta;
}

export interface Certification {
  id: number;
  credential: string;
  description: string;
  expiry: MonthYear;
  issued: MonthYear;
  level: string,
  name: string;
  vendor: string;
}

export interface CertificationMeta {
  levels: MetaData[];
  vendors: MetaData[];
}

export interface MetaData {
  name: string;
  display: string;
  description: string;
  rank: number;
}
 

модель.ts

 {
  "data": [
    {
      "id": "1",
      "name": "Cloud Practitioner",
      "credential": "1234",
      "description": "Lorem ipsum dolor sit amet.",
      "expiry": {
        "month": 7,
        "year": 2023
      },
      "issued": {
        "month": 12,
        "year": 2018
      },
      "level": "FOUNDATIONAL", <-- correlates to meta.levels.name
      "vendor": "AWS"          <-- correlates to meta.vendors.name
    }
    ...
  ],
  "meta": {
    "levels": [
      {
        "name": "FOUNDATIONAL",
        "description": "lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris venenatis purus",
        "display": "Associate",
        "rank": 2
      }
      ...
    ],
    "vendors": [
      {
        "name": "AWS",
        "display": "Amazon Web Services (AWS)"
      }
      ...
    ]
  }
}
 

пример данных модели

Комментарии:

1. Вызов функции из шаблона будет вызываться при каждом цикле обнаружения изменений, что в целом является плохой практикой. Это очень хороший вопрос (я предлагаю не голосовать за закрытие)

Ответ №1:

Один из способов создания переменной шаблона-с *ngIf помощью . В приведенном ниже коде директива применяется к div элементу внутри *ngFor цикла :

  • Чтобы убедиться, что *ngIf условие верно, создается и оценивается объект
  • Значение, возвращаемое выражением, присваивается свойству объекта
  • Объект присваивается новой переменной шаблона, metaForLevel
  • Затем вы можете использовать эту переменную несколько раз в итерации цикла
 <div *ngFor="let i of state.data" ...>
  <div *ngIf="{data: getMetaForLevel(i.level)} as metaForLevel" ...>
    ...
    <span>
      <mat-slider ... [value]="metaForLevel.data.rank"></mat-slider>
      {{ metaForLevel.data.display }}
    </span>
  </div>
</div>
 

В тех случаях, когда HTML-контейнер отсутствует или нежелателен внутри *ngFor цикла, *ngIf условие может быть применено к ng-container элементу.

Комментарии:

1. Будет ли этот подход работать с несколькими значениями на одном и том же условном или мне придется вложить? Также работает ли это с трубами вместо методов?

2. Каждый *ngIf может создать одну переменную шаблона. Таким образом, для нескольких переменных вам придется вложить несколько ng-container элементов. Я почти уверен, что вы можете использовать трубы в *ngIf выражении.

3. Вы все еще вызываете getMetaForLevel(i.level) функцию из шаблона. Это было бы вызвано несколько раз без необходимости, т. е. на каждом цикле CD

4. @Drenai — Мой ответ касается создания временной переменной в *ngFor цикле. Значение, присвоенное переменной, может быть результатом вызова функции или любого другого допустимого выражения. Я оставляю за каждым разработчиком право определить, подходит ли используемое ими выражение в их конкретном случае.

Ответ №2:

вы должны создать трубу здесь :-

 @Pipe({name: 'displayVal'})
export class DisplayValuePipe implements PipeTransform {
  transform(val, state): string {
     return state.meta.levels.find(meta => meta.name === value.vendor)?.display;
  }
}
 

и используйте его, как :-

 <span class="large">{{i | displayVal: state}}</span>
 

Комментарии:

1. Хммм. Однако это все равно будет выполняться несколько раз? Кроме того, мне нужен весь объект метаданных, так как я читаю несколько полей, а не только поле отображения. Я использую аналогичный шаблон в других HTML-файлах.

2. по одному разу для каждой строки.

3. Если мне нужны свойства отображения и ранжирования из модели метаданных. Этот метод вызывается дважды для каждой строки, даже если возвращаемый объект метаданных содержит все данные.

4. дважды за строку только в режиме разработки.

5. Почему только в режиме разработки?