Як оголосити змінну в шаблоні в Angular


201

У мене є такий шаблон:

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

і хотілося б закінчити:

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

Чи є спосіб це зробити?


Мені цікаво знати, що таке вимога / випадок використання для того, щоб змінити ім'я обов'язкового параметра, такого як цей приклад?
LDJ

31
Це просто запобігти повторенню чогось на зразок вкладки [елемент] .val за примірником. Я знаю, що можу вирішити проблему в компоненті, але я просто дивився на те, як це зробити в шаблоні (навіть якщо я не можу закінчити це рішення).
Scipion

2
@LDJ один випадок використання зразка: ефективність. Використовуйте зразок stackblitz.com/angular/… <mat-checkbox [ check ] = "descendantsAllSelected (node)" [indeterminate] = "descendantsParicallySelected (node)" (change) = "todoItemSelectionToggle (node)"> {{node. item}} </mat-checkbox> насправді нащадкиParicallySelected () викликає нащадківAllSelected (). Це означає, що колись нащадків. AllSelected називається двічі. Якщо є локальна змінна, цього можна уникнути.
Стівен.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 userяк у відповіді юрзуї .
CPHPython

Відповіді:


174

Оновлення

Ми можемо просто створити директиву як *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>

Приклад Plunker 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>

компонент.ц

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>

Як згадував @Keith у коментарях

це працюватиме в більшості випадків, але це не є загальним рішенням, оскільки він покладається на те, що змінна є неправдоподібною

Дивіться оновлення для іншого підходу.


10
це спрацює в більшості випадків, але це не є загальним рішенням, оскільки він покладається на variableте, що він є правдою
Кіт,

6
@Keith Дякую, що вказали на це. Ви можете подивитися на мою оновлену відповідь
yurzui

3
Це має бути нова відповідь, оскільки вона 1) є більш сучасним, ніж інше рішення 2) узагальнює запити на витяг, пов'язані в поточній відповіді, і економить багато часу 3) включає приклади вбудовані, а не по зовнішньому посиланню. Дякуємо, що показали це. AFAIK будь-який обернутий предмет {}оцінюватиметься як простий, тому це рішення є досить надійним.
kvanberendonck

4
Наприклад, кнопки, що розгортаються: *ngIf="{ expanded: false } as scope"а потім, якщо ви використовуєте завантажувальний інструмент, ви можете просто використовувати [ngClass]="{ 'in': scope.expanded }"та (click)="scope.expanded = !scope.expanded"замість цього додати що-небудь до js/ tsфайлів.
kvanberendonck

1
пов’язана проблема з github (вказується на використання простого *ngIfзамість користувальницьких матеріалів ngvar): github.com/angular/angular/isissue/14985
phil294

80

Некрасиво, але:

<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>ним служить роботі цілком правильно!
Анжелос Пікулас

2
У разі *ngForмайте на увазі, що весь вкладений вміст буде відтворено, якщо змінна величина зміниться, поки ви не вкажете trackByфункцію, яка повертає однаковий ідентифікатор для всіх значень.
Валерій Катков

74

Ви можете оголосити змінні в коді html, використовуючи templateелемент у Angular 2 або ng-templateв Angular 4+.

У шаблонах є контекстний об'єкт, властивості якого можуть бути призначені змінним за допомогою 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>в кутовій 2. Ви можете використовувати або <template>або <ng-template>в Angular 4, але ви повинні використовувати тільки <ng-template>. Кутова 5 впала підтримка для <template>.
Стівен Лійкенс

Для чого це t?
matttm

1
@matttm #t- це змінна шаблон, який зберігає ng-template. Він використовується [ngTemplateOutlet]="t"для виготовлення посилання на ng-шаблон.
Стівен

Це химерно, але це працює! Кутовий повинен зробити це простішим із вбудованою змінною директивою. Дякую.
TetraDev

57

оновлення 3

Випуск 2451 зафіксовано у Angular 4.0.0

Дивитися також

оновлення 2

Це не підтримується.

Існують змінні шаблонів, але вони не підтримуються для призначення довільних значень. Їх можна використовувати лише для позначення елементів, до яких вони застосовуються, експортованих назв директив або компонентів та змінних областей для структурних директив, наприклад ngFor,

Дивіться також https://github.com/angular/angular/isissue/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.

Чи не вдалося б створити структурну директиву для цього?
Scipion

Якщо вам це потрібно неодноразово, то директива може робити те, що ви хочете. Структурна директива створює власний погляд, це, мабуть, не те, що ви хочете.
Günter Zöchbauer

1
@ GünterZöchbauer, дуже хороший матеріал. Я знаю, що, ймовірно, є кращою практикою обчислювати / готувати змінні у component.tsфайлі. Але мені набагато простіше, якщо я їх переглядаю в деяких випадках через схему синхронізації, яку я впроваджую протягом усього додатка. Я користуюсь контрольними правилами javascript, коли різні змінні вказують на один і той же об'єкт.
AmmarCSE

Я отримую на зразок помилки There is no directive with "exportAs" set to "var". Чи може хтось, будь ласка, сказати мені, яку помилку я зробив? Я використав вищевказану директиву.
Partha Sarathi Ghosh

Можливо , ви не додати директиву declarations: [...]про @NgModule(). Якщо це не проблема, будь ласка, створіть нове запитання та введіть код, який дозволяє діагностувати проблему.
Günter 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щоб обійти це питання.
Чарлі

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 :

Синхронні:

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>

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

Це набагато простіше, не потрібно нічого додаткового. У своєму прикладі я оголошую змінну "відкритою", а потім використовую її.

   <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 для тимчасового зберігання значення.
Джек Рус

Дивовижно! Мені довелося скористатися, "?"оскільки у мене з'явилося повідомлення "Значення ідентифікатора" не визначено ", як це =>" відкрити?
А. Морель

1

Мені сподобався підхід створення директиви для цього (гарний дзвінок @yurzui).

Я в кінцевому підсумку знайшов середню статтю Angular "пускаю" директиву, яка добре пояснює цю проблему, і пропоную користувальницьку директиву дозволити, яка в кінцевому підсумку працювала чудово для мого використання з мінімальними змінами коду.

Ось суть (на момент публікації) з моїми модифікаціями:

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


Це був мій улюблений підхід на сторінці, і він працював на мене.
Skychan

1

Коротка відповідь, яка допомагає комусь

  • Змінна посилання на шаблон часто посилається на елемент DOM у шаблоні.
  • Також посилання на кутовий або веб-компонент та директиву.
  • Це означає, що ви можете легко отримати доступ до змінної в будь-якому місці шаблону

введіть тут опис зображення

введіть тут опис зображення

  • Оголосити опорну змінну за допомогою хеш-символу (#)
  • Може передати змінну як параметр події

введіть тут опис зображення

  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-шаблоном

У випадку з ng-шаблоном він дещо інший, тому що кожен шаблон має свій набір змінних вводу.

введіть тут опис зображення

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


1

Для тих, хто вирішив використовувати структурну директиву в якості заміни *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 не може з'ясувати тип змінної, тому в шаблонах немає заповнення коду. Я сподіваюся, що це скоро буде виправлено.


Це працює, але інтелігенція - ні. Я використовую кутовий 8.
Tx_monster
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.