#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)
функцию из шаблона. Это было бы вызвано несколько раз без необходимости, т. е. на каждом цикле CD4. @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. Почему только в режиме разработки?