哪种方法适合使用相同的表达式来评估多个指令?
Which is an appropriate way to use the same expression to evaluate multiple directives?
我目前正在构建一个 table,它有一个特定的列,我需要在其中稍微处理数据以获得我想要显示的实际值;该行对象中的值是一个 ID 号,因此我必须在变量中的对象数组中查找该 ID。
像这样:
findIndustry(industry: string) {
if (this.industries.find(x => x._id === parseInt(industry, 10))) {
const industryResult = this.industries.find(x => x._id === parseInt(industry, 10));
return `${industryResult.category} | ${industryResult.subcategory}`;
}
return '(Not Set)';
}
获得结果后,我可以像这样在 table 上显示它:
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client" class="industry-cell">{{ findIndustry(client.industry) }}</td>
</ng-container>
但是现在问题来了;根据返回值,我想显示一个 title
属性,我还想添加一个 ngClass
指令,以便在 (Not Set)
为值时以灰色显示文本:
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client"
[ngClass]="findIndustry(client.industry) === '(Not Set)' ? 'text-muted' : ''"
class="industry-cell"
[title]="findIndustry(client.industry)">{{ findIndustry(client.industry) }}</td>
</ng-container>
我读到在 Angular 模板中使用函数调用是个坏主意,因为它会 运行 每次更改检测的函数,这可能 很多 次;我想知道什么是避免 运行 多次使用该函数的有效方法,或者甚至更好的是完全避免使用该函数;我确实需要相同的值来应用不同的属性,这是我在每个指令和属性上使用它的唯一原因。
我喜欢做的是像这样将它保存在一个大对象中:
findIndustry() {
//
this.object[x._id] = `${industryResult.category} | ${industryResult.subcategory}`;
//
}
然后在您的模板中:
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client" class="industry-cell">{{ object[client._id] }}</td>
</ng-container>
我希望它不会太抽象(如果你有一个 stackblitz 实现它会有所帮助)。
正如您所说,在您的 html 中添加函数是一个非常糟糕的主意...
下面是一篇关于如何在 html
中调用函数的文章
Angular’s Digest Cycle
The digest cycle is how Angular’s auto-update magic works – it’s the reason that typing into an input box automatically updates anything that refers to its value.
When the digest cycle runs, it effectively redraws everything that might have changed on the page.
Angular uses some tricks to find “everything that might have changed”, and the main technique is watchers. These watchers are created automatically when you use directives like ng-if
and ng-class
, and when you use bindings like {{ yourBindingHere }}
.
Each one of those things registers a watcher. When Angular’s digest cycle runs, every watcher is asked to update its state. In the case of ng-class, it will re-run the function bound to it, to see if anything needs to change. This is why your controller function runs multiple times, and it’ll run again each time something changes on the page.
Source Controller Function Is Executed Multiple Times
响应式编程来拯救
Angular 默认预装了 rxjs
。我们可以发挥Observables
的力量来解决这个问题。
我们可以实现类似
的东西
- 用数据定义一个 observable
- 定义另一个 observable 来跟踪行业 id
- 结合两者
Obsevables
- 订阅结果
Observable
- 定义触发行业 id 更改的方法
用数据定义一个可观察对象
在很多情况下,这只是来自 http 请求的响应
industries$ = this.http.get<IIndustries[]>('some/api')
我们还可以使用来自 rxjs
的 from
和 of
运算符生成可观察对象
industries: IIndustries[] = [{ ... }, { ... }, ...]
industries$ = of(this.industries)
定义另一个 observable 来跟踪行业 id
下一步是定义跟踪所选行业的方法。我将使用一个 BehaviorSubject
,它将在初始加载时发出一个值,并且每次我们在 Subject
上调用 next()
函数时
selectedIdSubject$ = new BehaviorSubject<number>(1) // By default 1 will be selected
selectedId$ = this.selectedIdSubject$.asObservable()
结合两者Obsevables
接下来我们需要将两者结合起来Observables
。我们将使用 rxjs
中的 combineLatest
来自官方文档
This operator is best used when you have multiple, long-lived observables that rely on each other for some calculation or determination.
Source Why use combineLatest?
industry$ = combineLatest([this.industries$, this.selectedId]).pipe(
map(([industries, industry]) => {
if (industries.find(x => x._id === parseInt(industry, 10))) {
const industryResult = industries.find(x => x._id === parseInt(industry, 10));
return `${industryResult.category} | ${industryResult.subcategory}`;
}
return '(Not Set)';
}
})
)
我们正在管道 Observable
流并使用 rxjs/operators
中的 map
运算符来转换数据
即将完成,订阅结果 Observable
我们将 async
管道来执行此目的
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client" class="industry-cell">{{ industry$ | async }}</td>
</ng-container>
最后...定义一种触发行业id变化的方法
要触发所选项目的更改,我们只需调用 Subject
上的 next 方法
changeSelectedId (id) {
this.selectedIdSubject$.next(id)
}
通过上述方法,angular变化检测只会在调用changeSelectedId ()
函数时触发
我目前正在构建一个 table,它有一个特定的列,我需要在其中稍微处理数据以获得我想要显示的实际值;该行对象中的值是一个 ID 号,因此我必须在变量中的对象数组中查找该 ID。
像这样:
findIndustry(industry: string) {
if (this.industries.find(x => x._id === parseInt(industry, 10))) {
const industryResult = this.industries.find(x => x._id === parseInt(industry, 10));
return `${industryResult.category} | ${industryResult.subcategory}`;
}
return '(Not Set)';
}
获得结果后,我可以像这样在 table 上显示它:
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client" class="industry-cell">{{ findIndustry(client.industry) }}</td>
</ng-container>
但是现在问题来了;根据返回值,我想显示一个 title
属性,我还想添加一个 ngClass
指令,以便在 (Not Set)
为值时以灰色显示文本:
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client"
[ngClass]="findIndustry(client.industry) === '(Not Set)' ? 'text-muted' : ''"
class="industry-cell"
[title]="findIndustry(client.industry)">{{ findIndustry(client.industry) }}</td>
</ng-container>
我读到在 Angular 模板中使用函数调用是个坏主意,因为它会 运行 每次更改检测的函数,这可能 很多 次;我想知道什么是避免 运行 多次使用该函数的有效方法,或者甚至更好的是完全避免使用该函数;我确实需要相同的值来应用不同的属性,这是我在每个指令和属性上使用它的唯一原因。
我喜欢做的是像这样将它保存在一个大对象中:
findIndustry() {
//
this.object[x._id] = `${industryResult.category} | ${industryResult.subcategory}`;
//
}
然后在您的模板中:
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client" class="industry-cell">{{ object[client._id] }}</td>
</ng-container>
我希望它不会太抽象(如果你有一个 stackblitz 实现它会有所帮助)。
正如您所说,在您的 html 中添加函数是一个非常糟糕的主意...
下面是一篇关于如何在 html
中调用函数的文章Angular’s Digest Cycle
The digest cycle is how Angular’s auto-update magic works – it’s the reason that typing into an input box automatically updates anything that refers to its value.
When the digest cycle runs, it effectively redraws everything that might have changed on the page.
Angular uses some tricks to find “everything that might have changed”, and the main technique is watchers. These watchers are created automatically when you use directives likeng-if
andng-class
, and when you use bindings like{{ yourBindingHere }}
.
Each one of those things registers a watcher. When Angular’s digest cycle runs, every watcher is asked to update its state. In the case of ng-class, it will re-run the function bound to it, to see if anything needs to change. This is why your controller function runs multiple times, and it’ll run again each time something changes on the page.
Source Controller Function Is Executed Multiple Times
响应式编程来拯救
Angular 默认预装了 rxjs
。我们可以发挥Observables
的力量来解决这个问题。
我们可以实现类似
的东西- 用数据定义一个 observable
- 定义另一个 observable 来跟踪行业 id
- 结合两者
Obsevables
- 订阅结果
Observable
- 定义触发行业 id 更改的方法
用数据定义一个可观察对象
在很多情况下,这只是来自 http 请求的响应
industries$ = this.http.get<IIndustries[]>('some/api')
我们还可以使用来自 rxjs
from
和 of
运算符生成可观察对象
industries: IIndustries[] = [{ ... }, { ... }, ...]
industries$ = of(this.industries)
定义另一个 observable 来跟踪行业 id
下一步是定义跟踪所选行业的方法。我将使用一个 BehaviorSubject
,它将在初始加载时发出一个值,并且每次我们在 Subject
next()
函数时
selectedIdSubject$ = new BehaviorSubject<number>(1) // By default 1 will be selected
selectedId$ = this.selectedIdSubject$.asObservable()
结合两者Obsevables
接下来我们需要将两者结合起来Observables
。我们将使用 rxjs
中的 combineLatest
来自官方文档
This operator is best used when you have multiple, long-lived observables that rely on each other for some calculation or determination.
Source Why use combineLatest?
industry$ = combineLatest([this.industries$, this.selectedId]).pipe(
map(([industries, industry]) => {
if (industries.find(x => x._id === parseInt(industry, 10))) {
const industryResult = industries.find(x => x._id === parseInt(industry, 10));
return `${industryResult.category} | ${industryResult.subcategory}`;
}
return '(Not Set)';
}
})
)
我们正在管道 Observable
流并使用 rxjs/operators
中的 map
运算符来转换数据
即将完成,订阅结果 Observable
我们将 async
管道来执行此目的
<ng-container matColumnDef="parentName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Industry </th>
<td mat-cell *matCellDef="let client" class="industry-cell">{{ industry$ | async }}</td>
</ng-container>
最后...定义一种触发行业id变化的方法
要触发所选项目的更改,我们只需调用 Subject
changeSelectedId (id) {
this.selectedIdSubject$.next(id)
}
通过上述方法,angular变化检测只会在调用changeSelectedId ()
函数时触发