Search code examples
typescriptionic4

How do call a delete method to run all instances of an index


I have a method that deletes files off of my ionic 4 App. I can run a deleteFile() method which deletes individual files. I Am trying to use a forEach Method to run the through the deleting all files. But it fails after the first deletion.

html

<ion-card *ngFor="let data of list let i = index">
  <ion-card-content>
    <div >
      <div *ngFor="let n of loop let n = index">
        <div class = "linkButtons">
          <ion-button fill="outline" color="blackgreen" (click)=" DeleteAllFiles( )">delete all Photos</ion-button>> 
        </div>
      </div>
    </div> 

    <div>
      <ion-list>
        <ion-item *ngFor="let doc of documents; index as pos" class="ion-text-wrap">
          <ion-icon name="document" slot="start"></ion-icon>
          <ion-textarea auto-grow="true" ><small>{{ doc.name }}</small></ion-textarea>
          <ion-button slot="end" fill="clear" (click)="deleteFile(doc, pos)">
            <ion-icon slot="icon-only" name="trash"></ion-icon>
          </ion-button>
        </ion-item>
      </ion-list>
    </div>
  </ion-card-content>
</ion-card>

The (click)="deleteFile(doc, pos) works fine.

The (click)=" DeleteAllFiles( ) fails after the first deletion.

ts

DeleteAllFiles( ): void {
  this.documents.forEach(
    (imgEntry, position ) => this.deleteFile(imgEntry, position ));
}

deleteFile(imgEntry, position) {
  this.documents.splice(position, 1);
  this.storage.get('app_DOCS').then(documents => {
    let arr = JSON.parse(documents);
    console.log("arr ", arr );

    let filtered = arr.filter(name => name != imgEntry.name);
    console.log("filtered", filtered);

    this.storage.set('app_DOCS', JSON.stringify(filtered));
    let correctPath = imgEntry.filePath.substr(0, imgEntry.filePath.lastIndexOf('/') + 1);
    console.log("correctPath", correctPath);

    this.file.removeFile(correctPath, imgEntry.name).then(res => {
      this.presentToast('File removed.'); // edit later for one toast pop up
      }); 
  }); 
} 

console.logs

list of docs

Documents [
 {
   "name":"UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_icon-144x144.png",
   "path":"ionic://localhost/_app_file_/var/mobile/Containers/Data/Application/F8772763-A219-407D-92CD-EF5178F0EA9C/Library/NoCloud/UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_icon-144x144.png",
   "filePath":"file:///var/mobile/Containers/Data/Application/F8772763-A219-407D-92CD-EF5178F0EA9C/Library/NoCloud/UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_icon-144x144.png"
 },
 {
   "name":"UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_44198511-How-to-Fix-FAL-Rifle-Brass-Strikes.pdf",
   "path":"ionic://localhost/_app_file_/var/mobile/Containers/Data/Application/F8772763-A219-407D-92CD-EF5178F0EA9C/Library/NoCloud/UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_44198511-How-to-Fix-FAL-Rifle-Brass-Strikes.pdf",
   "filePath":"file:///var/mobile/Containers/Data/Application/F8772763-A219-407D-92CD-EF5178F0EA9C/Library/NoCloud/UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_44198511-How-to-Fix-FAL-Rifle-Brass-Strikes.pdf"
 }
]


arr  
  UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_icon-144x144.png,
  UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_44198511-How-to-Fix-FAL-Rifle-Brass-Strikes.pdf

  filtered UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_44198511-How-to-Fix-FAL-Rifle-Brass-Strikes.pdf

  correctPath file:///var/mobile/Containers/Data/Application/F8772763-A219-407D-92CD-EF5178F0EA9C/Library/NoCloud/

arr  
 UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_icon-144x144.png,UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_44198511-How-to-Fix-FAL-Rifle-Brass-Strikes.pdf

 filtered UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_icon-144x144.png

 correctPath file:///var/mobile/Containers/Data/Application/F8772763-A219-407D-92CD-EF5178F0EA9C/Library/NoCloud/

 imgEntry.name UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_icon-144x144.png
 imgEntry.name UUID_5deb6fae-4d84-41ac-8157-01f9c68e73c5_answerID_155_originalName_44198511-How-to-Fix-FAL-Rifle-Brass-Strikes.pdf

Working Code

deleteFile(imgEntry, position): Promise<any> {
  console.log("deleteFile selected");
  let request = this.storage.get(this.STORAGE_KEY_DOC);
  console.log("position", position);
  request.then(documents => {
    let arr = JSON.parse(documents);

    this.documents.splice(position);
  // let filtered = arr.filter(name => name != imgEntry.name);

    let correctPath = imgEntry.filePath.substr(0, imgEntry.filePath.lastIndexOf('/') + 1);

    this.file.removeFile(correctPath,  imgEntry.name).then(res => {
      let resp = JSON.stringify(res);
      console.log("resp", resp);
    });

    let removeDB = this.storage.set(this.STORAGE_KEY_DOC, JSON.stringify(this.documents));
  }).then(( )=> {
  });
  return request;
} 

Solution

  • It is "failing" because you are altering the documents array whilst you are iterating it.

    This is essentially what you have:

    var arr = [1, 2, 3, 4];
    arr.forEach((v, i) => {
      arr.splice(i, 1);
    })
    console.log(arr);

    Which boils down to:

    var arr = [1, 2, 3, 4];
    arr.splice(0, 1);
    arr.splice(1, 1);
    arr.splice(2, 1);
    arr.splice(3, 1);
    console.log(arr);

    If greater than the length of the array, start will be set to the length of the array. In this case, no element will be deleted but the method will behave as an adding function, adding as many element as item[n*] provided.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice

    To conclude, there is no point to splicing the array as you go. Have your .then implementation keep track of the files successfully deleted and then splice those after the .forEach has completed. You may want to return the promise from your deleteFile method so that you can Promise.all() it at the end.


    The example below returns a promise from the deleteFile method. The deleteAll method now maps instead of forEach so that we can store all of the priomises, and then once they are done we will be able to just wipe the documents array. Please not that I updated the below without a compiler, so there may be a couple syntax errors.

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

    DeleteAllFiles( ): void {
      var deleteRequests = this.documents.map((imgEntry, position ) => this.deleteFile(imgEntry, position ));
    
      Promise.all(deleteRequests).then(() => {
          this.documents = new Arrray();
      });
    }
    
    deleteFile(imgEntry, position): Promise<any> {
      var request = this.storage.get('app_DOCS');
    
      request.then(documents => {
        let arr = JSON.parse(documents);
        console.log("arr ", arr );
    
        let filtered = arr.filter(name => name != imgEntry.name);
        console.log("filtered", filtered);
    
        this.storage.set('app_DOCS', JSON.stringify(filtered));
        let correctPath = imgEntry.filePath.substr(0, imgEntry.filePath.lastIndexOf('/') + 1);
        console.log("correctPath", correctPath);
    
        this.file.removeFile(correctPath, imgEntry.name).then(res => {
          this.presentToast('File removed.'); // edit later for one toast pop up
          }); 
      }); 
    
      return request;
    }