1

I am trying to update the view of an Angular Component when data changes. I was going through the ChangeDetectionStrategy.OnPush and thought this might solve my problem. However, I didn't succeed.

I have a upcoming-studies component and this is a child component of studies component.

upcoming-studies.component.html

<div>
  <ngx-datatable
    #upcoming
    class="material"
    [headerHeight]="50"
    [footerHeight]="50"
    [rowHeight]="'auto'"
    [rows]="rows"
    [columns]="columns"
    [columnMode]="'flex'"
    [selected]="selected"
    [selectCheck]="selectUnselect"
    [selectionType]="'single'"
    [limit]="10"
    (select)='onSelect($event)'>
  </ngx-datatable>
</div>

The [rows]="rows", here variable rows gets data from backend using HTTP call. I am trying to update the view, whenever this variable rows gets updated or whenever HTTP call is made. Here is part of my ts file.

upcoming-studies.component.ts

        @Component({
        selector: 'rmis-upcomingstudies',
        templateUrl: './upcoming-studies.component.html',
        changeDetection: ChangeDetectionStrategy.OnPush,
        styleUrls: ['./upcoming-studies.component.css']
    })
    export class UpcomingStudiesComponent implements OnInit, OnChanges {

        // ToDo: Check specific data types
        @Input() rows = [];
        @Input() temp = [];
     constructor(private studiesService: StudiesService,
                    private exceptionService: ExceptionService,
                    private cd: ChangeDetectorRef) {
            // Set up the column labels and properties for the upcoming study table
            this.columns = [
                {prop: 'protocol_number', name: 'Protocol Number', flexGrow: 1},
                {prop: 'start_date', name: 'Start Date', flexGrow: 1},
                {prop: 'end_date', name: 'End Date', flexGrow: 1},
                {prop: 'patient_count', name: 'Patient Count', flexGrow: 1},
                {prop: 'trial_country', name: 'Country', flexGrow: 1},
            ];
        }

        ngOnInit() {
            this.getUpcomingStudies();
        }
        ngOnChanges() {
        //Trying to print the `rows` and `temp` to see if changes in these variable are detected. so that I can run cd.detectChangese() method to update the view.
}

First time the HTTP call happens onInIt(), the next time I am calling getUpcomingStudies() of upcoming-studies-component.ts from a differnet Component (Neither Parent not Child) and that is when I need to update the view. The HTTP call is made and updated data is also returned that updates temp and rows variable but the number of rows on UI does not change. Basically, it reduces the number of rows from upcoming studies view, so I need to update that view so it shows 1 less row. As you can see, I have tried using ChangeDetectionStrategy, but I am not sure on how to make this thing work. Any input would be appreciated. Thanks!

Update 1: I think, my @Input() variables are not getting updated and thus no change detection, as I tried printing them in ngOnChanges, but ngOnChanges never got executed. Is there any other way to perform the same action?

Update 2: I have made some changes in the ways rows and temp are updated. As I had mentioned earlier, upcoming-studies component is a child of studies component. I am passing rows and temp from studies to upcoming-studies component as shown below:

studies.component.html

  <upcoming-studies [rows] = rows [temp] = temp>
    </upcoming-studies>

The studies component makes an HTTP call to get the data(rows and temp).

studies.component.ts

public rows = [];
public temp = [];
ngOnInit() {
    this.getUpcomingStudies();
}
    getUpcomingStudies() {
    this.studiesService
        .getUpComingStudies()
        .subscribe(
            // the first argument is a function which runs on success
            (data) => {
                console.log('Data from backend-upcoming', data);
                this.temp = data['records'];
                // this.temp = JSON.parse(JSON.stringify(this.temp));
                this.rows = this.studiesService.util_to_cal_time(data['records']);
                // this.rows = JSON.parse(JSON.stringify(this.rows));
                // this.rows = data.records;

            },
            // the second argument is a function which runs on error
            (err: HttpErrorResponse) => {
                this.exceptionService.errorResponse(err);
            },
            // the third argument is a function which runs on completion
            () => {
            }
        );
}

For first time, ngOnInit in studies component, calls the getUpcomingStudies method. After this, it's called from another component. Let me know If you need more details.

util_to_cal_time - It just modify the time and return the same array.

util_to_cal_time(studies: Array<any>) {
        for (const each of studies) {
            const sDate = new Date(each.start_date * 1000);
            each.start_date = sDate.getUTCMonth() + 1 + '/' + sDate.getUTCDate() + '/' + sDate.getFullYear();
            const eDate = new Date(each.end_date * 1000);
            each.end_date = eDate.getUTCMonth() + 1 + '/' + eDate.getUTCDate() + '/' + eDate.getFullYear();
        }
        console.log(studies);
        return studies;
    }
7
  • Angular does not perform a deep diff of Objects and Arrays when redrawing the screen. That means that if an object in your array is modified -- Angular will be none the wiser. You could try assigning your rows property a completely new reference (clone the array) each time it is updated. For example this.rows = JSON.parse(JSON.stringify(this.rows)) or if your using Lodash: this.rows = _.cloneDeep(this.rows). If that works -- that will at least confirm your issue. Commented Aug 16, 2017 at 23:04
  • did you figure it out? Commented Aug 17, 2017 at 4:39
  • No..not yet @Maximus... I am seeing that ngOnChanges detects change in rows and temp variables only first time.. Then any other change in these arrays is not being detected as pointed out by @Graztok. And hence I am not able to run ChangeDetection. Do you have any input on this? Commented Aug 17, 2017 at 4:47
  • how do you update rows, show some code Commented Aug 17, 2017 at 4:50
  • @Maximus - I have added Update 2, that should make things more clear. Let me know If you need any other info. Commented Aug 17, 2017 at 5:13

2 Answers 2

2

The solution is to trigger the change detection in the array itself. So, once you are done populating the "rows" and "columns" component variables, just add the following two lines of code.

this.rows = [...this.rows];
this.columns = [...this.columns];
Sign up to request clarification or add additional context in comments.

Comments

0

I was able to solve this problem. There was no issue with ChangeDetection (It was even not required in my case). In my post above where I say After this, it's called from another component. Let me know If you need more details. , the problem was in the way I was making the call to the component method. I was using the new keyword to create the object of that component in order to call the getUpomingStudies method that and was giving me a fresh instance of the component each time. No wonder, my old component was never seeing the changes. It was a really silly mistake but that made me read and learn a lot about ChangeDetectionStrategy. Thanks everyone for their inputs!

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.