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