【问题标题】:How to declare a variable in a template in Angular如何在 Angular 的模板中声明变量
【发布时间】:2016-11-29 15:41:42
【问题描述】:

我有以下模板:

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

并希望最终得到:

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

有办法吗?

【问题讨论】:

  • 我很想知道想要更改绑定参数的名称(例如此示例)的要求/用例是什么?
  • 这只是为了防止通过实例重复 tab[element].val 之类的东西。我知道我可以在组件中解决问题,但我只是在看如何在模板中执行(即使我最终可能不会得到那个解决方案)。
  • @LDJ 一个示例用例:效率。使用stackblitz.com/angular/…的示例{{node.item}} 实际上,descendantsPartiallySelected() 调用了 descendantsAllSelected()。这意味着有时 descendantsAllSelected 被调用两次。如果有局部变量,则可以避免。
  • &lt;div *ngIf="{name:'john'} as user1; let user"&gt; &lt;i&gt;{{user1|json}}&lt;/i&gt; &lt;i&gt;{{user|json}}&lt;/i&gt; &lt;/div&gt;
  • @dasfdsa 我相信user1 === user,因此您要么使用*ngIf="{name:'john'} as user1 要么使用*ngIf="{name:'john'};let user,如yurzui's answer

标签: html angular


【解决方案1】:

更新

我们可以创建类似*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>

Plunker Example Angular4 ngVar

另见

原答案

Angular v4

  1. div + ngIf + let

    {{变量.a}} {{变量.b}}
  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;
}
  1. 如果你不想创建像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 在 cmets 中提到的那样

这在大多数情况下都有效,但它不是一个通用的解决方案,因为它 依赖于变量的真实性

查看更新以了解另一种方法。

【讨论】:

  • 这在大多数情况下都有效,但它不是一个通用的解决方案,因为它依赖于 variable 的真实性
  • @Keith 感谢您指出这一点。你可以看看我更新的答案
  • 这应该是新的答案,因为它 1) 比其他解决方案更现代 2) 总结当前答案中链接的拉取请求并节省大量时间 3) 包括示例内联而不是通过外部链接。感谢您展示这一点。 AFAIK 包裹在 {} 中的任何对象都将评估为真,因此该解决方案相当稳健。
  • 例如,可扩展按钮:*ngIf="{ expanded: false } as scope",然后如果您使用引导程序,您可以只使用[ngClass]="{ 'in': scope.expanded }"(click)="scope.expanded = !scope.expanded",而不是向您的js/ts 文件添加任何内容.
  • 由于 Angular 的命名空间,我不会使用 ngVar。
【解决方案2】:

您可以使用 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">

【讨论】:

  • 是的,你只能在 Angular 2 中使用 &lt;template&gt;。你可以在 Angular 4 中使用 &lt;template&gt;&lt;ng-template&gt;,但你只能使用 &lt;ng-template&gt;。 Angular 5 放弃了对 &lt;template&gt; 的支持。
  • @matttm #t 是一个模板变量,用于存储ng-template。在[ngTemplateOutlet]="t" 中使用它来使 ng-template 引用自身。
  • 这很奇怪,但它有效! Angular 应该使用内置的变量指令使这更简单。谢谢。
  • @JohnWhite 有没有可能你没有添加&lt;ng-template #t [ngTemplateOutlet]="t" ...&gt;?这就是为什么您可能会看到像 Can't bind to 'ngTemplateOutletContext' since it isn't a known property 这样的错误的原因之一。
  • @Krishnan 这会像任何其他绑定表达式一样受到您的更改检测策略的影响。因此,isExpanded(node) 会针对变更检测器的每次运行进行评估。在这方面没有真正的改进,除非您在模板中多次绑定结果。出于显而易见的原因,多次取消引用变量比多次评估函数更有效。
【解决方案3】:

丑,但是:

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

【讨论】:

  • 这是我本能想到的——它也适用于*ngFor="let a of [(someStream$ | async).someA]。我想与&lt;ng-container&gt; 一起使用它非常适合这项工作!
  • 如果是*ngFor,请记住,如果变量值发生更改,所有嵌套内容都将重新创建,直到您指定一个trackBy 函数,它为所有值返回相同的ID。
  • 太好了,终于找到了重用相同Componentexpression的方法!如果你真的不需要&lt;div&gt;,而只想重用Component 的成员/道具,请使用ng-container,例如——&lt;ng-container *ngFor="let DRYItUp of [this.myComponentProp]"&gt;
  • 我使用这种方法的最终解决方案是在 html 上:*ngFor="let var of [myBoolRule]; trackBy:trackFn",在 ts 上:trackFn() { return true; }。我想你在这个函数上返回什么并不重要,它只需要是一个常量
【解决方案4】:

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

