Come dichiarare una variabile in un modello in Angolare


203

Ho il seguente modello:

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

e vorrei finire con:

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

C'è un modo per fare?


Sono interessato a sapere qual è il requisito / caso d'uso per voler cambiare il nome di un parametro vincolante come questo esempio?
— LDJ,

31
È solo per evitare di ripetere qualcosa come tab [element] .val per istanza. So di poter risolvere il problema nel componente, ma stavo solo guardando come fare nel modello (anche se potrei non finire con quella soluzione).
— Scipione,

2
@LDJ un caso d'uso di esempio: efficienza. Usa l'esempio di stackblitz.com/angular/… <mat-checkbox [checked] = "descendantsAllSelected (node)" [indeterminate] = "descendantsPartiallySelected (node)" (change) = "todoItemSelectionToggle (node)"> {{nodo item}} </mat-checkbox> in effetti i discendentiPartiallySelected () chiama descendantsAllSelected (). Significa che a volte discendenti AllSelected viene chiamato due volte. Se c'è una variabile locale, questo può essere evitato.
— Steven.Xi

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

@dasfdsa Credo user1 === user, quindi lo fai *ngIf="{name:'john'} as user1o *ngIf="{name:'john'};let usercome nella risposta di Yurzui .
— CPHPython,

Risposte:


175

Aggiornare

Possiamo semplicemente creare una direttiva come *ngIfe chiamarla*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);
  }
}

con questa *ngVardirettiva possiamo usare quanto segue

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

o

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

o

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

o

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

Plunker Esempio Angular4 ngVar

Guarda anche

Risposta originale

V4 angolare

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

Visualizza

<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) Se non vuoi creare un wrapper come divpuoi usareng-container

Visualizza

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

Come @Keith ha menzionato nei commenti

questo funzionerà nella maggior parte dei casi, ma non è una soluzione generale poiché si basa sul fatto che la variabile è vera

Vedi aggiornamento per un altro approccio.


10
questo funzionerà nella maggior parte dei casi ma non è una soluzione generale poiché si basa sulla variableverità
— Keith

6
@Keith Grazie per averlo segnalato. Puoi dare un'occhiata alla mia risposta aggiornata
— yurzui,

3
Questa dovrebbe essere la nuova risposta in quanto 1) è più moderna dell'altra soluzione 2) sintetizza le richieste pull collegate nella risposta corrente e fa risparmiare molto tempo 3) include gli esempi in linea piuttosto che tramite un collegamento esterno. Grazie per averlo mostrato. AFAIK qualsiasi oggetto racchiuso {}verrà valutato come veritiero, quindi questa soluzione è abbastanza robusta.
— kvanberendonck,

4
Ad esempio, pulsanti espandibili: *ngIf="{ expanded: false } as scope"e quindi se stai usando bootstrap puoi semplicemente usare [ngClass]="{ 'in': scope.expanded }"e (click)="scope.expanded = !scope.expanded"invece di aggiungere qualsiasi cosa al tuo js/ tsfile.
— kvanberendonck,

