Cách khai báo một biến trong một mẫu trong Angular


199

Tôi có mẫu sau:

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

và muốn kết thúc với:

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

Có cách nào để làm không?


Tôi quan tâm để biết trường hợp yêu cầu / sử dụng là gì khi muốn thay đổi tên của một tham số ràng buộc như ví dụ này?
LDJ

29
Nó chỉ để ngăn lặp lại một cái gì đó như tab [phần tử] .val theo ví dụ. Tôi biết tôi có thể giải quyết vấn đề trong thành phần, nhưng tôi chỉ xem cách làm trong mẫu (mặc dù tôi có thể không kết thúc với giải pháp đó).
Scipion

2
@LDJ một trường hợp sử dụng mẫu: hiệu quả. Sử dụng mẫu stackblitz.com/angular/ trên <mat-hộp kiểm [đã kiểm tra] = "hậu duệ mục}} </ mat-hộp kiểm> trong thực tế, các hậu duệPartallySelected () gọi các hậu duệ ALLSelected (). Nó có nghĩa là đôi khi con cháu ALLSelected được gọi hai lần. Nếu có một biến cục bộ, điều này có thể tránh được.
Steven.Xi

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

@dasfdsa Tôi tin user1 === user, do đó bạn có thể làm *ngIf="{name:'john'} as user1hoặc *ngIf="{name:'john'};let usernhư trong câu trả lời của yurzui .
CPHPython

Câu trả lời:


168

Cập nhật

Chúng ta chỉ có thể tạo ra lệnh like *ngIfvà gọi nó*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);
  }
}

với *ngVarchỉ thị này, chúng ta có thể sử dụng như sau

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

hoặc là

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

hoặc là

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

hoặc là

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

Ví dụ Plunker Angular4 ngVar

Xem thêm

Câu trả lời gốc

Góc 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

lượt xem

<div *ngIf="{ a: 1, b: 2, c: 3 + x } as variable">
  <span>{{variable.a}}</span>
  <span>{{variable.b}}</span>
  <span>{{variable.c}}</span>
</div>

thành phần

export class AppComponent {
  x = 5;
}

3) Nếu bạn không muốn tạo trình bao bọc như divbạn có thể sử dụngng-container

lượt xem

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

Như @Keith đã đề cập trong các bình luận

điều này sẽ hoạt động trong hầu hết các trường hợp nhưng nó không phải là một giải pháp chung vì nó phụ thuộc vào biến là sự thật

Xem cập nhật cho một cách tiếp cận khác.


10
điều này sẽ hoạt động trong hầu hết các trường hợp nhưng nó không phải là một giải pháp chung vì nó dựa trên variablesự thật
Keith

6
@Keith Cảm ơn bạn đã chỉ ra điều này. Bạn có thể xem câu trả lời được cập nhật của tôi
yurzui

3
Đây phải là câu trả lời mới vì nó 1) hiện đại hơn giải pháp khác 2) tóm tắt các yêu cầu kéo được liên kết trong câu trả lời hiện tại và tiết kiệm rất nhiều thời gian 3) bao gồm các ví dụ nội tuyến thay vì liên kết ngoài. Cảm ơn đã cho thấy điều này. AFAIK bất kỳ đối tượng nào được bọc {}sẽ đánh giá là trung thực, vì vậy giải pháp này khá mạnh mẽ.
kvanberendonck

4
Ví dụ: các nút có thể mở rộng: *ngIf="{ expanded: false } as scope"và sau đó nếu bạn đang sử dụng bootstrap, bạn chỉ có thể sử dụng [ngClass]="{ 'in': scope.expanded }"(click)="scope.expanded = !scope.expanded"thay vì thêm bất cứ thứ gì vào js/ tstệp của mình.
kvanberendonck

1
vấn đề github liên quan (chỉ ra bằng cách sử dụng một thứ đơn giản *ngIfthay vì ngvar tùy chỉnh): github.com/angular/angular/issues/14985
phil294

79

Xấu xí, nhưng:

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

Khi được sử dụng với ống async:

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

4
Đó là người mà tôi nghĩ ra theo bản năng - nó cũng hoạt động với *ngFor="let a of [(someStream$ | async).someA]. Tôi đoán được sử dụng với một <ng-container>nó phục vụ công việc hoàn toàn đúng!
Angelos Pikoulas

1
Trong trường hợp *ngFor, hãy nhớ rằng tất cả nội dung lồng nhau sẽ được tạo lại nếu giá trị biến thay đổi, cho đến khi bạn chỉ định một trackByhàm trả về cùng một id cho tất cả các giá trị.
Valeriy Katkov

72

Bạn có thể khai báo các biến trong mã html bằng cách sử dụng một templatephần tử trong Angular 2 hoặc ng-templatetrong Angular 4+.

