2

General

I have a problem. My component does not re-render without calling changeDetectorRef.markForCheck method.

I have an autocomplete. When input changes I send some async request (just simple HttpClient service and get method). After that, I fill in some internal state.

The code

Note markForCheck call. If I remove this line: nothing works. I noticed that if I remove it and if I click somewhere outside of the component, I see re-render. Right in time when I click somewhere component is re-rendered.

By the way, I realized that markForCheck is working by accident. I just tried something and it worked. I got info about CD mechanisms and CD service from some articles.

Here is my main component:

@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
  }
  // ...

Here is child 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>();
}

These are just fragments. Whole code is available on GitHub.

By the way, I have another component (selected tags block) and I have input showLoader in it. It has exactly same problem.

My thoughts

Probably problem somehow connected to the zones mechanism. From some articles I know, that zone.js monkey-patches some events or XHR calls. And my case is XHR call (I didn't dive deep into HttpClient but it must just make an HTTP call).

What I want

I want to understand why changes are not detecting out of the box there (so I will use markForCheck and I will be ok) or I want to find a mistake in my code.

Hope you will help me there.

Sharikov Vladislav
  • 7,049
  • 9
  • 50
  • 87

1 Answers1

1

It's due to a ChangeDetectionStrategy.OnPush added on a parent component.

In that parent, if the references of his inputs don't change, his subtree components will not be checked for change.

Mohamed Gara
  • 2,665
  • 3
  • 13
  • 19