Como declarar uma variável em um modelo em Angular

199

Eu tenho o seguinte modelo:

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

e gostaria de terminar com:

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

Existe uma maneira de fazer?

Scipion
fonte
Estou interessado em saber qual é o requisito / caso de uso para querer alterar o nome de um parâmetro de ligação como este exemplo?
LDJ 26/07
29
É apenas para evitar repetir algo como tab [elemento] .val por instância. Eu sei que posso resolver o problema no componente, mas eu estava apenas olhando como fazer no modelo (mesmo que eu não tenha essa solução).
Scipion
2
@LDJ caso de uso de uma amostra: eficiência. Use a amostra de stackblitz.com/angular/… <mat-checkbox [marcada] = "descendantsAllSelected (node)" [indeterminado] = "descendantsPartiallySelected (node)" (change) = "todoItemSelectionToggle (node)"> {{node. item}} </mat-checkbox> de fato, os descendentesPartiallySelected () chama descendantsAllSelected (). Isso significa que em algum momento descendentesAllSelected é chamado duas vezes. Se houver uma variável local, isso pode ser evitado.
precisa saber é o seguinte
3
<div *ngIf="{name:'john'} as user1; let user"> <i>{{user1|json}}</i> <i>{{user|json}}</i> </div>
Dasfdsa 19/03/19
@dasfdsa Eu acredito user1 === user, assim você faz *ngIf="{name:'john'} as user1ou *ngIf="{name:'john'};let usercomo na resposta do yurzui .
CPHPython

Respostas:

169

Atualizar

Podemos apenas criar uma diretiva como *ngIfe chamá-la*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);
  }
}

com esta *ngVardiretiva, podemos usar o seguinte

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

ou

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

ou

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

ou

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

Exemplo de Plunker Angular4 ngVar

Veja também

Resposta original

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

Visão

<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 você não deseja criar um invólucro como divvocê pode usarng-container

Visão

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

Como @Keith mencionado nos comentários

isso funcionará na maioria dos casos, mas não é uma solução geral, pois depende da variável ser verdadeira

Consulte a atualização para outra abordagem.

yurzui
fonte
10
isso vai funcionar na maioria dos casos, mas não é uma solução geral, uma vez que se baseia em variableser truthy
Keith
6
@ Keith Obrigado por apontar isso. Você pode dar uma olhada na minha resposta atualizada
yurzui
3
Essa deve ser a nova resposta, pois 1) é mais moderna que a outra solução 2) resume as solicitações de recebimento vinculadas na resposta atual e economiza muito tempo 3) inclui os exemplos em linha e não por link externo. Obrigado por mostrar isso. AFAIK qualquer objeto envolvido {}será avaliado como verdadeiro, portanto, esta solução é bastante robusta.
precisa saber é o seguinte
4
Por exemplo, botões expansíveis: *ngIf="{ expanded: false } as scope"e, se você estiver usando o bootstrap, poderá usar [ngClass]="{ 'in': scope.expanded }"e, em (click)="scope.expanded = !scope.expanded"vez de adicionar algo aos seus js/ tsarquivos.
precisa saber é o seguinte
1
questão github relacionada (pontos fora usando um simples *ngIfem vez de coisas ngvar personalizado): github.com/angular/angular/issues/14985
phil294
79

Feio, mas:

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

Quando usado com tubo assíncrono:

<div *ngFor="let a of [aVariable | async]">
  <span>{{a.prop1}}</span>
  <span>{{a.prop2}}</span>
</div>
kayjtea
fonte
4
Foi com isso que eu instintivamente - ele também funciona *ngFor="let a of [(someStream$ | async).someA]. Eu acho que usado com um <ng-container>que serve o trabalho muito bem!
Angelos Pikoulas
1
No caso de *ngFor, lembre-se de que todo o conteúdo aninhado será recriado se o valor da variável for alterado, até você especificar uma trackByfunção que retorne o mesmo ID para todos os valores.
Valeriy Katkov
72

Você pode declarar variáveis ​​no código html usando um templateelemento no Angular 2 ou ng-templateno Angular 4+.

