扩展ngFor指令,而不丧失对象类型。

huangapple go评论72阅读模式
英文:

Extending ngFor directive without the interface losing object typing

问题

在我们的应用程序中,每个对象都有与其关联的唯一ID。为了帮助呈现我们遇到的大型列表(其中有很多),我决定在我们的应用程序中的所有模板化的*ngFor循环中使用trackby函数。

我决定减少trackby函数的重复,最佳方法是创建一个自定义的ngFor*指令,如果发现所讨论的对象具有ID或具有索引(业务逻辑决定这一点),则会自动添加自定义的trackby

这确实起作用,trackby会像我希望的那样被添加。但是,当使用自定义的*ngFor时,我注意到在模板中NgIterable对象的类型信息丢失了,而我不知道为什么,因为我在使用中进行了扩展。

以下是自定义指令的代码:

import {
  Directive,
  Input,
  IterableDiffers,
  NgIterable,
  TemplateRef,
  TrackByFunction,
  ViewContainerRef,
} from "@angular/core";

import { NgFor, NgForOfContext } from "@angular/common";
import { InfiniteScrollItem } from "@shared/lists";

@Directive({
  selector: "[dpsNgFor][[dpsNgForOf]]",
  standalone: true,
})
export class DpsNgForDirective<T, U extends NgIterable<T> = NgIterable<T>> extends NgFor<T, U> {
  public _trackBy(index: number, item: any) {
    const _isScrollItem = (object: InfiniteScrollItem<T> | never) => "item" in object && "index" in object;
    const _hasId = (object: InfiniteScrollItem<T> | never) => "id" in object;

    // If it's an infinite scroll object, use the item.index;
    if (_isScrollItem(item)) {
      return item.index;
    }

    // Not an infinite scroll item
    if (_hasId(item)) {
      // Use the object id (if it has one).
      return item.id;
    }
    // Fall back to index if id isn't found natively in the object.
    return index;
  }

  @Input()
  set dpsNgForOf(value) {
    super.ngForOf = value;
  }

  @Input()
  set dpsNgForTrackBy(trackBy: TrackByFunction<T>) {
    super.ngForTrackBy = trackBy;
  }

  constructor(
    public _viewContainer2: ViewContainerRef,
    public _templateRef2: TemplateRef<NgForOfContext<T, U>>,
    public _differs2: IterableDiffers
  ) {
    super(_viewContainer2, _templateRef2, _differs2);
    super.ngForTrackBy = this._trackBy;
  }
}

将对象类型与接口的正确类型对应:

  • 在使用常规ngFor时,接口的类型看起来通常如下:
    扩展ngFor指令,而不丧失对象类型。

  • 在使用自定义ngFor指令时,接口的类型会丢失。两者都能正确迭代,只是这一个丢失了与对象相关的类型信息。
    扩展ngFor指令,而不丧失对象类型。

英文:

In our application, every object has a unique ID associated with it. To help with rendering of the large lists we occur (of which, there are many), I have decided to utilize trackby functions for all templated *ngFor loops within our app.

I decided the best course of action to reduce duplication of the trackby functions, would be to create a custom ngFor* directive that does this automatically, if it finds that the object in question has an ID or has an index (business logic dictates this) it would add the custom trackby.

This does work, and the trackby gets added like I had hoped. However, when utilizing the custom *ngFor I noticed that the typing gets lost on the NgIterable object within the template and I'm not sure why, since I am extending with usages.

Here is the custom directive:

import {
Directive,
Input,
IterableDiffers,
NgIterable,
TemplateRef,
TrackByFunction,
ViewContainerRef,
} from &quot;@angular/core&quot;;
import { NgFor, NgForOfContext } from &quot;@angular/common&quot;;
import { InfiniteScrollItem } from &quot;@shared/lists&quot;;
@Directive({
selector: &quot;[dpsNgFor][[dpsNgForOf]]&quot;,
standalone: true,
})
export class DpsNgForDirective&lt;T, U extends NgIterable&lt;T&gt; = NgIterable&lt;T&gt;&gt; extends NgFor&lt;T, U&gt; {
public _trackBy(index: number, item: any) {
const _isScrollItem = (object: InfiniteScrollItem&lt;T&gt; | never) =&gt; &quot;item&quot; in object &amp;&amp; &quot;index&quot; in object;
const _hasId = (object: InfiniteScrollItem&lt;T&gt; | never) =&gt; &quot;id&quot; in object;
// If it&#39;s an infinite scroll object, use the item.index;
if (_isScrollItem(item)) {
return item.index;
}
// Not an infinite scroll item
if (_hasId(item)) {
// Use the object id (if it has one).
return item.id;
}
// Fall back to index if id isn&#39;t found natively in the object.
return index;
}
@Input()
set dpsNgForOf(value) {
super.ngForOf = value;
}
@Input()
set dpsNgForTrackBy(trackBy: TrackByFunction&lt;T&gt;) {
super.ngForTrackBy = trackBy;
}
constructor(
public _viewContainer2: ViewContainerRef,
public _templateRef2: TemplateRef&lt;NgForOfContext&lt;T, U&gt;&gt;,
public _differs2: IterableDiffers
) {
super(_viewContainer2, _templateRef2, _differs2);
super.ngForTrackBy = this._trackBy;
}
}

Any help would be appreciated, or pointed in the right direction.

Edit: Here are some examples of what I mean, when referring to "interface typing is lost".

This is what it typically looks like when the interface has the proper typing with a regular ngFor
扩展ngFor指令,而不丧失对象类型。

And this is what it looks like with the custom ngFor directive. Both accomplish the goal of iterating correctly. This one just lost the typing associated with the object.
扩展ngFor指令,而不丧失对象类型。

答案1

得分: 0

将代码翻译成中文:

原来,我必须在 `value` 变量上设置类型,以使其看起来像下面这样:

```typescript
@Input()
set dpsNgForOf(value: U &amp; NgIterable&lt;T&gt;) {
  super.ngForOf = value;
}
英文:

Turns out, I had to set typing on the value variable so that it ended up looking like the following:

  @Input()
set dpsNgForOf(value: U &amp; NgIterable&lt;T&gt;) {
super.ngForOf = value;
}
</details>

huangapple
  • 本文由 发表于 2023年2月19日 02:51:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75495635.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定