![](/img/trans.png)
[英]How to declare a variable with a return method as value in Angular?
[英]How to declare a variable in a template in Angular
我有以下模板:
<div>
<span>{{aVariable}}</span>
</div>
并希望以:
<div "let a = aVariable">
<span>{{a}}</span>
</div>
有办法吗?
我们可以创建像*ngIf
这样的指令并将其命名为*ngVar
ng-var.directive.ts
@Directive({
selector: '[ngVar]',
})
export class VarDirective {
@Input()
set ngVar(context: unknown) {
this.context.$implicit = this.context.ngVar = context;
if (!this.hasView) {
this.vcRef.createEmbeddedView(this.templateRef, this.context);
this.hasView = true;
}
}
private context: {
$implicit: unknown;
ngVar: unknown;
} = {
$implicit: null,
ngVar: null,
};
private hasView: boolean = false;
constructor(
private templateRef: TemplateRef<any>,
private vcRef: ViewContainerRef
) {}
}
有了这个*ngVar
指令,我们可以使用以下
<div *ngVar="false as variable">
<span>{{variable | json}}</span>
</div>
或者
<div *ngVar="false; let variable">
<span>{{variable | json}}</span>
</div>
或者
<div *ngVar="45 as variable">
<span>{{variable | json}}</span>
</div>
或者
<div *ngVar="{ x: 4 } as variable">
<span>{{variable | json}}</span>
</div>
也可以看看
角度 v4
div
+ ngIf
+ let
div
+ ngIf
+ as
看法
<div *ngIf="{ a: 1, b: 2, c: 3 + x } as variable">
<span>{{variable.a}}</span>
<span>{{variable.b}}</span>
<span>{{variable.c}}</span>
</div>
组件.ts
export class AppComponent {
x = 5;
}
div
这样的包装器,你可以使用ng-container
看法
<ng-container *ngIf="{ a: 1, b: 2, c: 3 + x } as variable">
<span>{{variable.a}}</span>
<span>{{variable.b}}</span>
<span>{{variable.c}}</span>
</ng-container>
正如@Keith 在评论中提到的
这在大多数情况下都有效,但它不是一个通用的解决方案,因为它依赖于变量的真实性
另一种方法见更新。
您可以使用 Angular 2 中的template
元素或 Angular 4+ 中ng-template
来在 html 代码中声明变量。
模板有一个上下文对象,可以使用let
绑定语法将其属性分配给变量。 请注意,您必须为模板指定一个出口,但它可以是对其自身的引用。
<ng-template #selfie [ngTemplateOutlet]="selfie"
let-a="aVariable" [ngTemplateOutletContext]="{ aVariable: 123 }">
<div>
<span>{{a}}</span>
</div>
</ng-template>
<!-- Output
<div>
<span>123</span>
</div>
-->
您可以通过使用上下文对象的$implicit
属性而不是自定义属性来减少代码量。
<ng-template #t [ngTemplateOutlet]="t"
let-a [ngTemplateOutletContext]="{ $implicit: 123 }">
<div>
<span>{{a}}</span>
</div>
</ng-template>
上下文对象可以是文字对象或任何其他绑定表达式。 其他有效示例:
<!-- Use arbitrary binding expressions -->
<ng-template let-sum [ngTemplateOutletContext]="{ $implicit: 1 + 1 }">
<!-- Use pipes -->
<ng-template let-formatPi [ngTemplateOutletContext]="{ $implicit: 3.141592 | number:'3.1-5' }">
<!-- Use the result of a public method of your component -->
<ng-template let-root [ngTemplateOutletContext]="{ $implicit: sqrt(2116) }">
<!--
You can create an alias for a public property of your component:
anotherVariable: number = 123;
-->
<ng-template let-aliased [ngTemplateOutletContext]="{ $implicit: anotherVariable }">
<!--
The entire context object can be bound from a public property:
ctx: { first: number, second: string } = { first: 123, second: "etc" }
-->
<ng-template let-a="first" let-b="second" [ngTemplateOutletContext]="ctx">
丑陋,但是:
<div *ngFor="let a of [aVariable]">
<span>{{a}}</span>
</div>
与异步管道一起使用时:
<div *ngFor="let a of [aVariable | async]">
<span>{{a.prop1}}</span>
<span>{{a.prop2}}</span>
</div>
更新 3
问题https://github.com/angular/angular/issues/2451在 Angular 4.0.0 中已修复
也可以看看
更新 2
这不受支持。
有模板变量,但不支持分配任意值。 它们只能用于引用它们所应用的元素、指令或组件的导出名称以及结构指令(如ngFor
)的范围变量,
另请参阅https://github.com/angular/angular/issues/2451
更新 1
@Directive({
selector: '[var]',
exportAs: 'var'
})
class VarDirective {
@Input() var:any;
}
并像这样初始化它
<div #aVariable="var" var="abc"></div>
或者
<div #aVariable="var" [var]="'abc'"></div>
并使用变量
<div>{{aVariable.var}}</div>
(未测试)
#aVariable
创建对VarDirective
的引用 ( exportAs: 'var'
)var="abc"
实例化VarDirective
并将字符串值"abc"
传递给它的值输入。aVariable.var
读取分配给var
指令var
输入的值。我建议这样做: https ://medium.com/@AustinMatherne/angular-let-directive-a168d4248138
该指令允许您编写如下内容:
<div *ngLet="'myVal' as myVar">
<span> {{ myVar }} </span>
</div>
这是我编写的一个指令,它扩展了 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 可以是任何有效的局部变量名。
如果要获取函数的响应并将其设置为变量,可以在模板中像下面这样使用它,使用ng-container
避免修改模板。
<ng-container *ngIf="methodName(parameters) as respObject">
{{respObject.name}}
</ng-container>
组件中的方法可以是
methodName(parameters: any): any {
return {name: 'Test name'};
}
如果您需要来自 Angular 语言服务的模板中的自动完成支持:
同步:
myVar = { hello: '' };
<ng-container *ngIf="myVar; let var;">
{{var.hello}}
</ng-container>
使用异步管道:
myVar$ = of({ hello: '' });
<ng-container *ngIf="myVar$ | async; let var;">
{{var.hello}}
</ng-container>
一个适合我要求的简单解决方案是:
<ng-container *ngIf="lineItem.productType as variable">
{{variable}}
</ng-container>
或者
<ng-container *ngIf="'ANY VALUE' as variable">
{{variable}}
</ng-container>
我正在使用Angular 版本: 12 。 似乎它也可以与其他版本一起使用。
我喜欢创建指令来执行此操作的方法(好电话@yurzui)。
我最终找到了一篇 Medium 文章Angular "let" Directive ,它很好地解释了这个问题,并提出了一个自定义的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
}
}
我的主要变化是:
appLet: T
更改为appLet: T | null
appLet: T | null
不知道为什么 Angular 团队不仅制定了官方的 ngLet 指令,还制定了 whatevs。
原始源代码归功于@AustinMatherne
对于那些决定使用结构指令代替*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 语言服务无法确定变量类型,因此模板中没有代码完成。 我希望它会尽快修复。
我正在使用 angular 6x,我最终使用了下面的代码片段。 我有一个场景,我必须从任务对象中找到用户。 它包含用户数组,但我必须选择分配的用户。
<ng-container *ngTemplateOutlet="memberTemplate; context:{o: getAssignee(task) }">
</ng-container>
<ng-template #memberTemplate let-user="o">
<ng-container *ngIf="user">
<div class="d-flex flex-row-reverse">
<span class="image-block">
<ngx-avatar placement="left" ngbTooltip="{{user.firstName}} {{user.lastName}}" class="task-assigned" value="28%" [src]="user.googleId" size="32"></ngx-avatar>
</span>
</div>
</ng-container>
</ng-template>
使用 Angular 12:
<div *ngIf="error$ | async as error">
<span class="text-warn">{{error.message}}</span>
</div>
我正在尝试做类似的事情,看起来这已在较新版本的 angular中得到修复。
<div *ngIf="things.car; let car">
Nice {{ car }}!
</div>
<!-- Nice Honda! -->
对某人有帮助的简短答案
show(lastName: HTMLInputElement){
this.fullName = this.nameInputRef.nativeElement.value + ' ' + lastName.value;
this.ctx.fullName = this.fullName;
}
*但是,您可以使用 ViewChild 装饰器在组件中引用它。
import {ViewChild, ElementRef} from '@angular/core';
在 Component 中引用 firstNameInput 变量
@ViewChild('firstNameInput') nameInputRef: ElementRef;
之后,您可以在组件内的任何位置使用 this.nameInputRef。
使用 ng 模板
在 ng-template 的情况下,它有点不同,因为每个模板都有自己的一组输入变量。
https://stackblitz.com/edit/angular-2-template-reference-variable
我是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));
}
@yurzui 的原始答案从 Angular 9 开始无法工作,因为 - 将angular 8 app 迁移到 9 的奇怪问题。 但是,您仍然可以从ngVar指令中受益,方法是拥有它并像使用它一样
<ng-template [ngVar]="variable">
your code
</ng-template>
虽然它可能会导致 IDE 警告:“变量未定义”
像这样尝试
<ng-container
[ngTemplateOutlet]="foo"
[ngTemplateOutletContext]="{ test: 'Test' }"
></ng-container>
<ng-template #foo let-test="test">
<div>{{ test }}</div>
</ng-template>
它更简单,不需要任何额外的东西。 在我的示例中,我声明变量“open”,然后使用它。
<mat-accordion class="accord-align" #open>
<mat-expansion-panel hideToggle="true" (opened)="open.value=true" (closed)="open.value=false">
<mat-expansion-panel-header>
<span class="accord-title">Review Policy Summary</span>
<span class="spacer"></span>
<a *ngIf="!open.value" class="f-accent">SHOW</a>
<a *ngIf="open.value" class="f-accent">HIDE</a>
</mat-expansion-panel-header>
<mat-divider></mat-divider>
<!-- Quote Details Component -->
<quote-details [quote]="quote"></quote-details>
</mat-expansion-panel>
</mat-accordion>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.