Os modelos têm um objeto de contexto cujas propriedades podem ser atribuídas a variáveis ​​usando leta sintaxe de ligação. Observe que você deve especificar uma saída para o modelo, mas pode ser uma referência a si mesma.

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

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

Você pode reduzir a quantidade de código usando a $implicitpropriedade do objeto de contexto em vez de uma propriedade customizada.

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

O objeto de contexto pode ser um objeto literal ou qualquer outra expressão de ligação. Até canos parecem funcionar quando cercados por parênteses.

Exemplos válidos de ngTemplateOutletContext:

  • [ngTemplateOutletContext]="{ aVariable: 123 }"
  • [ngTemplateOutletContext]="{ aVariable: (3.141592 | number:'3.1-5') }"
  • [ngTemplateOutletContext]="{ aVariable: anotherVariable }" usar com let-a="aVariable"
  • [ngTemplateOutletContext]="{ $implicit: anotherVariable }" usar com let-a
  • [ngTemplateOutletContext]="ctx"onde ctxé uma propriedade pública
Steven Liekens
fonte
Para fazê-lo funcionar, tive que alterar seu código de '<template ...' para '<ng-template ...'.
Humppakäräjät
2
Sim, você só pode usar <template>no Angular 2. Você pode usar um <template>ou outro <ng-template>no Angular 4, mas deve usar apenas <ng-template>. Angular 5 caiu suporte para <template>.
Steven Liekens 01/01
Para que serve t?
matttm
1
@matttm #té uma variável de modelo que armazena o ng-template. É usado [ngTemplateOutlet]="t"para fazer a referência do modelo ng em si.
Steven Liekens
Isso é bizare, mas funciona! Angular deve tornar isso mais simples, com uma diretiva de variável interna. Obrigado.
TetraDev
57

atualização 3

O problema 2451 foi corrigido no Angular 4.0.0

Veja também

atualização 2

Isto não é suportado.

Existem variáveis ​​de modelo, mas não há suporte para atribuir valores arbitrários. Eles só podem ser usados ​​para se referir aos elementos aos quais são aplicados, nomes exportados de diretivas ou componentes e variáveis ​​de escopo para diretivas estruturais como ngFor,

Consulte também https://github.com/angular/angular/issues/2451

Atualização 1

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

e inicialize como

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

ou

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

e use a variável como

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

(não testado)

  • #aVariablecria uma referência para o VarDirective( exportAs: 'var')
  • var="abc"instancia o VarDirectivee passa o valor da string "abc"para sua entrada de valor.
  • aVariable.varlê o valor atribuído à entrada de vardiretivas var.
Günter Zöchbauer
fonte
Não seria possível criar uma diretiva estrutural para isso?
Scipion
Se você precisar disso repetidamente, uma diretiva poderá fazer o que você deseja. Uma diretiva estrutural cria sua própria visão, provavelmente não é isso que você deseja.
Günter Zöchbauer
1
@ GünterZöchbauer, coisas muito boas. Eu sei que provavelmente é uma prática melhor ter variáveis ​​calculadas / preparadas no component.tsarquivo. Mas é muito mais fácil tê-los à vista em alguns casos devido a um esquema de sincronização que estou implementando em todo o meu aplicativo. Estou aproveitando as regras de referência de javascript quando variáveis ​​diferentes apontam para o mesmo objeto.
AmmarCSE
Estou recebendo um erro como There is no directive with "exportAs" set to "var". Alguém pode me dizer que erro eu cometi? Eu usei a diretiva acima.
Partha Sarathi Ghosh
Talvez você não tenha adicionado a diretiva a declarations: [...]de @NgModule(). Se este não for o problema, crie uma nova pergunta e forneça o código que permite diagnosticar o problema.
Günter Zöchbauer
11

Aqui está uma diretiva que escrevi que expande o uso do parâmetro decorador exportAs e permite que você use um dicionário como uma variável local.

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

Você pode usá-lo da seguinte maneira em um modelo:

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

É claro que #local pode ser qualquer nome de variável local válido.

Aaron
fonte
Não passa uma compilação de 'produção' como está (também mostra como erros por IDEs). Adicione [key: string]: any;ao Classpara contornar isso.
Charly
11

Gostaria de sugerir o seguinte: https://medium.com/@AustinMatherne/angular-let-directive-a168d4248138

