Как заставить рендеринг компонента в Angular 2?


181

Как заставить рендеринг компонента в Angular 2? В целях отладки, работая с Redux, я хотел бы заставить компонент перерисовать его представление, это возможно?


Что вы имеете в виду под «повторным рендерингом». Обновить привязки?
Гюнтер Цохбауэр

Просто быстрый вопрос, почему вам нужно форсировать рендеринг?
Туонг Ле

Ответы:


217

Рендеринг происходит после обнаружения изменений. Для принудительного обнаружения изменений, чтобы значения свойств компонентов, которые изменились, были переданы в DOM (а затем браузер отобразит эти изменения в представлении), вот несколько вариантов:

  • ApplicationRef.tick () - аналогично Angular 1, $rootScope.$digest()т. Е. Проверить полное дерево компонентов
  • NgZone.run (callback) - аналогично $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. Стратегия обнаружения изменений по умолчанию, поэтому я знаю, что не ошибся со стратегией обнаружения изменений.
Гэри

1
@ Гэри, вы должны опубликовать новый вопрос и включить свой компонент и код обслуживания (в идеале, указывайте минимальный шток, демонстрирующий проблему). Распространенная проблема, которую я видел, - это не использование правильного thisконтекста в обратном вызове POST.
Марк Райкок

Вы знаете, могу ли я вручную запускать каналы при каждом изменении? Я пытался вызвать обнаружение изменений, но канал не обновляется ... Я также пытался с этим pure:falseв канале. Это работает, но это слишком дорого (неэффективно) для моего варианта использования.
ncohen

1
@ncohen, я не знаю ни одного способа вручную запустить обновление канала. Вы можете использовать чистый канал и изменять ссылку на объект всякий раз, когда вы хотите запустить обновление. Это обсуждается в разделе «Чистые трубы» документа « Трубы» . В зависимости от вашего варианта использования вы можете захотеть использовать свойство компонента вместо канала. Эта техника кратко обсуждается в конце документа Pipes.
Марк Райкок

1
@ N-ели, все ссылки исправлены.
Марк Райкок

47

TX, нашел обходной путь, который мне был нужен:

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

запуск 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.

Все компоненты внутри моего контейнера возвращаются к крюкам полного жизненного цикла.

В шаблоне:

<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

если бы я мог проголосовать 10000+ раз ..
Язан Халайле

1 вещь, на которую следует обратить внимание - контейнер, исчезающий и появляющийся снова, может вызвать изменение размера, и страница может мигать
ghosh

9

Другие ответы здесь предоставляют решения для запуска циклов обнаружения изменений, которые обновят представление компонента (что отличается от полного повторного рендеринга).

Полное повторное рендер, который уничтожит и переинициализировать компонент (вызов все крюки жизненный цикл и восстановление зрения) может быть сделано с помощью 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);
}

Дополнительно начальный контент должен быть вставлен на AfterContentInitкрючок:

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()соответствующим образом (иногда очень легко понять, где вы должны его использовать, в других случаях это может быть очень сложным).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.