我有以下模板:

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

并希望以:

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

有办法吗?


当前回答

像这样试试

<ng-container
     [ngTemplateOutlet]="foo"
     [ngTemplateOutletContext]="{ test: 'Test' }"
></ng-container>

<ng-template #foo let-test="test">
    <div>{{ test }}</div>
</ng-template>

其他回答

@yurzui的原始答案将不能从Angular 9开始工作,因为一个奇怪的问题,将Angular 8应用程序迁移到9。 然而,你仍然可以从ngVar指令中受益

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

尽管它可能会导致IDE警告:“变量未定义”

更新

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

or

<div *ngVar="false; let variable">
    <span>{{variable | json}}</span>
</div>

or

<div *ngVar="45 as variable">
    <span>{{variable | json}}</span>
</div>

or

<div *ngVar="{ x: 4 } as variable">
    <span>{{variable | json}}</span>
</div>

Angular4 ngVar

另请参阅

Angular 4在哪里为*ngIf定义了“as local-var”行为?

原来的答案

角v4

div + ngIf + let {{variable.a}} {{variable.b}} div + ngIf + as

view

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

如果你不想创建像div这样的包装器,你可以使用ng-container

view

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

这将在大多数情况下工作,但它不是一个普遍的解决方案,因为它 依赖于变量为真

有关另一种方法,请参阅update。

对于那些决定使用结构指令代替*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,我最终使用了下面的代码片段。 我有一个需要从任务对象中查找user的场景。它包含用户数组,但我必须选择分配的用户。

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

我是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));
}