Esta diretiva permite que você escreva algo como:

<div *ngLet="'myVal' as myVar">
  <span> {{ myVar }} </span>
</div>
monkeythedev
fonte
1
como este, mas * é reservado para directivas estruturais que esta não é, de qualquer maneira +1
danday74
7

Caso deseje obter a resposta de uma função e configurá-la em uma variável, você pode usá-la da seguinte forma no modelo, ng-containerpara evitar a modificação do modelo.

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

E o método no componente pode ser algo como

methodName(parameters: any): any {
  return {name: 'Test name'};
}
Philip John
fonte
5

Se você precisar de suporte de preenchimento automático de seus modelos no Angular Language Service :

Síncrono:

myVar = { hello: '' };

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

Usando pipe assíncrono:

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

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

É muito mais simples, sem necessidade de mais nada. No meu exemplo, declaro a variável "open" e depois a uso.

   <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>
Jack Rus
fonte
você nomear uma tag, ele não declarar variável
Amirreza
1
@Amirreza, para ser mais preciso, estou usando o ElementRef para armazenar temporariamente um valor.
Jack Rus
Impressionante! Eu tive que usar "?"porque tinha a mensagem "Identificador 'valor' não está definido" como este => "aberto? .Valor" Mas está funcionando !!
A. Morel
2

Estou usando 6x angular e acabei usando o trecho abaixo. Eu tenho um cenário onde eu tenho que encontrar usuário de um objeto de tarefa. Ele contém vários usuários, mas eu tenho que escolher o usuário atribuído.

<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>
O mecânico
fonte
1

Gostei da abordagem de criar uma diretiva para fazer isso (boa chamada @yurzui).

Acabei encontrando uma diretiva angular "let" de artigo Médio, que explica bem esse problema e propõe uma diretiva let personalizada que acabou funcionando muito bem no meu caso de uso com alterações mínimas de código.

Aqui está a essência (no momento da postagem) com minhas modificações:

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

Minhas principais mudanças foram:

  • alterando o prefixo de 'ng' para 'app' (você deve usar o prefixo personalizado do seu aplicativo)
  • mudando appLet: TparaappLet: T | null

Não sei por que a equipe Angular não fez apenas uma diretiva ngLet oficial, mas o que quer.

O crédito do código fonte original vai para @AustinMatherne

Keego
fonte
Esta foi a minha abordagem favorita na página e funcionou para mim.
Skychan 4/03
1

Resposta curta que ajuda a alguém

  • A variável de referência de modelo geralmente faz referência ao elemento DOM dentro de um modelo.
  • Consulte também componentes e diretivas angulares ou da web.
  • Isso significa que você pode acessar facilmente a variável em qualquer lugar do modelo

insira a descrição da imagem aqui

insira a descrição da imagem aqui

  • Declarar variável de referência usando o símbolo de hash (#)
  • Consegue passar uma variável como parâmetro em um evento

insira a descrição da imagem aqui

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

* No entanto, você pode usar o decorador do ViewChild para referenciá-lo dentro do seu componente.

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

Variável de referência firstNameInput dentro de Component

@ViewChild('firstNameInput') nameInputRef: ElementRef;

Depois disso, você pode usar this.nameInputRef em qualquer lugar dentro do seu Component.

Trabalhando com ng-template

No caso do ng-template, é um pouco diferente porque cada modelo possui seu próprio conjunto de variáveis ​​de entrada.

insira a descrição da imagem aqui

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

Mano
fonte
0

Para aqueles que decidiram usar uma diretiva estrutural como uma substituição *ngIf, lembre-se de que o contexto da diretiva não é verificado por tipo. Para criar uma ngTemplateContextGuardpropriedade de diretiva de segurança de tipo deve ser adicionada, consulte Digitando o contexto da diretiva . Por exemplo:

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

A diretiva pode ser usada da mesma maneira *ngIf, exceto que pode armazenar valores falsos :

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

A única desvantagem comparada *ngIfé que o Angular Language Service não pode descobrir o tipo de variável, portanto, não há conclusão de código nos modelos. Espero que seja corrigido em breve.

Valeriy Katkov
fonte