Jak zadeklarować zmienną w szablonie w Angular


201

Mam następujący szablon:

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

i chciałbym skończyć z:

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

Czy jest na to sposób?


Jestem zainteresowany, aby dowiedzieć się, jaki jest wymóg / przypadek użycia, aby chcieć zmienić nazwę parametru wiązania, takiego jak ten przykład?
— LDJ

31
Ma to na celu uniknięcie powtarzania czegoś takiego jak tab [element] .val według instancji. Wiem, że mogę rozwiązać problem w komponencie, ale właśnie zastanawiałem się, jak to zrobić w szablonie (nawet jeśli nie mogę tego rozwiązać).
— Scipion

2
@LDJ jeden przykładowy przypadek użycia: wydajność. Użyj próbki stackblitz.com/angular/… <mat-checkbox [zaznaczone] = "descendantsAllSelected (node)" [indeterminate] = "descendantsPartIALSelected (node)" (change) = "todoItemSelectionToggle (node)"> {{node. item}} </mat-checkbox> w rzeczywistości descendantsPartIALSelected () wywołuje descendantsAllSelected (). Oznacza to, że kiedyś potomkowie AllSelected są wywoływani dwukrotnie. Jeśli istnieje zmienna lokalna, można tego uniknąć.
— Steven.Xi

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

@dasfdsa Wierzę user1 === user, więc albo to robisz, *ngIf="{name:'john'} as user1albo *ngIf="{name:'john'};let userjak w odpowiedzi yurzui .
— CPHPython

Odpowiedzi:


174

Aktualizacja

Możemy po prostu stworzyć taką dyrektywę *ngIfi ją nazwać*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);
  }
}

dzięki tej *ngVardyrektywie możemy zastosować następujące

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

lub

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

lub

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

lub

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

Przykład plunkera Angular4 ngVar

Zobacz też

Oryginalna odpowiedź

Angular 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

widok

<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) Jeśli nie chcesz tworzyć opakowania, divktórego możesz użyćng-container

widok

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

Jak wspomniano w komentarzach @Keith

zadziała to w większości przypadków, ale nie jest to ogólne rozwiązanie, ponieważ polega na tym, że zmienna jest prawdomówna

Zobacz aktualizację dla innego podejścia.


10
to zadziała w większości przypadków, ale nie jest to ogólne rozwiązanie, ponieważ polega na variablebyciu prawdomównym
— Keith

6
@ Keith Dzięki za zwrócenie na to uwagi. Możesz
— rzucić

3
To powinna być nowa odpowiedź, ponieważ 1) jest nowocześniejsza niż inne rozwiązanie 2) podsumowuje żądania ściągania powiązane z bieżącą odpowiedzią i oszczędza dużo czasu 3) zawiera przykłady zamiast wbudowanego łącza zewnętrznego. Dzięki za pokazanie tego. AFAIK jakikolwiek zawinięty obiekt {}oceni prawdę, więc to rozwiązanie jest dość solidne.
— kvanberendonck

4
Na przykład przyciski rozwijane: *ngIf="{ expanded: false } as scope"a jeśli używasz bootstrap, możesz po prostu użyć [ngClass]="{ 'in': scope.expanded }"i (click)="scope.expanded = !scope.expanded"zamiast dodawać cokolwiek do js/ tsplików.
— kvanberendonck

1
powiązany problem *ngIfz githubem (zwraca uwagę na proste, a nie niestandardowe rzeczy związane z Ngvar): github.com/angular/angular/angular/issues/14985
— phil294

80

Brzydkie, ale:

<div *ngFor="let a of [aVariable]">
  <span>{{a}}</span>
</div>

W połączeniu z potokiem asynchronicznym:

<div *ngFor="let a of [aVariable | async]">
  <span>{{a.prop1}}</span>
  <span>{{a.prop2}}</span>
</div>

4
To właśnie wymyśliłem instynktownie - z tym też działa *ngFor="let a of [(someStream$ | async).someA]. Wydaje mi się, że użyty z <ng-container>to całkiem dobrze działa!
— Angelos Pikoulas

2
W takim przypadku *ngFornależy pamiętać, że cała zagnieżdżona zawartość zostanie odtworzona, jeśli zmieni się wartość zmiennej, dopóki nie zostanie określona trackByfunkcja, która zwraca ten sam identyfikator dla wszystkich wartości.
— Valeriy Katkov

74

Możesz zadeklarować zmienne w kodzie HTML, używając templateelementu w Angular 2 lub ng-templateAngular 4+.