【讨论】:

  • 难道不能创建一个结构指令来做到这一点吗?
  • 如果你反复需要这个,那么一个指令可能会做你想做的事。结构指令创建它自己的视图,这可能不是你想要的。
  • @GünterZöchbauer,非常好的东西。我知道在component.ts 文件中计算/准备变量可能是一种更好的做法。但是由于我在整个应用程序中实施的同步方案,在某些情况下,我更容易将它们放在视图中。当不同的变量指向同一个对象时,我正在利用 javascript 引用规则。
  • 我收到类似There is no directive with "exportAs" set to "var" 的错误。谁能告诉我我犯了什么错误?我已经使用了上述指令。
  • 也许您没有将指令添加到declarations: [...]@NgModule()。如果这不是问题,请创建一个新问题并提供允许诊断问题的代码。
【解决方案5】:

我建议:https://medium.com/@AustinMatherne/angular-let-directive-a168d4248138

此指令允许您编写如下内容:

<div *ngLet="'myVal' as myVar">
  <span> {{ myVar }} </span>
</div>

【讨论】:

  • 像这样,但 * 是为结构指令保留的,这不是,反正 +1
【解决方案6】:

这是我编写的一个指令,它扩展了 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 以解决此问题。
【解决方案7】:

如果你想得到一个函数的响应并将其设置成一个变量,你可以在模板中像下面这样使用它,使用ng-container避免修改模板。

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

组件中的方法可以是这样的

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

【讨论】:

  • 我知道这是旧的,但如果有人在读这个:不要这样做;不要在模板中编写函数调用(事件除外)。该函数将在每个生命周期中执行,因为 Angular 无法检测到变化
【解决方案8】:

如果您需要来自 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>

【讨论】:

    【解决方案9】:

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

    我最终找到了一篇 Medium 文章 Angular "let" Directive,它很好地解释了这个问题,并提出了一个自定义的 let directive,它最终对我的用例非常有用,而且代码更改最少。

    这是我修改的要点(在发布时):

    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 指令,还制定了 whatevs。

    原始源代码归功于@AustinMatherne

    【讨论】:

    • 这是我最喜欢的页面方法,它对我有用。
    【解决方案10】:

    对于那些决定使用结构指令代替*ngIf 的人,请记住指令上下文默认情况下不进行类型检查。要创建类型安全指令ngTemplateContextGuard,应添加属性,请参阅Typing the directive's context。例如:

    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一样使用,除了它可以存储false值:

    <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 语言服务无法确定变量类型,因此模板中没有代码完成。我希望它会尽快修复。

    【讨论】:

    • 这有效,但智能感知无效。我正在使用角度 8。
    【解决方案11】:

    满足我要求的简单解决方案是:

     <ng-container *ngIf="lineItem.productType as variable">
           {{variable}}
     </ng-container>
    

     <ng-container *ngIf="'ANY VALUE' as variable">
           {{variable}}
      </ng-container>
    

    我正在使用 Angular 版本:12。它似乎也可以与其他版本一起使用。

    【讨论】:

      【解决方案12】:

      对某人有帮助的简答题

      • 模板引用变量通常引用一个 DOM 元素 模板。
      • 还引用了 Angular 或 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

      【讨论】:

        【解决方案13】:

        我使用的是 angular 6x,我最终使用了下面的 sn-p。 我有一个场景,我必须从任务对象中找到用户。它包含用户数组,但我必须选择分配的用户。

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

        【讨论】:

          【解决方案14】:

          使用 Angular 12:

            <div *ngIf="error$ | async as error">
               <span class="text-warn">{{error.message}}</span>
             </div>
          

          【讨论】:

            【解决方案15】:

            我正在尝试做类似的事情,看起来这已在 newer versions of angular 中修复。

                <div *ngIf="things.car; let car">
                  Nice {{ car }}!
                </div>
                <!-- Nice Honda! -->
            

            【讨论】:

            • 这很好,但我认为它更像是一种解决方法,一个巧妙的技巧,而不是一个真正的解决方案。因为声明变量也应该是可能的,IMO,没有条件。因为副作用是在这种情况下,没有汽车就不会渲染元素。但是,如果我想在模板中声明并重复使用多次的变量是可选的呢?结构 if 指令建议 2 个“模板”路径。但在我的情况下,模板总是一样的,因为没有条件。
            【解决方案16】:

            @yurzui 的原始答案由于 - strange problem migrating angular 8 app to 9 而无法从 Angular 9 开始。 但是,您仍然可以从 ngVar 指令中受益,方法是拥有它并像使用它一样

            <ng-template [ngVar]="variable">
            your code
            </ng-template>
            

            虽然它可能会导致 IDE 警告:“变量未定义

            【讨论】:

              【解决方案17】:

              它更简单,不需要任何额外的东西。在我的示例中,我声明变量“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,确切地说,我正在使用 ElementRef 临时存储一个值。
              • 太棒了!我不得不使用"?",因为我收到了这样的消息“标识符'值'未定义”=>“open?.value”但它正在工作!
              猜你喜欢
              • 2018-08-04
              • 1970-01-01
              • 1970-01-01
              • 2016-04-15
              • 1970-01-01
              • 1970-01-01
              • 2020-05-10
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多