我有以下模板:
<div>
<span>{{aVariable}}</span>
</div>
并希望以:
<div "let a = aVariable">
<span>{{a}}</span>
</div>
有办法吗?
我有以下模板:
<div>
<span>{{aVariable}}</span>
</div>
并希望以:
<div "let a = aVariable">
<span>{{a}}</span>
</div>
有办法吗?
当前回答
我喜欢创建一个指令来做这件事的方法(很好的调用@yurzui)。
最后我在Medium上找到了一篇文章,Angular的“let”指令很好地解释了这个问题,并提出了一个自定义的let指令,它在我的用例中工作得很好,代码更改很少。
以下是要点(在发布时)和我的修改:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'
interface LetContext <T> {
appLet: T | null
}
@Directive({
selector: '[appLet]',
})
export class LetDirective <T> {
private _context: LetContext <T> = { appLet: null }
constructor(_viewContainer: ViewContainerRef, _templateRef: TemplateRef <LetContext <T> >) {
_viewContainer.createEmbeddedView(_templateRef, this._context)
}
@Input()
set appLet(value: T) {
this._context.appLet = value
}
}
我的主要改变是:
将前缀从'ng'更改为'app'(你应该使用你的应用程序的自定义前缀是什么) 将appLet: T更改为appLet: T | null
不知道为什么Angular团队还没有发布一个官方的ngLet指令。
原始源代码归功于@AustinMatherne
其他回答
如果你需要Angular语言服务中模板内部的自动补全支持:
同步:
myVar = { hello: '' };
<ng-container *ngIf="myVar; let var;">
{{var.hello}}
</ng-container>
使用async管道:
myVar$ = of({ hello: '' });
<ng-container *ngIf="myVar$ | async; let var;">
{{var.hello}}
</ng-container>
一个简单的解决方案可以满足我的需求:
<ng-container *ngIf="lineItem.productType as variable">
{{variable}}
</ng-container>
OR
<ng-container *ngIf="'ANY VALUE' as variable">
{{variable}}
</ng-container>
我使用的是Angular版本:12。这似乎也适用于其他版本。
下面是我编写的一个指令,它扩展了exportAs装饰器参数的使用,并允许您使用字典作为局部变量。
import { Directive, Input } from "@angular/core";
@Directive({
selector:"[localVariables]",
exportAs:"localVariables"
})
export class LocalVariables {
@Input("localVariables") set localVariables( struct: any ) {
if ( typeof struct === "object" ) {
for( var variableName in struct ) {
this[variableName] = struct[variableName];
}
}
}
constructor( ) {
}
}
你可以在模板中使用它:
<div #local="localVariables" [localVariables]="{a: 1, b: 2, c: 3+2}">
<span>a = {{local.a}}</span>
<span>b = {{local.b}}</span>
<span>c = {{local.c}}</span>
</div>
当然,#local可以是任何有效的局部变量名。
对于那些决定使用结构指令代替*ngIf的人,请记住,默认情况下指令上下文不进行类型检查。要创建一个类型安全的指令,应该添加ngTemplateContextGuard属性,请参见输入指令的上下文。例如:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
// don't use 'ng' prefix since it's reserved for Angular
selector: '[appVar]',
})
export class VarDirective<T = unknown> {
// https://angular.io/guide/structural-directives#typing-the-directives-context
static ngTemplateContextGuard<T>(dir: VarDirective<T>, ctx: any): ctx is Context<T> {
return true;
}
private context?: Context<T>;
constructor(
private vcRef: ViewContainerRef,
private templateRef: TemplateRef<Context<T>>
) {}
@Input()
set appVar(value: T) {
if (this.context) {
this.context.appVar = value;
} else {
this.context = { appVar: value };
this.vcRef.createEmbeddedView(this.templateRef, this.context);
}
}
}
interface Context<T> {
appVar: T;
}
该指令可以像*ngIf一样使用,除了它可以存储假值:
<ng-container *appVar="false as value">{{value}}</ng-container>
<!-- error: User doesn't have `nam` property-->
<ng-container *appVar="user as user">{{user.nam}}</ng-container>
<ng-container *appVar="user$ | async as user">{{user.name}}</ng-container>
与*ngIf相比,唯一的缺点是Angular语言服务无法识别变量类型,因此模板中没有代码补全功能。我希望它能很快修好。
我是https://www.npmjs.com/package/ng-let的作者
将数据作为局部变量共享到html组件模板的结构指令。
源代码:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
interface NgLetContext<T> {
ngLet: T;
$implicit: T;
}
@Directive({
// tslint:disable-next-line: directive-selector
selector: '[ngLet]'
})
export class NgLetDirective<T> {
private context: NgLetContext<T | null> = { ngLet: null, $implicit: null };
private hasView: boolean = false;
// eslint-disable-next-line no-unused-vars
constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef<NgLetContext<T>>) { }
@Input()
set ngLet(value: T) {
this.context.$implicit = this.context.ngLet = value;
if (!this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef, this.context);
this.hasView = true;
}
}
/** @internal */
public static ngLetUseIfTypeGuard: void;
/**
* Assert the correct type of the expression bound to the `NgLet` input within the template.
*
* The presence of this static field is a signal to the Ivy template type check compiler that
* when the `NgLet` structural directive renders its template, the type of the expression bound
* to `NgLet` should be narrowed in some way. For `NgLet`, the binding expression itself is used to
* narrow its type, which allows the strictNullChecks feature of TypeScript to work with `NgLet`.
*/
static ngTemplateGuard_ngLet: 'binding';
/**
* Asserts the correct type of the context for the template that `NgLet` will render.
*
* The presence of this method is a signal to the Ivy template type-check compiler that the
* `NgLet` structural directive renders its template with a specific context type.
*/
static ngTemplateContextGuard<T>(dir: NgLetDirective<T>, ctx: any): ctx is NgLetContext<Exclude<T, false | 0 | '' | null | undefined>> {
return true;
}
}
用法:
import { Component } from '@angular/core';
import { defer, Observable, timer } from 'rxjs';
@Component({
selector: 'app-root',
template: `
<ng-container *ngLet="timer$ | async as time"> <!-- single subscription -->
<div>
1: {{ time }}
</div>
<div>
2: {{ time }}
</div>
</ng-container>
`,
})
export class AppComponent {
timer$: Observable<number> = defer(() => timer(3000, 1000));
}