Почему компонент не обнаруживает внутреннее изменение состояния без `changeDetetectorRef.markForCheck`?

генеральный

У меня проблема. Мой компонент не рендерится без вызова changeDetectorRef.markForCheck метод.

У меня есть автозаполнение. При изменении ввода я отправляю некоторый асинхронный запрос (просто простую службу HttpClient и get метод). После этого я заполняю какое-то внутреннее состояние.

Код

Заметка markForCheck вызов. Если я уберу эту строку: ничего не работает. Я заметил, что если я удаляю его и щелкаю где-то за пределами компонента, я вижу повторную визуализацию. В тот момент, когда я щелкаю куда-то, компонент перерисовывается.

Кстати, я понял, что markForCheck работает случайно. Я только что попробовал, и это сработало. Я получил информацию о механизмах CD и обслуживании CD из некоторых статей.

Вот мой основной компонент:

@Component({
  selector: 'tags-auto-complete',
  template: `
    <tags-internal-auto-complete-input
      // ....
      (inputChanged)="onInputChange($event);"
    ></tags-internal-auto-complete-input>
    <tags-internal-auto-complete-results
      [data]="queryResultsTags"
      // ....
    ></tags-internal-auto-complete-results>
  `,
})
export class TagsAutoCompleteContainerComponent implements OnInit {
  inputChanged = new Subject<string>();
  queryResultsTags: Tag[] = [];

  constructor(
    private tagsService: TagsService,
    private changeDetectorRef: ChangeDetectorRef,
  ) {}

  onInputChange(query: string): void {
    this.inputChanged.next(query);
  }
  ngOnInit() {
    this.inputChanged
      .filter(inputValue => inputValue.length > 0)
      .debounceTime(400)
      .switchMap(query => this.tagsService.getTagsList({ query }))
      .do(() => this.changeDetectorRef.markForCheck()); // note this
      .subscribe((tags: Tag[]) => (this.queryResultsTags = tags)) // here I change the input of inner component
  }
  // ...

Вот дочерний компонент (tags-internal-auto-complete-results):

@Component({
  selector: 'tags-internal-auto-complete-results',
  template: `
    <div class="container">
      <span *ngFor="let tag of data" (click)="selectTag.emit(tag);" class="tag">
        {{tag.name}}
      </span>
    </div>
  `,
  styleUrls: ['./results.styles.css'],
})
export class TagsAutoCompleteResultsComponent {
  @Input() data: Tag[] = [];

  @Output() selectTag = new EventEmitter<Tag>();
}

Это всего лишь фрагменты. Весь код доступен на GitHub.

Кстати, у меня есть другой компонент (блок выделенных тегов), и у меня есть вход showLoader в этом. У него точно такая же проблема.

Мои мысли

Вероятно, проблема как-то связана с механизмом зон. Из некоторых статей я знаю, что zone.js исправляет некоторые события или вызовы XHR. И в моем случае это вызов XHR (я не углублялся в HttpClient, а просто должен был сделать HTTP-вызов).

Что я хочу

Я хочу понять, почему изменения не обнаруживаются прямо из коробки (поэтому я буду использовать markForCheck, и все будет в порядке), или я хочу найти ошибку в своем коде.

Надеюсь, вы мне там поможете.

1 ответ

Это происходит из-за ChangeDetectionStrategy.OnPush, добавленного в родительский компонент.

В этом родительском элементе, если ссылки на его входные данные не изменяются, его компоненты поддерева не будут проверяться на предмет изменений.

Другие вопросы по тегам