在Angular 2中的模板中键入cast

Plo*_*log 22 typescript angular

我正在研究一个Angular项目(Angular 4.0.0),我无法将抽象类的属性绑定到ngModel,因为我首先需要将它作为实际的具体类来转换为访问该属性.

即我有一个AbstractEvent类,它有一个具体的实现Event,它有一个布尔属性'acknowigned',我需要通过ngModel双向绑定来设置一个复选框.

我目前在我的DOM中有这个元素:

<input type="checkbox" *ngIf="event.end" [(ngModel)]="(event as Event).acknowledged" 
                                          [disabled]="(event as Event).acknowledged">
Run Code Online (Sandbox Code Playgroud)

不幸的是,这会引发以下错误:

未捕获错误:模板解析错误:分析器错误:缺少预期)在第8列[(事件为事件).已确认]

谷歌搜索似乎表明这可能是因为在模板中使用它时不支持使用'as'?虽然我不确定这一点.

我也无法弄清楚如何在驱动模板的typescript文件中为它编写一个函数,因为这会破坏我需要的ngModel上的双向绑定.

如果有人有办法解决这个问题或正确地在角度模板中进行类型转换,我将非常感激!

Muh*_*BUL 42

如果你不关心类型控制。

在 Angular 8 及更高版本中

[(ngModel)]="$any(event).acknowledged"
Run Code Online (Sandbox Code Playgroud)

来自官方文档:https : //angular.io/guide/template-typecheck#disabling-type-checking-using-any

@Component({
  selector: 'my-component',
  template: '{{$any(person).addresss.street}}'
})
class MyComponent {
  person?: Person;
}
Run Code Online (Sandbox Code Playgroud)

  • 这就像把垃圾藏在地毯下一样。它确实有效,但我们应该很少使用它。 (24认同)

Gün*_*uer 27

这是不可能的,因为Event无法从模板中引用.

(as模板绑定表达式也不支持) 您需要先使其可用:

class MyComponent {
  EventType = Event;
Run Code Online (Sandbox Code Playgroud)

这应该工作

[(ngModel)]="(event as EventType).acknowledged"
Run Code Online (Sandbox Code Playgroud)

更新

class MyComponent {
  asEvent(val) : Event { return val; }
Run Code Online (Sandbox Code Playgroud)

然后用它作为

[(ngModel)]="asEvent(event).acknowledged"
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢让它发挥作用.是的,我不太担心表现.目前正在研究我的一位同事撰写的一种原型项目.将来我可能会试图避免这种需要铸造的设计.再次感谢. (2认同)
  • 有关管道的解决方案,请参阅下面的答案,避免负面性能影响。 (2认同)

Mic*_*Mic 18

如前所述,使用准系统方法调用会对性能产生影响。

更好的方法是使用管道,您可以两全其美。只需定义一个 Cast 管道:

@Pipe({
  name: 'cast',
  pure: true
})
export class CastPipe implements PipeTransform {  
  transform(value: any, args?: any): Event {
    return value;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后在您的模板中,event | cast在需要演员时使用。

这样,更改检测保持高效,并且输入是安全的(当然,考虑到请求的类型更改是合理的)。

不幸的是,由于name属性的原因,我没有看到具有这种通用性的方法,因此您必须为每种类型定义一个新管道。

  • 您可以将其设为通用类,请参阅/sf/answers/4630782411/ (2认同)

smn*_*brv 12

如果您使用类(不是接口!),您可以传递类以从中提取类型。

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
  name: 'as',
  pure: true,
})
export class AsPipe implements PipeTransform {

  transform<T>(value: any, clss: new (...args: any[]) => T): T {
    return value as T;
  }

}
Run Code Online (Sandbox Code Playgroud)

clss 参数未使用,但服务于主要目标:从构造函数推断类型。

可以用作:

class Event {
  prop: string;
}

export class MyComponent {

  MyClass = MyClass; // export class as value, that is visible in the template

}
Run Code Online (Sandbox Code Playgroud)
<td mat-cell *matCellDef="let row">
  {{ (row | as : MyClass).prop }}
</td>
Run Code Online (Sandbox Code Playgroud)

这不适用于接口,因为接口无法传递到模板中(在编写时)。

要使用该接口,您可以将其包装到类中:

class MyClass implements Partial<MyInterface> {}
Run Code Online (Sandbox Code Playgroud)

使用 Angular 11.1 和最新的 Ivy 语言服务进行测试。

  • 在启用“strictTemplates”的 Angular 12 中工作。 (2认同)

Kha*_*ela 9

  • 使用我的TypeSafe泛型答案

  • 并受到smnbbrv 答案的启发,当没有地方可以推断类型时,将类型显式传递为可选参数。

     import { Pipe, PipeTransform } from '@angular/core';
    
     /**
      * Cast super type into type using generics
      * Return Type obtained by optional @param type OR assignment type.
      */
    
     @Pipe({ name: 'cast' })
     export class CastPipe implements PipeTransform {
         /**
          * Cast (S: SuperType) into (T: Type) using @Generics.
          * @param value (S: SuperType) obtained from input type.
          * @optional @param type (T CastingType)
          * type?: { new (): T }
          * type?: new () => T
          */
         transform<S, T extends S>(value: S, type?: new () => T): T {
             return <T>value;
         }
     }
    
    Run Code Online (Sandbox Code Playgroud)

    用法:

    模板.html

     <input
         type="checkbox"
         *ngIf="event.end"
         [(ngModel)]="(event | cast: Event).acknowledged"
         [disabled]="(event | cast: Event).acknowledged"
     />
    
    Run Code Online (Sandbox Code Playgroud)

    组件.ts

     export abstract class AbstractEvent {
         end: boolean;
     }
     export class Event extends AbstractEvent {
         acknowledged: boolean;
     }
    
    
     export class MyComponent{
         event: AbstractEvent;
         Event = Event;
     }
    
    Run Code Online (Sandbox Code Playgroud)


bob*_*603 6

您还可以创建一个返回Type Predicate 的函数。

应用程序组件.html

<some-component *ngIf="isFoo(foo)" [foo]="foo"></some-component>
Run Code Online (Sandbox Code Playgroud)

应用程序组件.ts

isFoo(value: Foo | Bar): value is Foo {
    return value === 'Foo';
}
Run Code Online (Sandbox Code Playgroud)

这会将模板变量转换foo为类型Foo,并消除有关联合类型的任何 strictTemplate 错误。

  • 在某些情况下,这实际上已经足够了,并且避免了添加管道的麻烦。打的好 ! (2认同)

小智 5

要扩展 @smnbbrv 的答案,您可以对接口使用类似的语法,如下所示:

@Pipe({ name: 'as', pure: true })
export class AsPipe implements PipeTransform {
  transform<T>(input: unknown, baseItem: T | undefined): T {
    return (input as unknown) as T;
  }
}
Run Code Online (Sandbox Code Playgroud)

这要求我们提供正确类型的“baseItem”。但是,我们不需要实际创建该项目,我们只需要声明它(因为该项目可以是未定义的)。这意味着我们可以在类中创建建议类型的变量,如下所示:

export interface Person{
  name: string;
  age: number;
}

export class MyComponent {
  Person: Person;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们没有为 分配任何值baseItem,我们只是指定它的类型。如果您已strictPropertyInitialization启用,则需要将非空断言添加到您的baseItem

export class MyComponent {
  Person!: Person;
}
Run Code Online (Sandbox Code Playgroud)

然后可以在您的模板中使用它,如下所示:

<td mat-cell *matCellDef="let row">
  {{ (row | as : Person).name }}
</td>
Run Code Online (Sandbox Code Playgroud)