父组件如何识别来自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;
}
答案 0 :(得分:3)
let-*
变量没有类型推断。 let-
上下文是Angular的微语法解析器的一部分,并且由于没有明确的来源,IDE无法推断类型。
https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855
您可以尝试使用$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>
您可以使用函数 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' 上不存在。