วิธีประกาศตัวแปรในเทมเพลตในเชิงมุม


202

ฉันมีแม่แบบต่อไปนี้:

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

และต้องการจบด้วย:

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

มีวิธีทำอย่างไร?


ฉันสนใจที่จะรู้ว่าสิ่งที่ต้องการ / กรณีการใช้งานที่ต้องการเปลี่ยนชื่อของพารามิเตอร์ที่มีผลผูกพันเช่นตัวอย่างนี้หรือไม่?
LDJ

31
มันเป็นเพียงเพื่อป้องกันการทำซ้ำบางสิ่งบางอย่างเช่นแท็บ [องค์ประกอบ] .val โดยตัวอย่าง ฉันรู้ว่าฉันสามารถแก้ไขปัญหาในองค์ประกอบได้ แต่ฉันแค่ดูวิธีการทำในแม่แบบ (แม้ว่าฉันจะไม่ได้ลงเอยด้วยวิธีแก้ปัญหานั้น)
Scipion

2
@LDJ กรณีการใช้ตัวอย่างหนึ่งตัวอย่าง: ประสิทธิภาพ ใช้ตัวอย่างของstackblitz.com/angular/… <mat-checkbox [checked] = "descendantAllSelected (node)" [indeterminate] = "descendantsPartiallySelected (node)" (เปลี่ยน) = "todoItemSelectionToggle (node)"> {{โหนด item}} </mat-checkbox> อันที่จริงลูกหลานส่วนใหญ่เลือก () โทรลูกหลานลูกหลานทั้งหมด () มันหมายความว่าบางครั้งลูกหลาน AllSelected เรียกว่าสองครั้ง หากมีตัวแปรในตัวเครื่องสามารถหลีกเลี่ยงได้
Steven.Xi

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

@dasfdsa ผมเชื่อว่าuser1 === userทำให้คุณทำอย่างใดอย่างหนึ่ง*ngIf="{name:'john'} as user1หรือ*ngIf="{name:'john'};let userในขณะที่คำตอบของ yurzui
CPHPython

คำตอบ:


175

ปรับปรุง

เราสามารถสร้าง directive like *ngIfและเรียกมันว่า*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);
  }
}

ด้วย*ngVarคำสั่งนี้เราสามารถใช้ต่อไปนี้

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

หรือ

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

หรือ

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

หรือ

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

ตัวอย่าง Plunker Angular4 ngVar

ดูสิ่งนี้ด้วย

คำตอบเดิม

เชิงมุม 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

ดู

<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) ถ้าคุณไม่ต้องการสร้างเสื้อคลุมเหมือนdivคุณสามารถใช้ng-container

ดู

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

ตามที่ @ Keith พูดถึงในความคิดเห็น

สิ่งนี้จะได้ผลในกรณีส่วนใหญ่ แต่มันไม่ได้เป็นวิธีการแก้ปัญหาทั่วไปเพราะมันขึ้นอยู่กับตัวแปรเป็นความจริง

ดูการอัปเดตสำหรับวิธีการอื่น


10
นี้จะทำงานได้ในกรณีส่วนใหญ่ แต่มันไม่ได้เป็นวิธีการแก้ปัญหาทั่วไปเพราะมันขึ้นอยู่กับvariableความจริง
Keith

6
@ Keith ขอบคุณที่ชี้นำสิ่งนี้ คุณสามารถดูคำตอบที่อัปเดตของฉันได้
yurzui

3
นี่ควรเป็นคำตอบใหม่เนื่องจาก 1) ทันสมัยกว่าโซลูชันอื่น ๆ 2) สรุปคำขอดึงข้อมูลที่เชื่อมโยงในคำตอบปัจจุบันและประหยัดเวลาได้มาก 3) มีตัวอย่างแบบอินไลน์มากกว่าลิงก์ภายนอก ขอบคุณที่แสดงสิ่งนี้ AFAIK วัตถุใดก็ตามที่อยู่ในห่อ{}จะประเมินความจริงดังนั้นโซลูชันนี้จึงค่อนข้างแข็งแกร่ง
kvanberendonck

4
ตัวอย่างเช่นปุ่มที่ขยายได้: *ngIf="{ expanded: false } as scope"จากนั้นหากคุณใช้ bootstrap คุณสามารถใช้[ngClass]="{ 'in': scope.expanded }"และ(click)="scope.expanded = !scope.expanded"แทนที่จะเพิ่มสิ่งใด ๆ ลงในjs/ tsไฟล์ของคุณ
kvanberendonck