1
problema relativo a github (sottolinea l'uso di un semplice *ngIfngvar personalizzato): github.com/angular/angular/issues/14985
— phil294

81

Brutto, ma:

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

Se utilizzato con tubo asincrono:

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

4
È quello che mi è venuto in mente istintivamente - funziona anche con *ngFor="let a of [(someStream$ | async).someA]. Immagino che l'abbia usato <ng-container>serva bene il lavoro!
— Angelos Pikoulas,

2
In caso affermativo *ngFor, tenere presente che tutto il contenuto nidificato verrà ricreato se il valore della variabile cambia, fino a quando non si specifica una trackByfunzione che restituisce lo stesso ID per tutti i valori.
— Valeriy Katkov il

76

Puoi dichiarare le variabili nel codice html usando un templateelemento in Angular 2 o ng-templatein Angular 4+.

I modelli hanno un oggetto di contesto le cui proprietà possono essere assegnate alle variabili usando la letsintassi di associazione. Si noti che è necessario specificare un punto vendita per il modello, ma può essere un riferimento a se stesso.

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

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

È possibile ridurre la quantità di codice utilizzando la $implicitproprietà dell'oggetto di contesto anziché una proprietà personalizzata.

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

L'oggetto di contesto può essere un oggetto letterale o qualsiasi altra espressione vincolante. Anche i tubi sembrano funzionare quando sono racchiusi tra parentesi.

Esempi validi di ngTemplateOutletContext:

  • [ngTemplateOutletContext]="{ aVariable: 123 }"
  • [ngTemplateOutletContext]="{ aVariable: (3.141592 | number:'3.1-5') }"
  • [ngTemplateOutletContext]="{ aVariable: anotherVariable }" Usa con let-a="aVariable"
  • [ngTemplateOutletContext]="{ $implicit: anotherVariable }" Usa con let-a
  • [ngTemplateOutletContext]="ctx"dove ctxè una proprietà pubblica

Per farlo funzionare ho dovuto cambiare il tuo codice da '<template ...' a '<ng-template ...'.
— Humppakäräjät,

2
Sì, puoi usare solo <template>in Angolare 2. Puoi usare uno <template>o <ng-template>in Angolare 4, ma dovresti usare solo <ng-template>. Supporto angolare 5 abbandonato per <template>.
— Steven Liekens,

A cosa serve t?
— Matttm,

1
@matttm #tè una variabile modello che memorizza il file ng-template. Viene utilizzato [ngTemplateOutlet]="t"per creare il riferimento ng-template stesso.
— Steven Liekens,

Questo è bizzarro, ma funziona! Angular dovrebbe renderlo più semplice, con una direttiva variabile integrata. Grazie.
— TetraDev,

57

aggiornamento 3

Il problema 2451 è stato risolto in Angular 4.0.0

Guarda anche

aggiornamento 2

Questo non è supportato.

Esistono variabili modello ma non è supportato per assegnare valori arbitrari. Possono essere utilizzati solo per fare riferimento agli elementi a cui sono applicati, nomi esportati di direttive o componenti e variabili di ambito per direttive strutturali come ngFor,

Vedi anche https://github.com/angular/angular/issues/2451

Aggiornamento 1

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

e inizializzarlo come

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

o

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

e usa la variabile like

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

(non testato)

  • #aVariablecrea un riferimento a VarDirective( exportAs: 'var')
  • var="abc"crea un'istanza di VarDirectivee passa il valore di stringa "abc"al suo input di valore.
  • aVariable.varlegge il valore assegnato all'ingresso delle vardirettive var.

Non sarebbe possibile creare una direttiva strutturale per farlo?
— Scipione,

Se ne hai bisogno ripetutamente, una direttiva potrebbe fare quello che vuoi. Una direttiva strutturale crea il proprio punto di vista, probabilmente non è quello che vuoi.
— Günter Zöchbauer,

1
@ GünterZöchbauer, roba molto buona. So che è probabilmente una pratica migliore avere variabili calcolate / preparate nel component.tsfile. Ma è molto più facile per me averli in vista per alcuni casi a causa di uno schema di sincronizzazione che sto implementando in tutta la mia app. Sto sfruttando le regole di riferimento javascript quando variabili diverse puntano allo stesso oggetto.
— AmmarCSE,

Ricevo un errore come There is no directive with "exportAs" set to "var". Qualcuno può dirmi quale errore ho fatto? Ho usato la direttiva di cui sopra.
— Partha Sarathi Ghosh,

Forse non hai aggiunge la direttiva declarations: [...]di @NgModule(). Se questo non è il problema, crea una nuova domanda e fornisci il codice che consente di diagnosticare il problema.
— Günter Zöchbauer,


11

Ecco una direttiva che ho scritto che si espande sull'uso del parametro decorator exportAs e ti consente di usare un dizionario come variabile locale.

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

Puoi usarlo come segue in un modello:

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

Naturalmente #local può essere qualsiasi nome di variabile locale valido.


Non passa una build di "produzione" così com'è (mostra anche come errori dagli IDE). Aggiungi [key: string]: any;a Classper aggirare questo.
— Charly,

7

Nel caso in cui si desideri ottenere la risposta di una funzione e impostarla in una variabile, è possibile utilizzarla come segue nel modello, usando ng-containerper evitare di modificare il modello.

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

E il metodo nel componente può essere qualcosa di simile

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

5

Se hai bisogno del supporto per il completamento automatico all'interno dei tuoi modelli dal servizio linguistico angolare :

Sincrono:

myVar = { hello: '' };

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

Utilizzando una pipa asincrona:

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

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

2

Sto usando l'angolare 6x e ho finito usando lo snippet di seguito. Ho uno scenario in cui devo trovare l'utente da un oggetto task. contiene un array di utenti ma devo scegliere l'utente assegnato.

<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

È molto più semplice, non è necessario altro. Nel mio esempio dichiaro la variabile "aperta" e quindi la utilizzo.

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

stai nominando un tag, non sta dichiarando variabile
— Amirreza,

1
@Amirreza, per essere precisi sto usando ElementRef per memorizzare un valore.
— Jack Rus,

Eccezionale! Ho dovuto usare "?"perché avevo il messaggio "Identificatore 'valore' non definito" come questo => "aperto? .Valore" Ma funziona !!
— A. Morel,

1

Mi è piaciuto l'approccio della creazione di una direttiva per farlo (buona chiamata @yurzui).

Ho finito per trovare una direttiva "let" angolare di articolo medio che spiega bene questo problema e propone una direttiva let personalizzata che ha funzionato alla grande per il mio caso d'uso con modifiche minime al codice.

Ecco l'essenza (al momento della pubblicazione) con le mie modifiche:

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

Le mie principali modifiche sono state:

  • cambiando il prefisso da 'ng' a 'app' (dovresti usare qualunque sia il prefisso personalizzato della tua app)
  • cambiando appLet: TinappLet: T | null

Non so perché il team di Angular non abbia appena fatto una direttiva ngLet ufficiale, ma whatevs.

Il credito del codice sorgente originale va a @AustinMatherne


Questo era il mio approccio preferito sulla pagina e ha funzionato per me.
— Skychan,

1

Risposta breve che aiuta qualcuno

  • La variabile di riferimento del modello fa spesso riferimento all'elemento DOM all'interno di un modello.
  • Anche riferimento a componente angolare o web e direttiva.
  • Ciò significa che puoi accedere facilmente alla variabile ovunque in un modello

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

  • Dichiarare la variabile di riferimento usando il simbolo hash (#)
  • È in grado di passare una variabile come parametro su un evento

inserisci qui la descrizione dell'immagine

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

* Tuttavia, è possibile utilizzare ViewChild Decorator per fare riferimento all'interno del componente.

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

Riferimento firstNameInput variabile all'interno del componente

@ViewChild('firstNameInput') nameInputRef: ElementRef;

Successivamente, è possibile utilizzare this.nameInputRef ovunque all'interno del componente.

Lavorare con ng-template

Nel caso di ng-template, è un po 'diverso perché ogni modello ha il proprio set di variabili di input.

inserisci qui la descrizione dell'immagine

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


1

Per coloro che hanno deciso di utilizzare una direttiva strutturale in sostituzione di *ngIf, tenere presente che il contesto della direttiva non è controllato per impostazione predefinita. Per creare una ngTemplateContextGuardproprietà direttiva sicura da aggiungere, vedere Digitazione del contesto della direttiva . Per esempio:

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

La direttiva può essere utilizzata esattamente come *ngIf, tranne per il fatto che può contenere valori falsi :

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

L'unico inconveniente rispetto al fatto *ngIfche Angular Language Service non è in grado di capire il tipo di variabile, quindi nei template non esiste il completamento del codice. Spero che sarà risolto presto.


Funziona ma l'intellisense no. Sto usando l'angolare 8.
— Tx_monster
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.