ng-template-类型变量

时间:2019-04-01 15:15:51

标签: angular typescript

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

  

未解决的变量类型

如何将其键入为{{content.type}}

parent.component.ts:

Video

parent.component.html:

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

public videos: Video = [{id: 1, duration: 30, type: 'documentary'}];

tile.component.ts:

<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>

tile.component.html:

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

5 个答案:

答案 0 :(得分:3)

let-*变量没有类型推断。 let-上下文是Angular的微语法解析器的一部分,并且由于没有明确的来源,IDE无法推断类型。

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

您可以尝试使用$any()

使IDE警告静音

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>

您可以使用函数 force 进行类型推断

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

public toVideo(value: any): Video { return value as Video; }

答案 1 :(得分:2)

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

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

export class CardComponent {
  ...
  public video = (item: Video) => item;
}

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

<h5 class="tile__type">{{video(content).type}}</h5>

答案 2 :(得分:2)

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

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; }
}

像这样使用

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

在组件中

// 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 };

答案 3 :(得分:1)

有办法,但是很笨拙……

如果您的ts文件中有此文件作为模板

somethingNullable?: { name: string };

您可以使用以下内容删除可空性...

*ngIf="somethingNullable; let somethingNullable"

...但是事实并非如此。好吧,它删除了可空性,但是通过将其键入为 any 来实现此目的。很长时间以来,我一直认为它给了我{ name: string }类型的东西,我认为这是解决问题的一种超级聪明的方法。

正如reactular所说,您可以创建一个函数,但是随后您必须在<ng-template>内多次使用它。

笨拙的方式

另一种方法是返回仅包含一项的数组,然后使用*ngFor

coerceProductArray(product: any): { sku: string, price: number } []
{
    return [ product ];
}

幸运的是,*ngFor 确实将项目的类型传递给了“内部模板”

<ng-container *ngFor="let product of coerceProductArray(product)">

    <!-- These work -->
    {{ product.sku }}
    {{ product.price }}
    {{ product.price * 2 }}

    <!-- These have squiggles -->
    {{ product.desc }}
    {{ product.price + 'cat' }}
    {{ product.sku * 2 }}

</ng-container>

在将ng-template用于更复杂的可重用模板(您不想创建新组件)时,重构非常不方便-但这至少意味着您可以快速找到问题。

希望有一种更聪明的方法。

答案 4 :(得分:0)

这可以通过将变量包装在另一个 ng-template

中来解决

这是另一种解决方法,但我比其他解决方案更喜欢它,因为它只是在 HTML 中添加了 2 行代码,当然,如果您只使用变量 1 或 2 次 @Reactangular {{ 3}} 更好。我的回答:

取而代之的是:

<ng-template *ngTemplateOutlet="foo; context: {$implicit: {fooProp: 'Hello!'}}"></p>
<ng-template #foo let-args>
    This is untyped: {{ args.fooProp }}<br>
</ng-template>

这样做:

<ng-template *ngTemplateOutlet="foo; context: {$implicit: {fooProp: 'Hello!'}}"></p>
<ng-template #foo let-untypedArgs>
    <ng-template [ngIf]="identity(untypedArgs)" let-args="ngIf">
        This is typed: {{ args.fooProp }}<br>
    </ng-template>
</ng-template>
identity(foo: Foo): Foo {
    return foo;
}

当使用 *ngFor*ngIf 时,类型断言为 answer。此解决方案的缺点是内部 <ng-template> 由于 [ngIf] 而延迟呈现。

有了这个,现在如果你向你的上下文添加一个无效的属性,你会得到以下编译错误,这很好,noticed by the IDE

<块引用>

属性 'newFooProp' 在类型 'Foo' 上不存在。

相关问题