1
ปัญหา GitHub ที่เกี่ยวข้อง (ชี้ให้เห็นการใช้ง่าย ๆ*ngIfแทนที่จะเป็นของ ngvar ที่กำหนดเอง): github.com/angular/angular/issues/14985
phil294

81

น่าเกลียด แต่:

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

เมื่อใช้กับ async pipe:

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

4
นั่นเป็นสิ่งที่ฉันคิดขึ้นมาโดยสัญชาตญาณ - มันใช้ได้*ngFor="let a of [(someStream$ | async).someA]ดีเช่นกัน ฉันเดาว่าใช้กับ<ng-container>มันทำหน้าที่ค่อนข้างถูกต้อง!
Angelos Pikoulas

2
ในกรณีของ*ngForโปรดทราบว่าเนื้อหาที่ซ้อนกันทั้งหมดจะถูกสร้างขึ้นใหม่หากการเปลี่ยนแปลงค่าตัวแปรจนกว่าคุณจะระบุtrackByฟังก์ชั่นที่ส่งกลับรหัสเดียวกันสำหรับค่าทั้งหมด
Valeriy Katkov

76

คุณสามารถประกาศตัวแปรในรหัส html โดยใช้templateองค์ประกอบใน Angular 2 หรือng-templateใน Angular 4+

แม่แบบมีวัตถุบริบทที่มีคุณสมบัติที่สามารถกำหนดให้กับตัวแปรโดยใช้letไวยากรณ์ผูกพัน โปรดทราบว่าคุณต้องระบุทางออกสำหรับเทมเพลต แต่สามารถอ้างอิงถึงตัวเองได้

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

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

คุณสามารถลดจำนวนรหัสได้โดยใช้$implicitคุณสมบัติของวัตถุบริบทแทนคุณสมบัติที่กำหนดเอง

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

วัตถุบริบทสามารถเป็นวัตถุที่แท้จริงหรือการแสดงออกที่มีผลผูกพันอื่น ๆ แม้แต่ท่อก็ยังใช้งานได้เมื่ออยู่ในวงเล็บ

ตัวอย่างที่ถูกต้องของngTemplateOutletContext:

  • [ngTemplateOutletContext]="{ aVariable: 123 }"
  • [ngTemplateOutletContext]="{ aVariable: (3.141592 | number:'3.1-5') }"
  • [ngTemplateOutletContext]="{ aVariable: anotherVariable }" ใช้กับ let-a="aVariable"
  • [ngTemplateOutletContext]="{ $implicit: anotherVariable }" ใช้กับ let-a
  • [ngTemplateOutletContext]="ctx"ctxทรัพย์สินสาธารณะอยู่ที่ไหน

เพื่อให้มันทำงานฉันต้องเปลี่ยนรหัสของคุณจาก '<template ... ' เป็น '<ng-template ... '
Humppakäräjät

2
อ๋อคุณสามารถใช้<template>ในเชิงมุม 2. คุณสามารถใช้อย่างใดอย่างหนึ่ง<template>หรือ<ng-template>ในเชิงมุม 4 <ng-template>แต่คุณควรใช้เท่านั้น เชิงมุม 5 <template>ลดลงการสนับสนุน
Steven Liekens

มีไว้tเพื่ออะไร
matttm

1
@matttm เป็นตัวแปรแม่แบบที่ร้านค้า#t ng-templateมันถูกใช้ใน[ngTemplateOutlet]="t"การทำอ้างอิงแม่แบบ ng เอง
Steven Liekens

นี่มันแปลกประหลาด แต่ใช้งานได้! เชิงมุมควรทำให้สิ่งนี้ง่ายขึ้นด้วยคำสั่งตัวแปรในตัว ขอบคุณ
TetraDev

57

อัปเดต 3

ปัญหา 2451 ได้รับการแก้ไขใน Angular 4.0.0

ดูสิ่งนี้ด้วย

อัปเดต 2

สิ่งนี้ไม่รองรับ

มีตัวแปรเทมเพลต แต่ไม่รองรับการกำหนดค่าเอง พวกเขาเท่านั้นที่สามารถใช้ในการอ้างถึงองค์ประกอบที่พวกเขาจะนำไปใช้ชื่อการส่งออกของคำสั่งหรือส่วนประกอบและตัวแปรขอบเขตการสั่งโครงสร้างเช่นngFor,

ดูเพิ่มเติมที่https://github.com/angular/angular/issues/2451

อัปเดต 1

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

และเริ่มต้นเช่น

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

หรือ

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

และใช้ตัวแปรเช่น

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

