如何在Angular的模板中声明变量


198

我有以下模板:

<div>
  <span>{{aVariable}}</span>
</div>

并最终以:

<div "let a = aVariable">
  <span>{{a}}</span>
</div>

有办法吗?


我想知道想要更改绑定参数名称(例如本示例)的要求/用例是什么?
LDJ

29
只是为了防止实例重复诸如tab [element] .val之类的内容。我知道我可以解决组件中的问题,但我只是在研究如何在模板中进行操作(尽管我可能最终不会获得该解决方案)。
Scipion

2
@LDJ一个示例用例:效率。使用stackblitz.com/angular/…的示例 。 item}} </ mat-checkbox>实际上,despantsantsPartiallySelected()调用了despantsantsAllSelected()。这意味着某个时间后裔AllSelected被调用两次。如果存在局部变量,则可以避免。
Steven.Xi

3
<div *ngIf="{name:'john'} as user1; let user"> <i>{{user1|json}}</i> <i>{{user|json}}</i> </div>
dasfdsa

@dasfdsa我相信user1 === user,这样你要么做*ngIf="{name:'john'} as user1*ngIf="{name:'john'};let useryurzui的答案
CPHPython

Answers:


167

更新资料

我们可以像这样创建指令*ngIf并调用它*ngVar

ng-var.directive.ts

@Directive({
    selector: '[ngVar]',
})
export class VarDirective {
  @Input()
  set ngVar(context: any) {
    this.context.$implicit = this.context.ngVar = context;
    this.updateView();
  }

  context: any = {};

  constructor(private vcRef: ViewContainerRef, private templateRef: TemplateRef<any>) {}

  updateView() {
    this.vcRef.clear();
    this.vcRef.createEmbeddedView(this.templateRef, this.context);
  }
}

通过此*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>

柱塞示例Angular4 ngVar

也可以看看

原始答案

角v4

1)div+ ngIf+let

<div *ngIf="{ a: 1, b: 2 }; let variable">
  <span>{{variable.a}}</span>
  <span>{{variable.b}}</span>
</div>

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

component.ts

export class AppComponent {
  x = 5;
}

3)如果您不想创建包装器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>

正如@基思在评论中提到的

这在大多数情况下都可以使用,但这不是通用解决方案,因为它依赖于变量是否为真

有关其他方法,请参见更新。


10
这在大多数情况下都可以使用,但这不是一个通用的解决方案,因为它依赖于variable真实性
Keith

6
@Keith感谢您指出这一点。您可以看看我的最新答案
yurzui

3
这应该是新答案,因为它1)比其他解决方案更现代2)总结了当前答案中链接的请求请求,并节省了大量时间3)包括内联示例,而不是外部链接。感谢您展示此内容。AFAIK任何包裹在其中的对象都{}将评估为真实,因此该解决方案非常可靠。
kvanberendonck '17

4
例如,可扩展按钮:*ngIf="{ expanded: false } as scope"然后,如果您正在使用引导程序,则可以使用[ngClass]="{ 'in': scope.expanded }"(click)="scope.expanded = !scope.expanded"不是向js/ ts文件中添加任何内容。
kvanberendonck '17

1
相关的github问题(指出使用简单*ngIf而不是自定义的ngvar东西):github.com/angular/angular/issues/14985
phil294

79

丑陋,但是:

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

4
那是我本能地想到的-它也可以使用*ngFor="let a of [(someStream$ | async).someA]。我想与<ng-container>它一起使用可以很好地完成工作!
Angelos Pikoulas

1
对于*ngFor,请注意,如果变量值更改,将重新创建所有嵌套的内容,直到您指定一个trackBy对所有值返回相同ID 的函数为止。
Valeriy Katkov

71

您可以使用templateAngular 2或ng-templateAngular 4+中的元素在html代码中声明变量。

模板具有上下文对象,可以使用let绑定语法将其属性分配给变量。请注意,您必须为模板指定出口,但是它可以作为对自身的引用。

<ng-template let-a="aVariable" [ngTemplateOutletContext]="{ aVariable: 123 }" [ngTemplateOutlet]="selfie" #selfie>
  <div>
    <span>{{a}}</span>
  </div>
</ng-template>

<!-- Output
<div>
  <span>123</span>
</div>
-->

您可以通过使用$implicit上下文对象的属性而不是自定义属性来减少代码量。

<ng-template let-a [ngTemplateOutletContext]="{ $implicit: 123 }" [ngTemplateOutlet]="t" #t>
  <div>
    <span>{{a}}</span>
  </div>
</ng-template>

上下文对象可以是文字对象或任何其他绑定表达式。当用圆括号括起来时,连管道都似乎起作用。

