0

My view has some simple code:

  HERE{{ files.length }}THERE
  <div class="uploaded-files" *ngIf="files.length > 0">
    <div class="uploaded-files-title">
      <h4>Selected Files</h4><h5>&nbsp;(X)</h5>
    </div>
    <table class="table">
      <thead class="thead-dark">
      <tr>
        <th scope="col">File Name</th>
        <th scope="col">File Size</th>
        <th scope="col" class="text-center">Upload Status</th>
        <th scope="col" class="text-center"><a href="" (click)="removeAll($event)">Remove All</a></th>
      </tr>
      </thead>
      <tbody>
        <tr *ngFor="let file of files">
          <td>{{ file.relativePath }}</td>
          <td>{{file.size}}</td>
          <td class="text-center"><i class="mdi mdi-check-circle mdi-24px approved"></i></td>
          <td class="text-center"><i class="mdi mdi-delete mdi-24px actions"></i></td>
        </tr>

My component is:

import {
  Component,
  OnInit
} from '@angular/core';
import { UploadEvent, UploadFile, FileSystemFileEntry  } from 'ngx-file-drop';


@Component({
  selector: 'upload-modal',  // <upload-modal></upload-modal>
  providers: [
  ],
  styleUrls: [ './upload.component.scss' ],
  templateUrl: './upload.component.html'
})
export class UploadComponent implements OnInit {


  constructor() {
  }

  public files: UploadFile[] = [];

  public ngOnInit() {
  }

  dropFile(event) {
    let droppedFiles: UploadFile[] = []
    if(this.files.length === 1) {
      return
    }

    const fileEntry = event.files[0].fileEntry as FileSystemFileEntry;
    fileEntry.file(fileData => {
      console.log('before', this.files)
      this.files.push({
        name: fileEntry.name,
        size: fileData.size
      })
      console.log('after', this.files)
    })
  }

  handleFileInput() {
    alert('files')
  }

  removeAll(event) {
    event.stopPropagation()
    this.files: UploadFile[] = []
  }

}

When my component's dropFile function does what it does, the console prints out correctly, but the view doesn't have any updated files.

I'm using angular 5.2.0. What am I doing wrong?

8
  • so you're *ngIf never changes to true to display the content? but you're console.log('after', this.files) shows that it has length greater than 0 is that correct? Commented Mar 19, 2018 at 20:37
  • @Woot that is correct. Commented Mar 19, 2018 at 20:38
  • am I correct in assuming the that Here {{ files.length }} There section of your code does display the correct length? Commented Mar 19, 2018 at 20:40
  • 1
    what triggers the dropFile function? I don't see that in your component. If that function isn't triggered by your component then I would guess that angular doesn't fire the change detection process when the function is called. Also I noticed that you component implements OnChanges but you don't import it and you don't implement it. That could cause issues. Commented Mar 19, 2018 at 20:45
  • 1
    Are you sure you don’t have console errors? You’re defining your class to implement OnChanges but you are not implementing the method. Not sure how is this even compiling. Commented Mar 19, 2018 at 22:34

4 Answers 4

2

I think Angular's not aware of you having changed the model behind the scenes.

By default, Angular uses zones to trigger it's change detection mechanism after async events—although this works fine for most async events you'll encounter, the zone implementation used by Angular, Zone.js, only supports a handful of non-standard APIs and FileSystemFileEntry isn't one of them. In cases like this, your best bet is to manually trigger the change detection as explained here.

Sign up to request clarification or add additional context in comments.

3 Comments

I'm using npmjs.com/package/ngx-file-drop to get the FileSystemFileEntry. I tried with zones - no dice.
It's strange that manually triggering the change detection's not working—in the official demo of the library they do exactly and it looks like it's working fine.
If you to it like in the official demo i.e. inject a ChangeDetectorRef into the constructor and call it's detectChanges() method in the last line in the file callback (not the last line of your dropFile method) I think it should work fine. Doesn't it?
1

You can manually detect changes using the change detection. I don't recommend using this method in all situations but in this one it's probably the best way.

Add ChangeDectorRef to the import statement

import {
  Component,
  OnInit,
  ChangeDetectorRef 
} from '@angular/core';

then add it to your constructor

constructor(private cd: ChangeDetectorRef) {}

Then after you drop the files you can use the change detector to trigger change detection.

dropFile(event) {

  // add this to the end of the dropFile method
  this.cd.detectChanges();
}

Comments

0

The change detector needs to be the last line of the callback as user4091335 pointed out and not the last line of the droppped file method.

dropped(event: any) {
    for (const droppedFile of event.files) {
      const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
      fileEntry.file((file: File) => {
        this.files.push(file);
        **this.ref.detectChanges();**
      })
    }
  }

Comments

0

I had the same issue trying to update the view, and I solved using promises instead of detecting the changes on the view using ChangeDetectorRef. Worked like a charm. Here's my solution

    triggerUpload(evt:UploadEvent){    


    let files = evt.files;
    let self = this;
    this.fillupFiles(files).then(function(response){
       self.uploadAndRender();          
    });

  }

fillupFiles(files):Promise<Object>{
    let self = this;
    let promise = new Promise(function(resolve, reject){
      var count = files.length;
      var index = 0;
      for(let droppedFile of files){
        if(droppedFile.fileEntry.isFile){
          let fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
          fileEntry.file((file:File)=>{
            index++;
            self.files.push(file);
            if(index==count){
              resolve(true);
            }
          });
        }
      }
    });

    return promise;
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.