(ไม่ผ่านการทดสอบ)

  • #aVariableสร้างการอ้างอิงถึงVarDirective( exportAs: 'var')
  • var="abc"instantiates VarDirectiveและส่งผ่านค่าสตริง"abc"ไปเป็นค่าของมัน
  • aVariable.varอ่านค่าที่กำหนดให้กับอินพุตvarคำสั่งvar

มันจะเป็นไปไม่ได้หรือไม่ที่จะสร้างคำสั่งโครงสร้างให้ทำเช่นนั้น?
Scipion

หากคุณต้องการสิ่งนี้ซ้ำ ๆ คำสั่งอาจทำสิ่งที่คุณต้องการ คำสั่งโครงสร้างสร้างมุมมองของตัวเองซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ
GünterZöchbauer

1
@ GünterZöchbauerเป็นสิ่งที่ดีมาก ฉันรู้ว่ามันน่าจะเป็นวิธีที่ดีกว่าในการคำนวณ / จัดเตรียมตัวแปรในcomponent.tsไฟล์ แต่มันง่ายกว่ามากสำหรับฉันที่จะให้พวกเขาดูในบางกรณีเนื่องจากรูปแบบการซิงค์ที่ฉันใช้งานตลอดทั้งแอพของฉัน ฉันใช้ประโยชน์จากกฎการอ้างอิงจาวาสคริปต์เมื่อตัวแปรที่แตกต่างชี้ไปที่วัตถุเดียวกัน
AmmarCSE

There is no directive with "exportAs" set to "var"ฉันได้รับข้อผิดพลาดเช่น ใครช่วยได้โปรดบอกฉันว่าฉันทำผิดพลาดได้ไหม? ฉันได้ใช้คำสั่งข้างต้น
Partha Sarathi Ghosh

บางทีคุณอาจจะไม่ได้เพิ่มคำสั่งการของdeclarations: [...] @NgModule()หากนี่ไม่ใช่ปัญหาโปรดสร้างคำถามใหม่และระบุรหัสที่อนุญาตให้วินิจฉัยปัญหา
GünterZöchbauer

12

ฉันอยากจะแนะนำสิ่งนี้: https://medium.com/@AustinMatherne/angular-let-directive-a168d4248138

คำสั่งนี้อนุญาตให้คุณเขียนสิ่งที่ชอบ:

<div *ngLet="'myVal' as myVar">
  <span> {{ myVar }} </span>
</div>

1
แบบนี้ แต่ * สงวนไว้สำหรับคำสั่งเชิงโครงสร้างซึ่งนี่ก็ไม่ใช่ +1
danday74

11

นี่เป็นคำสั่งที่ฉันเขียนซึ่งขยายการใช้พารามิเตอร์ decorAs exportAs และอนุญาตให้คุณใช้พจนานุกรมเป็นตัวแปรโลคอล

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

คุณสามารถใช้มันได้ในเทมเพลต:

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

แน่นอน #local สามารถเป็นชื่อตัวแปรท้องถิ่นที่ถูกต้อง


ไม่ผ่านการสร้าง 'การผลิต' ตามที่เป็นอยู่ (ยังแสดงเป็นข้อผิดพลาดโดย IDEs) เพิ่ม[key: string]: any;ไปยังที่Classจะได้รับรอบนี้
Charly

7

ในกรณีที่คุณต้องการรับการตอบสนองของฟังก์ชั่นและตั้งเป็นตัวแปรคุณสามารถใช้มันได้ดังต่อไปนี้ในเทมเพลตโดยใช้ng-containerเพื่อหลีกเลี่ยงการแก้ไขเทมเพลต

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

และวิธีการในองค์ประกอบสามารถเป็นเช่น

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

5

หากคุณต้องการการสนับสนุนการเติมข้อความอัตโนมัติจากภายในในเทมเพลตของคุณจาก Angular Language Service :

ซิงโคร:

myVar = { hello: '' };

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

ใช้ท่อ async:

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

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

2

ฉันกำลังใช้ 6x เชิงมุมและฉันก็ลงเอยด้วยการใช้ตัวอย่างด้านล่าง ฉันเป็นฉากที่ฉันจะหาผู้ใช้จากวัตถุภารกิจ มันมีอาร์เรย์ของผู้ใช้ แต่ฉันจะเลือกผู้ใช้ที่ได้รับมอบหมาย

<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

มันง่ายกว่ามากโดยไม่ต้องการอะไรเพิ่มเติม ในตัวอย่างของฉันฉันประกาศตัวแปร "open" แล้วใช้มัน

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

คุณตั้งชื่อแท็กไม่ใช่การประกาศตัวแปร
Amirreza

1
@Amirreza เพื่อความแม่นยำฉันใช้ ElementRef เพื่อเก็บค่า
Jack Rus

! น่ากลัว ฉันต้องใช้"?"เพราะฉันมีข้อความ "ตัวระบุ 'ค่า' ไม่ได้กำหนด" เช่นนี้ => "เปิด?. มูลค่า" แต่มันทำงาน !!
A. Morel

1

ฉันชอบวิธีการในการสร้างคำสั่งให้ทำเช่นนี้ (โทรดี @ yurzui)

ฉันลงเอยด้วยการหาบทความขนาดกลางAngular "ให้" คำสั่งที่อธิบายปัญหานี้ได้อย่างดีและเสนอคำสั่งให้กำหนดเองซึ่งลงเอยด้วยการทำงานที่ยอดเยี่ยมสำหรับกรณีการใช้งานของฉันด้วยการเปลี่ยนแปลงรหัสน้อยที่สุด

นี่คือส่วนสำคัญ (ตอนที่โพสต์) พร้อมการแก้ไขของฉัน:

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

การเปลี่ยนแปลงหลักของฉันคือ:

  • เปลี่ยนคำนำหน้าจาก 'ng' เป็น 'app' (คุณควรใช้ส่วนนำหน้าที่กำหนดเองของแอพของคุณ)
  • เปลี่ยนappLet: Tเป็นappLet: T | null

ไม่แน่ใจว่าทำไมทีม Angular ไม่ได้ทำเพียงคำสั่ง ngLet อย่างเป็นทางการ แต่เป็นคำพูด

เครดิตซอร์สโค้ดต้นฉบับไปที่ @AustinMatherne


นี่เป็นวิธีการที่ฉันโปรดปรานในหน้านี้และใช้ได้สำหรับฉัน
Skychan

1

คำตอบสั้น ๆ ที่ช่วยให้ใครบางคน

  • ตัวแปรการอ้างอิงเทมเพลตมักอ้างอิงถึงองค์ประกอบ DOM ภายในเทมเพลต
  • ยังอ้างถึงองค์ประกอบเชิงมุมหรือเว็บและคำสั่ง
  • นั่นหมายความว่าคุณสามารถเข้าถึง varible ได้ทุกที่ในเทมเพลต

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

  • ประกาศตัวแปรอ้างอิงโดยใช้สัญลักษณ์แฮช (#)
  • สามารถส่งตัวแปรเป็นพารามิเตอร์ในเหตุการณ์

ป้อนคำอธิบายรูปภาพที่นี่

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

* อย่างไรก็ตามคุณสามารถใช้ ViewChild decorator เพื่ออ้างอิงภายในคอมโพเนนต์ของคุณ

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

การอ้างอิงตัวแปรชื่อแรกอินพุตภายในคอมโพเนนต์

@ViewChild('firstNameInput') nameInputRef: ElementRef;

หลังจากนั้นคุณสามารถใช้ this.nameInputRef ได้ทุกที่ภายใน Component ของคุณ

ทำงานกับ ng-template

ในกรณีของ ng-template มันแตกต่างกันเล็กน้อยเพราะแต่ละ template มีชุดตัวแปรอินพุตของตัวเอง

ป้อนคำอธิบายรูปภาพที่นี่

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


1

สำหรับผู้ที่ตัดสินใจใช้คำสั่งโครงสร้างแทนการ*ngIfจำไว้ว่าบริบทคำสั่งไม่ได้ถูกตรวจสอบโดยค่าเริ่มต้น เมื่อต้องการสร้างngTemplateContextGuardคุณสมบัติคำสั่งที่ปลอดภัยชนิดควรเพิ่มดูการพิมพ์บริบทของคำสั่งพิมพ์ดีดบริบทสั่งของตัวอย่างเช่น:

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

คำสั่งนั้นสามารถใช้ได้เหมือน*ngIfกันยกเว้นว่ามันสามารถเก็บค่าเท็จได้ :

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

ข้อเสียเปรียบเพียงอย่างเดียวเมื่อเทียบกับ*ngIfคือ Angular Language Service ไม่สามารถระบุประเภทของตัวแปรได้ดังนั้นจึงไม่มีการกรอกรหัสในแม่แบบ ฉันหวังว่ามันจะได้รับการแก้ไขในไม่ช้า


วิธีนี้ใช้งานได้ แต่ไม่ได้ใช้ระบบ IntelliSense ฉันใช้มุม 8
Tx_monster
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.