Szablony mają obiekt kontekstowy, którego właściwości można przypisać do zmiennych za pomocą letskładni wiązania. Pamiętaj, że musisz określić wylot dla szablonu, ale może to być odniesienie do samego siebie.

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

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

Można zmniejszyć ilość kodu, używając $implicitwłaściwości obiektu kontekstu zamiast właściwości niestandardowej.

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

Obiekt kontekstu może być obiektem dosłownym lub dowolnym innym wyrażeniem wiążącym. Wydaje się, że nawet rury działają w nawiasach.

Prawidłowe przykłady ngTemplateOutletContext:

  • [ngTemplateOutletContext]="{ aVariable: 123 }"
  • [ngTemplateOutletContext]="{ aVariable: (3.141592 | number:'3.1-5') }"
  • [ngTemplateOutletContext]="{ aVariable: anotherVariable }" używać z let-a="aVariable"
  • [ngTemplateOutletContext]="{ $implicit: anotherVariable }" używać z let-a
  • [ngTemplateOutletContext]="ctx"gdzie ctxjest własność publiczna

Aby działało, musiałem zmienić kod z „<szablon ...” na „<ng-szablon ...”.
— Humppakäräjät

2
Tak, można używać tylko <template>w kątowej 2. Można użyć jednej <template>lub <ng-template>w kątowym 4, ale należy używać tylko <ng-template>. Angular 5 porzucił wsparcie dla <template>.
— Steven Liekens

Do czego służy t?
— matttm

1
@matttm #tto zmienna szablonu, która przechowuje ng-template. Służy [ngTemplateOutlet]="t"do tworzenia odniesienia do samego szablonu ng.
— Steven Liekens

To dziwne, ale działa! Angular powinien to uprościć dzięki wbudowanej zmiennej dyrektywie. Dzięki.
— TetraDev

57

aktualizacja 3

Problem 2451 został naprawiony w Angular 4.0.0

Zobacz też

aktualizacja 2

To nie jest obsługiwane.

Istnieją zmienne szablonów, ale przypisywanie dowolnych wartości nie jest obsługiwane. Można ich używać tylko w odniesieniu do elementów, do których są stosowane, eksportowanych nazw dyrektyw lub komponentów i zmiennych zakresu dla dyrektyw strukturalnych, takich jak ngFor,

Zobacz także https://github.com/angular/angular/issues/2451

Aktualizacja 1

@Directive({
  selector: '[var]',
  exportAs: 'var'
})
class VarDirective {
  @Input() var:any;
}

i zainicjuj to jak

<div #aVariable="var" var="abc"></div>

lub

<div #aVariable="var" [var]="'abc'"></div>

i użyj zmiennej like

<div>{{aVariable.var}}</div>

(nie testowany)

  • #aVariabletworzy odniesienie do VarDirective( exportAs: 'var')
  • var="abc"tworzy instancję VarDirectivei przekazuje wartość ciągu "abc"do wartości wejściowej.
  • aVariable.varodczytuje wartość przypisaną do danych wejściowych vardyrektywvar

Czy nie byłoby możliwe stworzenie w tym celu dyrektywy strukturalnej?
— Scipion

Jeśli potrzebujesz tego wielokrotnie, dyrektywa może zrobić to, co chcesz. Dyrektywa strukturalna tworzy własny pogląd, prawdopodobnie nie tego chcesz.
— Günter Zöchbauer

1
@ GünterZöchbauer, bardzo dobre rzeczy. Wiem, że prawdopodobnie lepszą praktyką jest obliczanie / przygotowywanie zmiennych w component.tspliku. Ale o wiele łatwiej jest mi je wyświetlić w niektórych przypadkach ze względu na schemat synchronizacji, który wdrażam w całej mojej aplikacji. Korzystam z reguł odwołań javascript, gdy różne zmienne wskazują ten sam obiekt.
— AmmarCSE,

Otrzymuję błąd jak There is no directive with "exportAs" set to "var". Czy ktoś może mi powiedzieć, jaki popełniłem błąd? Użyłem powyższej dyrektywy.
— Partha Sarathi Ghosh

Być może nie dodać do dyrektywy declarations: [...]o @NgModule(). Jeśli to nie jest problem, utwórz nowe pytanie i podaj kod umożliwiający zdiagnozowanie problemu.
— Günter Zöchbauer


11

Oto dyrektywa, którą napisałem, która rozszerza zastosowanie parametru dekoratora exportAs i pozwala na użycie słownika jako zmiennej lokalnej.

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( ) {
    }
}

Możesz użyć go w szablonie w następujący sposób:

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

Oczywiście #local może być dowolną poprawną nazwą zmiennej lokalnej.


