繁体   English   中英

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>

Plunker 示例 Angular4 ngVar

也可以看看

原始答案

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

组件.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 在评论中提到的

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

另一种方法见更新。

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

我的主要变化是:

  • 将前缀从“ng”更改为“app”(您应该使用应用的自定义前缀)
  • 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! -->

对某人有帮助的简短答案

  • 模板引用变量通常引用模板中的 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';

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM