วิธีการบังคับให้มีการเรนเดอร์องค์ประกอบใหม่ใน Angular 2


181

วิธีการบังคับให้มีการเรนเดอร์องค์ประกอบใหม่ใน Angular 2 สำหรับจุดประสงค์ในการแก้ปัญหาการทำงานกับ Redux ฉันต้องการบังคับให้องค์ประกอบแสดงภาพอีกครั้งเป็นไปได้หรือไม่


คุณหมายถึงอะไรโดย "แสดงผลซ้ำ" อัปเดตการรวมหรือไม่
GünterZöchbauer

เป็นคำถามสั้น ๆ ว่าทำไมคุณต้องบังคับให้เรนเดอร์ซ้ำ?
Tuong Le

คำตอบ:


217

การแสดงผลเกิดขึ้นหลังจากการตรวจจับการเปลี่ยนแปลง ในการบังคับให้ตรวจจับการเปลี่ยนแปลงดังนั้นค่าคุณสมบัติของส่วนประกอบที่มีการเปลี่ยนแปลงจะถูกแพร่กระจายไปยัง DOM (จากนั้นเบราว์เซอร์จะแสดงการเปลี่ยนแปลงเหล่านั้นในมุมมอง) นี่คือตัวเลือกบางส่วน:

  • ApplicationRef.tick () - คล้ายกับ Angular 1's $rootScope.$digest()- เช่นตรวจสอบโครงสร้างส่วนประกอบแบบเต็ม
  • NgZone.run (การเรียกกลับ)คล้ายกับ$rootScope.$apply(callback)- คือประเมินฟังก์ชันการโทรกลับภายในโซน Angular 2 ฉันคิดว่า แต่ฉันไม่แน่ใจว่าสิ่งนี้จบลงที่การตรวจสอบโครงสร้างส่วนประกอบแบบเต็มหลังจากดำเนินการฟังก์ชันการเรียกกลับ
  • ChangeDetectorRef.detectChanges () - คล้ายกับ$scope.$digest()- เช่นให้ตรวจสอบเฉพาะส่วนนี้และลูก ๆ ของมัน

คุณจะต้องนำเข้าแล้วฉีดApplicationRef, NgZoneหรือChangeDetectorRefเข้าองค์ประกอบของคุณ

สำหรับสถานการณ์เฉพาะของคุณฉันขอแนะนำตัวเลือกสุดท้ายหากมีการเปลี่ยนแปลงองค์ประกอบเดียวเท่านั้น


1
มีรหัสทำงานบน ChangeDetectorRef สำหรับรุ่นสุดท้ายของ angular2 หรือไม่ ตอนนี้ฉันกำลังเผชิญกับสถานการณ์ที่มุมมองไม่ได้รับการอัปเดตหลังจากคำขอโพสต์ของ http เพื่อสร้างผู้ใช้ใหม่และจากนั้นจึงผลักวัตถุใหม่ไปยังรายการผู้ใช้เก่าที่มีอยู่เรียบร้อยแล้ว this is the first time I am facing an update not working in ng2ค่อนข้างแปลกที่ กลยุทธ์การตรวจจับการเปลี่ยนแปลงเป็นค่าเริ่มต้นดังนั้นฉันรู้ว่าฉันไม่ได้ยุ่งกับกลยุทธ์การตรวจจับการเปลี่ยนแปลง
Gary

1
@Gary คุณควรโพสต์คำถามใหม่และรวมส่วนประกอบของคุณและรหัสบริการของคุณ (โดยหลักแล้วควรมีผู้รวบรวมที่แสดงถึงปัญหาน้อยที่สุด) ปัญหาทั่วไปที่ฉันเห็นไม่ได้ใช้thisบริบทที่เหมาะสมในการโทรกลับ POST
Mark Rajcok

คุณรู้หรือไม่ว่าฉันสามารถทริกเกอร์ท่อด้วยตนเองเมื่อใดก็ตามที่ฉันทำการเปลี่ยนแปลง ฉันพยายามที่จะทริกเกอร์การตรวจจับการเปลี่ยนแปลง แต่ไปป์ไม่อัปเดต ... ฉันได้ลองด้วยpure:falseในไพพ์แล้ว มันใช้งานได้ แต่มันแพงเกินไป (ไม่มีประสิทธิภาพ) สำหรับกรณีการใช้งานของฉัน
ncohen

1
@ ncohen ฉันไม่ทราบวิธีใด ๆ ที่จะทริกเกอร์การอัปเดตด้วยตนเอง คุณสามารถใช้ไพพ์บริสุทธิ์และเปลี่ยนการอ้างอิงวัตถุเมื่อใดก็ตามที่คุณต้องการเรียกการอัพเดท นี้จะกล่าวถึงในส่วน "ท่อบริสุทธิ์" ของท่อ doc ขึ้นอยู่กับกรณีการใช้งานของคุณคุณอาจต้องการใช้คุณสมบัติส่วนประกอบแทนไพพ์ เทคนิคนี้จะกล่าวถึงสั้น ๆ ในตอนท้ายของเอกสาร Pipes
Mark Rajcok

1
@ ไม่ได้แก้ไขลิงก์ทั้งหมดจะได้รับการแก้ไข
Mark Rajcok

47

tx พบวิธีแก้ปัญหาที่ฉันต้องการ:

  constructor(private zone:NgZone) {
    // enable to for time travel
    this.appStore.subscribe((state) => {
        this.zone.run(() => {
            console.log('enabled time travel');
        });
    });

running zone.run จะบังคับให้องค์ประกอบแสดงผลอีกครั้ง


6
appStore คืออะไรในบริบทนี้ - ชนิดของตัวแปรและชนิดของมันคืออะไร? ดูเหมือนว่าจะเป็นที่สังเกตได้ ... แต่สิ่งที่สังเกตได้ของฉันอยู่ในองค์ประกอบที่ฉันต้องการรีเฟรชเมื่อคลิกปุ่ม ... และฉันไม่ทราบวิธีการเข้าถึงวิธีการองค์ประกอบเด็ก / ตัวแปรจากผู้ปกครอง / ตำแหน่งปัจจุบัน
Abdeali Chandanwala

28

วิธีการของ ChangeDetectorRef

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';

export class MyComponent {

    constructor(private cdr: ChangeDetectorRef) { }

    selected(item: any) {
        if (item == 'Department')
            this.isDepartment = true;
        else
            this.isDepartment = false;
        this.cdr.detectChanges();
    }

}

14

ฉันบังคับให้โหลดองค์ประกอบของฉันอีกครั้งโดยใช้ * ngIf

ส่วนประกอบทั้งหมดภายในคอนเทนเนอร์ของฉันกลับไปที่ hooks lifecycle แบบเต็ม

ในเทมเพลต:

<ng-container *ngIf="_reload">
    components here 
</ng-container>

จากนั้นในไฟล์ ts:

public _reload = true;

private reload() {
    setTimeout(() => this._reload = false);
    setTimeout(() => this._reload = true);
}

ขอบคุณสำหรับสิ่งนี้ @loonis! setTimeout()ฉันรู้สึกเช่นนี้ควรจะทำงานและฉันมีทุกอย่างยกเว้น ตอนนี้ฉันกำลังทำงานกับโซลูชันที่ง่ายและมีน้ำหนักเบา!
LHM

ถ้าฉันสามารถโหวตได้มากกว่า 10,000 ครั้ง ..
Yazan Khalaileh

สิ่งหนึ่งที่ควรทราบ - คอนเทนเนอร์ที่หายไปและปรากฏขึ้นอีกครั้งอาจทำให้เกิดการเปลี่ยนแปลงขนาดและหน้าเว็บอาจสั่นไหว
ghosh

9

คำตอบอื่น ๆ ที่นี่มีวิธีแก้ปัญหาสำหรับการทริกเกอร์รอบการตรวจจับการเปลี่ยนแปลงที่จะอัปเดตมุมมองของส่วนประกอบ (ซึ่งไม่เหมือนกับการแสดงผลซ้ำทั้งหมด)

เต็มรูปแบบอีกครั้งทำให้ซึ่งจะทำลายและ reinitialize ส่วนประกอบ (เรียกตะขอวงจรชีวิตและการสร้างมุมมอง) สามารถทำได้โดยใช้ng-template, ng-containerและViewContainerRefในทางต่อไปนี้:

<div>
  <ng-container #outlet >
  </ng-container>
</div>

<ng-template #content>
  <child></child>
</ng-template>

จากนั้นในส่วนที่มีการอ้างอิงถึงทั้งสอง#outletและ#contentเราสามารถล้างเนื้อหาของร้านค้าและแทรกอินสแตนซ์อื่นขององค์ประกอบลูก:

@ViewChild("outlet", {read: ViewContainerRef}) outletRef: ViewContainerRef;
@ViewChild("content", {read: TemplateRef}) contentRef: TemplateRef<any>;

private rerender() {
    this.outletRef.clear();
    this.outletRef.createEmbeddedView(this.contentRef);
}

นอกจากนี้เนื้อหาเริ่มต้นควรถูกแทรกลงในAfterContentInithook:

ngAfterContentInit() {
    this.outletRef.createEmbeddedView(this.contentRef);
}

วิธีการแก้ปัญหาการทำงานเต็มรูปแบบที่สามารถพบได้ที่นี่https://stackblitz.com/edit/angular-component-rerender


1

ChangeDetectorRef.detectChanges()มักจะเป็นวิธีที่เน้นมากที่สุดในการทำเช่นนี้ ApplicationRef.tick()มักเป็นวิธีการค้อนขนาดใหญ่เกินไป

ในการใช้งานChangeDetectorRef.detectChanges()คุณจะต้องใช้สิ่งนี้ที่ส่วนบนของส่วนประกอบ:

import {  ChangeDetectorRef } from '@angular/core';

... จากนั้นคุณมักจะอ้างว่าเมื่อคุณฉีดเข้าไปในคอนสตรัคเตอร์ของคุณเช่นนี้:

constructor( private cdr: ChangeDetectorRef ) { ... }

จากนั้นในสถานที่ที่เหมาะสมคุณเรียกมันว่า:

this.cdr.detectChanges();

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

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