Mẫu có một đối tượng ngữ cảnh có thuộc tính có thể được gán cho các biến bằng letcú pháp ràng buộc. Lưu ý rằng bạn phải chỉ định một lối thoát cho mẫu, nhưng nó có thể là một tham chiếu đến chính nó.

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

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

Bạn có thể giảm số lượng mã bằng cách sử dụng thuộc $implicittính của đối tượng bối cảnh thay vì thuộc tính tùy chỉnh.

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

Đối tượng bối cảnh có thể là một đối tượng bằng chữ hoặc bất kỳ biểu thức ràng buộc nào khác. Ngay cả các đường ống dường như hoạt động khi được bao quanh bởi dấu ngoặc đơn.

Ví dụ hợp lệ của ngTemplateOutletContext:

  • [ngTemplateOutletContext]="{ aVariable: 123 }"
  • [ngTemplateOutletContext]="{ aVariable: (3.141592 | number:'3.1-5') }"
  • [ngTemplateOutletContext]="{ aVariable: anotherVariable }" dùng với let-a="aVariable"
  • [ngTemplateOutletContext]="{ $implicit: anotherVariable }" dùng với let-a
  • [ngTemplateOutletContext]="ctx"nơi ctxlà một tài sản công cộng

Để làm cho nó hoạt động, tôi đã phải thay đổi mã của bạn từ '<mẫu ...' thành '<ng-template ...'.
Humppakäräjät

2
Đúng, bạn chỉ có thể sử dụng <template>trong Angular 2. Bạn có thể sử dụng <template>hoặc <ng-template>trong Angular 4, nhưng bạn chỉ nên sử dụng <ng-template>. Angular 5 bỏ hỗ trợ cho <template>.
Steven Liekens

Để làm gì t?
matttm

1
@matttm #tlà một biến mẫu lưu trữ ng-template. Nó được sử dụng [ngTemplateOutlet]="t"để tạo tham chiếu mẫu ng.
Steven Liekens

Đây là bizare, nhưng nó hoạt động! Angular nên làm cho điều này đơn giản hơn, với một chỉ thị biến tích hợp. Cảm ơn.
TetraDev

57

cập nhật 3

Sự cố 2451 được khắc phục trong Angular 4.0.0

Xem thêm

cập nhật 2

Điều này không được hỗ trợ.

Có các biến mẫu nhưng nó không được hỗ trợ để gán các giá trị tùy ý. Chúng chỉ có thể được sử dụng để chỉ các yếu tố mà chúng được áp dụng, xuất khẩu tên của chỉ thị hoặc thành phần và biến phạm vi cho các chỉ thị cấu trúc nhưngFor ,

Xem thêm https://github.com/angular/angular/issues/2451

Cập nhật 1

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

và khởi tạo nó như thế nào

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

hoặc là

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

và sử dụng biến như

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

(không được kiểm tra)

  • #aVariabletạo một tham chiếu đến VarDirective(exportAs: 'var' )
  • var="abc"khởi tạo VarDirectivevà truyền giá trị chuỗi"abc" cho đầu vào giá trị của nó.
  • aVariable.varđọc giá trị được gán cho đầu vào varchỉ thị var.

Không thể tạo ra một chỉ thị cấu trúc để làm như vậy?
Scipion

Nếu bạn cần điều này nhiều lần thì một chỉ thị có thể làm những gì bạn muốn. Một chỉ thị cấu trúc tạo ra quan điểm riêng của nó, đó có thể không phải là điều bạn muốn.
Günter Zöchbauer

1
@ GünterZöchbauer, thứ rất tốt. Tôi biết rằng có lẽ nên thực hiện tốt hơn các biến được tính toán / chuẩn bị trong component.tstệp. Nhưng nó dễ dàng hơn nhiều đối với tôi khi xem chúng trong một số trường hợp do sơ đồ đồng bộ hóa mà tôi đang triển khai trong suốt ứng dụng của mình. Tôi đang tận dụng các quy tắc tham chiếu javascript khi các biến khác nhau trỏ đến cùng một đối tượng.
AmmarCSE

Tôi nhận được một lỗi như thế nào There is no directive with "exportAs" set to "var". Có ai có thể vui lòng cho tôi biết tôi đã làm gì sai không? Tôi đã sử dụng chỉ thị trên.
Partha Sarathi Ghosh

Có lẽ bạn không thêm các chỉ thị để declarations: [...]về @NgModule(). Nếu đây không phải là vấn đề, vui lòng tạo một câu hỏi mới và cung cấp mã cho phép chẩn đoán sự cố.
Günter Zöchbauer

11

Dưới đây là một chỉ thị mà tôi đã viết, mở rộng về việc sử dụng tham số trang trí exportAs và cho phép bạn sử dụng từ điển làm biến cục bộ.

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

Bạn có thể sử dụng nó như sau trong một mẫu:

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

