6

I am currently using Angular 2 with Electron (which is basically using Node and web technologies to create a GUI).

All I want to do is list the files of the current directory.

Unfortunately, the variable "this.files" does not seem to update the data shown on the UI. Surprisingly however, when I click the dummy button thats linked to an empty method, it suddenly update. How do I fix this issue and whats the problem?

import {Component} from "@angular/core";
const fs = require('fs');

@Component(<any>{
    selector: 'files',
    template: `
<h2>Files</h2>

<ul *ngFor="let file of files">
    <li>{{ file }}</li>
</ul>

<button (click)="showFiles">Show Files</button>
`,
})
export class FilesComponent {
    files: any[];
    cwd: string;

    constructor() {}

    ngOnInit() {
        this.cwd = __dirname;
        this.files = [];
        this.loadFiles();
    }

    loadFiles() {
        fs.readdir(this.cwd, (err, dir) => {
            for (let filePath of dir) {
                console.log(filePath);
                this.files.push(filePath);
            }
        });
    }

    showFiles() {
        // Empty method
        // Shows the files for some reason despite nothing happening
    }
}
halfer
  • 19,824
  • 17
  • 99
  • 186
Yahya Uddin
  • 26,997
  • 35
  • 140
  • 231
  • What does `showFiles()` do? – Günter Zöchbauer Jun 30 '16 at 17:55
  • literally nothing in the code. But for some reason the UI shows all the files when I click it. – Yahya Uddin Jun 30 '16 at 17:56
  • 2
    Regarding why it works when you click: click events are monkey-patched by Angular's zone, so all of your template data bindings are checked after the click event handler runs. Since `files` changed since the last check, the view is updated. I don't know how Electron works, but as @Günter already answered, async events in Node.js are likely not monkey-patched, so Angular change detection is not triggered when the `readdir` callback runs. – Mark Rajcok Jun 30 '16 at 18:41

2 Answers2

7

That's probably caused by fs.readdir. It seems it is using an API that is not patched by Angulars zone. To work around you can use

export class FilesComponent {
   constructor(private cdRef:ChangeDetectorRef) {}

   loadFiles() {
     fs.readdir(this.cwd, (err, dir) => {
        for (let filePath of dir) {
            console.log(filePath);
            this.files.push(filePath);
        }
        this.cdRef.detectChanges();
     });
   }
}
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Can you explain this in more detail. I have never seen the `ChangeDetectorRef` before. A link would be helpful. However this has solved the issue. Thanks a lot! – Yahya Uddin Jun 30 '16 at 18:31
  • 2
    @YahyaUddin, https://angular.io/docs/ts/latest/api/core/index/ChangeDetectorRef-class.html#!#detectChanges-anchor. – Mark Rajcok Jun 30 '16 at 18:37
1

Here's another option for you as the accepted answer didn't work for my use case... basically fs runs outside of the Angular zone so you need to assign the variable inside of ngZone.

NOTE: You may not actually need to run change detection manually depending on your use case.

import {Component, NgZone, ChangeDetectorRef} from "@angular/core";
const fs = require('fs');

@Component(<any>{
    selector: 'files',
    template: `
<h2>Files</h2>

<ul *ngFor="let file of files">
    <li>{{ file }}</li>
</ul>

<button (click)="showFiles">Show Files</button>
`,
})
export class FilesComponent {
    files: any[];
    cwd: string;

    constructor(
      private cd: ChangeDetectorRef,
      private zone: NgZone
    ) {}

    ngOnInit() {
        this.cwd = __dirname;
        this.files = [];
        this.loadFiles();
    }

    loadFiles() {
        fs.readdir(this.cwd, (err, dir) => {
            var newFiles = [];
            for (let filePath of dir) {
                console.log(filePath);
                newfiles.push(filePath);
            }
            this.zone.run(() => {
              this.files = newFiles;
              // NOTE: you may be able to delete this next line depending on your use case
              this.cd.detectChanges();
            })
        });
    }

    showFiles() {
        // Empty method
        // Shows the files for some reason despite nothing happening
    }
}
Kyle Krzeski
  • 6,183
  • 6
  • 41
  • 52