Why do I need to call detectChanges() with the default change detection strategy?

77,630

Solution 1

I have solved the problem.

Another component sending notifications to this component was running Observable.fromEvent() outside the Angular zone, so change detection was not happening automatically in response to these events. This post on zones and this StackOverflow post on the issue held the solution!

Solution 2

You can Input your searchResults into the child Components

@Input() searchResults;

after that you pass it trough the parent template

// parent template
<app-child [searchResults]="searchResults"></app-child>

you can use it in the child template

// child template 
<my-date-picker [ngModel]="searchResults"></my-date-picker>

after that you can 'listen' for changes in this property in the child Component

export class ChildComponent implements OnChanges {

  @Input() searchResults;

  constructor() { }

  ngOnChanges() {
    // some code
  }

Every time searchResults is changed the changes will be populated in the child component, the values in the child template will get the new value.

Share:
77,630

Related videos on Youtube

serlingpa
Author by

serlingpa

Updated on July 09, 2022

Comments

  • serlingpa
    serlingpa almost 2 years

    I am working on an Angular 4 app, but I am having a problem inasmuch as I am having to call this.changeDetectorRef.detectChanges(); to update the view when the model changes. For example, I have this code which is for pagination:

    changePage(pageNumber) {
      this.currentPage = pageNumber;
      // why do I need to do this?
      this.changeDetectorRef.detectChanges();
    }
    

    I have expicitly set the change detection strategy on the component to ChangeDetectionStrategy.Default but this has no effect. It is happening here too when subscribing to an observable:

    showResults(preference) {
      this.apiService.get('dining_autocomplete/', `?search=${preference}`)
        .subscribe((results) => {
          this.searchResults = results;
          // why do I need to do this?
          this.changeDetectorRef.detectChanges();
      });
    }
    

    If I console.log() this.searchResults in the TypeScript, I get the expected results, but if I use {{ searchResults }} in the HTML, it doesn't update until some other event happens, presumably when it goes through another digest cycle.

    What could be going on?

    == EDIT ===========================================================

    The code for my component looks like this:

    import {ChangeDetectorRef, Component, Input, OnChanges} from "@angular/core";
    
    import * as _ from 'lodash';
    
    @Component({
      selector: 'dining-search-results',
      templateUrl: './dining-search-results.template.html',
      styleUrls: ['./dining-search-results.style.scss'],
    
    })
    export class DiningSearchResultsComponent {
      @Input() searchResults: any[];
      @Input() hotTablesOnly: boolean = false;
      @Input() memberBenefitsOnly: boolean = false;
    
      numberOfTagsToDisplay: number = 3;
      resultsPerPage: number = 5;
      currentPage: number = 1;
    
      constructor(private changeDetectorRef: ChangeDetectorRef) {
      }
    
      get filteredResults() {
        return this.searchResults ?
          this.searchResults.filter((r) => !((this.hotTablesOnly && !r.has_hot_table)
            || (this.memberBenefitsOnly && !r.has_member_benefit))) : [];
      }
    
      get pagedResults() {
        return _.chain(this.filteredResults)
          .drop(this.resultsPerPage * (this.currentPage - 1))
          .take(this.resultsPerPage)
          .value();
      }
    
      get totalPages(): number {
        return Math.ceil(this.filteredResults.length / this.resultsPerPage);
      }
    
      getInitialTags(tagsArray: any[], count: number): any[] {
        return _.take(tagsArray, count);
      }
    
      changePage(pageNumber) {
        this.currentPage = pageNumber;
        // why do I need to do this?
        this.changeDetectorRef.detectChanges();
      }
    }
    

    When changePage() is called, and this.currentPage is updated, the changes are not reflected in the HTML unless I call detectChanges().

    • smnbbrv
      smnbbrv almost 7 years
      reasons could be different: e.g. parent component has a strategy onPush and the value is proxied or e.g. the component is initialised outside of Angular zone. You should reproduce it in plnkr and while reproducing you will most likely find the reason on your own
    • onetwo12
      onetwo12 almost 7 years
      I think you can try using ngOnChanges. You have to @Input the variable in the child component's and it will update the templates also.
    • Max Koretskyi
      Max Koretskyi almost 7 years
      you don't have to call, this is not right. Can you show your full code or create a plunker?
    • serlingpa
      serlingpa almost 7 years
      @smnbbrv there are no components in the app using the onPush strategy.
    • serlingpa
      serlingpa almost 7 years
      @Maximus I have added the code of my component to my post.
    • Max Koretskyi
      Max Koretskyi almost 7 years
      okay, now I have two more questions, how does './dining-search-results.template.html', look and how is changePage triggered?
    • serlingpa
      serlingpa almost 7 years
      As it happens @Maximus, the code has changed considerably since I wrote this post and the problem remains. I have posted on the GitHub issue page for Angular since I really do believe this to be a bug. Whether it's my bug or Angular's is another matter ;) please see github.com/angular/angular/issues/18320
  • serlingpa
    serlingpa almost 7 years
    But the problem is occurring in the ChildComponent properties that aren't bound by @Input. For example, I have a changePage(pageNumber) but when the page number is updated, the view is not being updated.
  • onetwo12
    onetwo12 almost 7 years
    ngOnChanges only works in combination with @Input