ng-template-类型变量

ssu*_*ski 8 typescript angular

父组件如何识别let-content其来源 类型ngTemplateOutletContext?现在{{content.type}}可以正常工作,但是IDE会说:

未解析的变量类型

如何输入为Video

parent.component.ts:

export interface Video {
  id: number;
  duration: number;
  type: string;
}

public videos: Video = [{id: 1, duration: 30, type: 'documentary'}];
Run Code Online (Sandbox Code Playgroud)

parent.component.html:

<ul>
  <li *ngFor="let video of videos">
    <tile [bodyTemplate]="tileTemplate" [content]="video"></app-card>
  </li>
</ul>

<ng-template #tileTemplate let-content>
  <h5 class="tile__type">{{content.type}}</h5>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

tile.component.ts:

@Component({
  selector: 'tile',
  templateUrl: './tile.component.html',
  styleUrls: ['./tile.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CardComponent {
  @Input() tileTemplate: TemplateRef<any>;
  @Input() content: Video;
}
Run Code Online (Sandbox Code Playgroud)

tile.component.html:

<div
...
  <ng-container
    [ngTemplateOutlet]="tileTemplate"
    [ngTemplateOutletContext]="{ $implicit: content }">
  </ng-container>
...
</div>
Run Code Online (Sandbox Code Playgroud)

Lor*_*ole 36

我创建了一个辅助指令来解决这个问题。

import { Directive, Input, TemplateRef } from '@angular/core';

@Directive({selector: 'ng-template[typedTemplate]'})
export class TypedTemplateDirective<TypeToken> {

  // how you tell the directive what the type should be
  @Input('typedTemplate')
  typeToken: TypeToken;

  // the directive gets the template from Angular
  constructor(private contentTemplate: TemplateRef<TypeToken>) {
  }

  // this magic is how we tell Angular the context type for this directive, which then propagates down to the type of the template
  static ngTemplateContextGuard<TypeToken>(dir: TypedTemplateDirective<TypeToken>, ctx: unknown): ctx is TypeToken{ return true; }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它

<!-- typedTemplate is the directive, typeToken is an object on our component -->
<ng-template #someTemplate [typedTemplate]="typeToken" let-param="param">
  {{param}}
</ng-template>
Run Code Online (Sandbox Code Playgroud)

并且在组件中

// here we create typeToken. the value doesn't matter as it's never used, but the type of this variable defines the types of all template parameters. 
typeToken: { param: string };
Run Code Online (Sandbox Code Playgroud)

  • 不起作用,对象参数为空 (2认同)
  • 这是如何与多个参数一起工作的 (2认同)

Rei*_*las 8

有一个简单的解决方法可以让 IDE 运行并使用用户定义的类型保护来完成代码:

在您的类中创建一个函数,该函数将变量作为参数并返回相同的变量:

export class CardComponent {
  ...
  public video = (item: Video) => item;
}
Run Code Online (Sandbox Code Playgroud)

现在只需使用函数将变量包装在模板中:

<h5 class="tile__type">{{video(content).type}}</h5>
Run Code Online (Sandbox Code Playgroud)

  • 这是 @Reactangular 在接受的答案中给出的解决方案的重新发布:/sf/answers/3882110571/ (8认同)
  • 但这会带来性能问题,不是吗?您将对象引用更改为必须在每个周期运行的方法。在 Angular 中,这应该使用纯管道来完成,以明确指示框架计算一次。 (4认同)
  • 我见过的最好的解决方法 - 使用 1 个字符的变量名称几乎不会使模板混乱。但很难相信这是必要的...... (2认同)

Rea*_*lar 7

没有let-*变量的类型推断。所述let-上下文是微句法分析器折角的一部分,并且因为没有明确原点的IDE不能推断的类型。

https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855

您可以尝试使用以下方法使IDE警告静音 $any()

https://angular.io/guide/template-syntax#the-any-type-cast-function

<ng-template #tileTemplate let-content>
  <h5 class="tile__type">{{$any(content).type}}</h5>
</ng-template>
Run Code Online (Sandbox Code Playgroud)

您可以使用函数来强制类型推断

<ng-template #tileTemplate let-content>
  <h5 class="tile__type">{{toVideo(content).type}}</h5>
</ng-template>

public toVideo(value: any): Video { return value as Video; }
Run Code Online (Sandbox Code Playgroud)

  • 但这将导致 ngZone 在每次运行中调用该函数。我认为如果每个模板中都有这个,那么性能下降是不值得的 (4认同)
  • @MusabKurt 是的,会的。另一种方法是使用管道,这更方便,因为您可以在不同的组件模板中使用管道。我希望 Angular 团队能够为我们介绍一种为模板定义自定义 $type() 方法的方法。 (2认同)
  • 如果您使用“changeDetection:ChangeDetectionStrategy.OnPush”,您可能可以摆脱此方法 (2认同)