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.