Nie przekazuje kompilacji „produkcyjnej” w takiej postaci, w jakiej jest (pokazuje również jako błędy według IDE). Dodaj [key: string]: any;do, Classaby obejść ten problem.
— Charly,

7

W przypadku, gdy chcesz uzyskać odpowiedź funkcji i ustawić ją w zmiennej, możesz użyć jej w szablonie w następujący sposób, ng-containeraby uniknąć modyfikacji szablonu.

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

Metoda w komponencie może być podobna

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

5

Jeśli potrzebujesz wsparcia autouzupełniania z poziomu szablonów z Angular Language Service :

Synchroniczny:

myVar = { hello: '' };

<ng-container *ngIf="myVar; let var;">
  {{var.hello}}
</ng-container>

Za pomocą potoku asynchronicznego:

myVar$ = of({ hello: '' });

<ng-container *ngIf="myVar$ | async; let var;">
  {{var.hello}}
</ng-container>

2

Używam kątowego 6x i skończyło się na tym, że użyłem poniżej fragmentu kodu. Mam scenerię, w której muszę znaleźć użytkownika z obiektu zadania. zawiera tablicę użytkowników, ale muszę wybrać przypisanego użytkownika.

<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

Jest to o wiele prostsze, bez potrzeby niczego dodatkowego. W moim przykładzie deklaruję zmienną „open”, a następnie jej używam.

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

nazywając tag, nie deklaruje on zmiennej
— Amirreza

1
@Amirreza, a ściślej używam ElementRef do tymczasowego przechowywania wartości.
— Jack Rus

Niesamowite! Musiałem użyć, "?"ponieważ miałem komunikat „Wartość identyfikatora” nie jest zdefiniowana ”jak to =>„ open? .Value ”Ale to działa !!
— A. Morel,

1

Podobało mi się podejście polegające na stworzeniu w tym celu dyrektywy (dobre połączenie @yurzui).

Skończyło się na tym, że znalazłem dyrektywę Angular „let” w artykule Medium, która ładnie wyjaśnia ten problem i proponuje niestandardową dyrektywę let, która ostatecznie świetnie działa w moim przypadku użycia z minimalnymi zmianami kodu.

Oto sedno (w momencie publikowania) moich modyfikacji:

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

Moje główne zmiany to:

  • zmiana prefiksu z „ng” na „app” (należy użyć dowolnego niestandardowego prefiksu aplikacji)
  • zmiana appLet: TnaappLet: T | null

Nie jestem pewien, dlaczego zespół Angular nie tylko wydał oficjalną dyrektywę ngLet, ale co tam.

Oryginalny kod źródłowy trafia do @AustinMatherne


To było moje ulubione podejście na stronie i zadziałało dla mnie.
— Skychan

1

Krótka odpowiedź, która komuś pomaga

  • Zmienna odniesienia szablonu często odwołuje się do elementu DOM w szablonie.
  • Również odniesienie do komponentu i dyrektywy kątowej lub sieci.
  • Oznacza to, że możesz łatwo uzyskać dostęp do zmiennej w dowolnym miejscu szablonu

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

  • Deklaracja zmiennej referencyjnej za pomocą symbolu skrótu (#)
  • Potrafi przekazać zmienną jako parametr zdarzenia

wprowadź opis zdjęcia tutaj

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

* Można jednak użyć dekoratora ViewChild, aby odwołać się do niego wewnątrz komponentu.

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

Odwołanie do zmiennej firstNameInput wewnątrz komponentu

@ViewChild('firstNameInput') nameInputRef: ElementRef;

Następnie możesz użyć this.nameInputRef w dowolnym miejscu w swoim Component.

Praca z szablonem ng

W przypadku ng-template jest nieco inaczej, ponieważ każdy szablon ma swój własny zestaw zmiennych wejściowych.

wprowadź opis zdjęcia tutaj

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


1

Dla tych, którzy zdecydowali się zastosować dyrektywę strukturalną jako zamiennik *ngIf, należy pamiętać, że kontekst dyrektywy nie jest domyślnie sprawdzany pod względem typu. Aby utworzyć bezpieczną dyrektywę typu ngTemplateContextGuardnależy dodać właściwość, patrz Pisanie kontekstu dyrektywy . Na przykład:

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

Dyrektywa może być używana tak samo *ngIf, z tym wyjątkiem, że może przechowywać fałszywe wartości:

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

Jedyną wadą w porównaniu z *ngIftym jest to, że Angular Language Service nie jest w stanie ustalić typu zmiennej, więc nie ma uzupełniania kodu w szablonach. Mam nadzieję, że zostanie to wkrótce naprawione.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.