Tất nhiên #local có thể là bất kỳ tên biến cục bộ hợp lệ nào.


Không vượt qua bản dựng 'sản xuất' như hiện tại (cũng hiển thị là lỗi của IDE). Thêm [key: string]: any;vào Classđể có được xung quanh này.
Charly


7

Trong trường hợp nếu bạn muốn nhận được phản hồi của hàm và đặt nó thành một biến, bạn có thể sử dụng nó như sau trong mẫu, sử dụng ng-containerđể tránh sửa đổi mẫu.

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

Và phương thức trong thành phần có thể giống như

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

5

Nếu bạn cần hỗ trợ tự động hoàn thành từ bên trong các mẫu của mình từ Dịch vụ Ngôn ngữ góc :

Đồng bộ:

myVar = { hello: '' };

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

Sử dụng ống không đồng bộ:

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

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

2

Nó đơn giản hơn nhiều, không cần thêm gì. Trong ví dụ của tôi, tôi khai báo biến "mở" và sau đó sử dụng nó.

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

bạn đặt tên cho một thẻ, nó không khai báo biến
Amirreza

1
@Amirreza, chính xác là tôi đang sử dụng ElementRef để lưu trữ một giá trị.
Jack Rus

Tuyệt vời! Tôi đã phải sử dụng "?"vì tôi có thông báo "Định danh 'giá trị' không được xác định" như thế này => "mở?. Giá trị" Nhưng nó hoạt động !!
A. Morel

2

Tôi đang sử dụng 6x góc và tôi đã kết thúc bằng cách sử dụng đoạn trích dưới đây. Tôi đã có một scenerio nơi tôi sẽ tìm thấy người dùng từ một đối tượng tác vụ. nó chứa mảng người dùng nhưng tôi chọn người dùng được chỉ định.

<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

Tôi thích cách tiếp cận của việc tạo ra một chỉ thị để làm điều này (gọi tốt @yurzui).

Cuối cùng tôi đã tìm thấy một bài báo trung bình Chỉ thị "cho phép" giải thích vấn đề này một cách độc đáo và đề xuất một lệnh tùy chỉnh cho phép kết thúc tốt cho trường hợp sử dụng của tôi với các thay đổi mã tối thiểu.

Đây là ý chính (tại thời điểm đăng) với các sửa đổi của tôi:

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

Những thay đổi chính của tôi là:

  • thay đổi tiền tố từ 'ng' thành 'ứng dụng' (bạn nên sử dụng bất kỳ tiền tố tùy chỉnh nào của ứng dụng của bạn)
  • đổi appLet: TsangappLet: T | null

Không chắc chắn tại sao nhóm Angular chưa đưa ra một chỉ thị ngLet chính thức nhưng whatevs.

Tín dụng mã nguồn ban đầu được gửi tới @AustinMatherne


Đây là cách tiếp cận yêu thích của tôi trên trang và nó hiệu quả với tôi.
Skychan

1

Câu trả lời ngắn giúp ai đó

  • Biến tham chiếu mẫu thường tham chiếu đến phần tử DOM trong một mẫu.
  • Cũng tham khảo các thành phần góc và web và chỉ thị.
  • Điều đó có nghĩa là bạn có thể dễ dàng truy cập vào biến ở bất cứ đâu trong một mẫu

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

  • Khai báo biến tham chiếu bằng ký hiệu băm (#)
  • Có thể truyền một biến làm tham số cho một sự kiện

nhập mô tả hình ảnh ở đây

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

* Tuy nhiên, bạn có thể sử dụng trang trí ViewChild để tham chiếu nó bên trong thành phần của bạn.

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

Tham chiếu biến FirstNameInput bên trong Thành phần

@ViewChild('firstNameInput') nameInputRef: ElementRef;

Sau đó, bạn có thể sử dụng this.nameInputRef bất cứ nơi nào trong Thành phần của bạn.

Làm việc với ng-template

Trong trường hợp của ng-template, nó hơi khác một chút vì mỗi mẫu có một bộ biến đầu vào riêng.

nhập mô tả hình ảnh ở đây

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


0

Đối với những người quyết định sử dụng chỉ thị cấu trúc để thay thế *ngIf, hãy nhớ rằng bối cảnh chỉ thị không được kiểm tra theo mặc định. Để tạo một thuộc tính chỉ thị an toàn ngTemplateContextGuardnên được thêm vào, hãy xem Nhập ngữ cảnh của lệnh . Ví dụ:

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

Lệnh này có thể được sử dụng giống như *ngIf, ngoại trừ việc nó có thể lưu trữ các giá trị sai :

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

Hạn chế duy nhất so với *ngIflà Dịch vụ Ngôn ngữ Angular không thể tìm ra loại biến nên không có mã hoàn thành trong các mẫu. Tôi hy vọng nó sẽ được sửa chữa sớm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.