有效的示例ngTemplateOutletContext

  • [ngTemplateOutletContext]="{ aVariable: 123 }"
  • [ngTemplateOutletContext]="{ aVariable: (3.141592 | number:'3.1-5') }"
  • [ngTemplateOutletContext]="{ aVariable: anotherVariable }" 与...一起使用 let-a="aVariable"
  • [ngTemplateOutletContext]="{ $implicit: anotherVariable }" 与...一起使用 let-a
  • [ngTemplateOutletContext]="ctx"ctx公共财产在哪里

要使其正常工作,我必须将您的代码从“ <template ...”更改为“ <ng-template ...”。
Humppakäräjät

2
是的,您只能<template>在Angular 2中使用。可以使用<template><ng-template>在Angular 4中使用,但只能使用<ng-template>。Angular 5放弃了对的支持<template>
史蒂文·里肯斯

有什么t
matttm

1
@matttm #t是存储的模板变量ng-template。它用于[ngTemplateOutlet]="t"制作ng-template引用本身。
Steven Liekens

57

更新3

问题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创建对VarDirectiveexportAs: 'var')的引用
  • var="abc"实例化VarDirective并将字符串值传递"abc"给它的值输入。
  • aVariable.var读取分配给var指令var输入的值。

创建结构指令是否可以这样做?
Scipion

如果您反复需要此命令,那么指令可能会执行您想要的操作。结构化指令创建它自己的视图,这可能不是您想要的。
君特Zöchbauer

1
@GünterZöchbauer,非常棒的东西。我知道在component.ts文件中计算/准备变量可能是更好的做法。但是由于我正在整个应用程序中实施的同步方案,在某些情况下,将它们显示在视图中对我来说要容易得多。当不同的变量指向同一对象时,我会利用javascript参考规则。
AmmarCSE

我收到类似的错误There is no directive with "exportAs" set to "var"。谁能告诉我我犯了什么错误?我已经使用了上面的指令。
Partha Sarathi Ghosh'2

也许你没有的指令添加到declarations: [...]@NgModule()。如果这不是问题,请创建一个新问题并提供可以诊断问题的代码。
君特Zöchbauer

11

这是我写的指令,它扩展了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可以是任何有效的局部变量名称。


不能按原样传递“生产”版本(IDE也显示为错误)。添加[key: string]: any;Class来解决这个问题。
查理(Charly)


7

如果要获取函数的响应并将其设置为变量,则可以像在模板中一样使用它,ng-container以免修改模板。

<ng-container *ngIf="methodName(parameters) as respObject">
  {{respObject.name}}
</ng-container>

组件中的方法可能类似于

methodName(parameters: any): any {
  return {name: 'Test name'};
}

5

如果您需要Angular Language Service模板中的自动完成支持

同步:

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>

3

这要简单得多,不需要其他任何东西。在我的示例中,我声明了变量“ 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>

您命名标签时,它不是在声明变量
Amirreza

1
@Amirreza,确切地说,我正在使用ElementRef临时存储值。
杰克·鲁斯

太棒了!我不得不使用,"?"因为这样的消息是“未定义标识符'value'” =>“ open?.value”,但是它可以正常工作!
A. Morel

2

我使用的是角度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>

1

我喜欢创建指令来执行此操作的方法(调用@yurzui好)。

我最终找到了一个中型文章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: TappLet: T | null

不知道为什么Angular团队不仅制定了正式的ngLet指令,还制定了什么。

原始源代码归功于@AustinMatherne


这是我在页面上最喜欢的方法,对我有用。
Skychan

1

简短答案,对某人有帮助

  • 模板引用变量通常引用模板中的DOM元素。
  • 另请参阅角度或Web组件和指令。
  • 这意味着您可以轻松地访问模板中任何地方的变量

在此处输入图片说明

在此处输入图片说明

  • 使用井号(#)声明参考变量
  • 能够在事件中将变量作为参数传递

在此处输入图片说明

  show(lastName: HTMLInputElement){
    this.fullName = this.nameInputRef.nativeElement.value + ' ' + lastName.value;
    this.ctx.fullName = this.fullName;
  }

*但是,您可以使用ViewChild装饰器在组件内部对其进行引用。

import {ViewChild, ElementRef} from '@angular/core';

在组件内部引用firstNameInput变量

@ViewChild('firstNameInput') nameInputRef: ElementRef;

之后,可以在组件内部的任何位置使用this.nameInputRef。

使用ng-template

就ng-template而言,它有点不同,因为每个模板都有自己的输入变量集。

在此处输入图片说明

https://stackblitz.com/edit/angular-2-template-reference-variable


0

对于那些决定使用结构化指令代替的人*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 Language Service无法确定变量类型,因此模板中没有代码完成。我希望它将尽快修复。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.