What is the proper procedure for invoking a delete method to remove all occurrences of a specific index?

I have implemented a file deletion method in my Ionic 4 App. Using the deleteFile() method, I can successfully delete individual files. However, when attempting to delete multiple files using a forEach loop, the process 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 usage of (click)="deleteFile(doc, pos) works correctly.

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

TypeScript

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);

    let filtered = arr.filter(name => name != imgEntry.name);

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

    this.file.removeFile(correctPath, imgEntry.name).then(res => {
      this.presentToast('File removed.');
    }); 
  }); 
} 

Console Logs

List of Documents

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-Stri...
]

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 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;
} 

Answer №1

Your code is "failing" because you are modifying the documents array while iterating through it.

Here's what your code essentially does:

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

Which can be simplified 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 the start index provided in splice is greater than the length of the array, no elements will be deleted but instead added as many times as item[n] was provided.

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

In conclusion, splicing the array during iteration is unnecessary. Instead, track successfully deleted files within a .then implementation and splice them after the .forEach loop completes. Return the promise from the deleteFile method for optimal use with Promise.all().


The revised example below demonstrates returning promises from the deleteFile method. The deleteAll method now uses map instead of forEach to collect all promises before wiping the documents array once they complete. Note: Below code is updated without compilation so syntax errors may exist.

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 Array();
  });
}

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;
} 

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unique validation for mandatory selection on changeSelected

In my Angular HTML code, I have set up a dropdown and two text-boxes. My goal is to create a feature where, if the user selects an option from the 'payable_frequency' dropdown, then either one of the 'payable_commission' fields (min or ...

Neither Output nor EventEmitter are transmitting data

I am struggling to pass data from my child component to my parent component using the Output() and EventEmitter methods. Despite the fact that the emitter function in the child component is being called, it seems like no data is actually being sent through ...

Is it possible to modify the number format of an input field while in Antd's table-row-edit mode?

I am currently utilizing the Table Component of Ant Design v2.x and unfortunately, I cannot conduct an upgrade. My concern lies with the inconsistent formatting of numbers in row-edit mode. In Display mode, I have German formatting (which is desired), but ...

I must connect a TypeScript class to an Angular component

Here is an example of element class export class Element { fields = [] } And here are two field classes export class SmallText { value constructor(value) { this.value = value } } export class LargeText { value construct ...

Testing a NestJS service with multiple constructor parameters can be done by utilizing various techniques such as dependency

Content When testing a service that needs one parameter in the constructor, it's essential to initialize the service as a provider using an object instead of directly passing the service through: auth.service.ts (example) @Injectable() export class ...

Unexpected behavior in resolving modules with Babel (using node and typescript)

Within my node project setup, I utilize babel-plugin-module-resolver for managing relative paths efficiently. tsconfig.json { "compilerOptions": { "outDir": "build", "target": "es5", ...

Issue with calling a function to change the CSS color class of a button in Angular

Within my Angular code, I am attempting to set a CSS color for my button by calling a TypeScript function that will return the appropriate CSS class name. This is the code I have tried: <button style="height: 10%" class="getColor(days.date)">{{days ...

Managing the re-rendering in React

I am encountering a situation similar to the one found in the sandbox example. https://codesandbox.io/s/react-typescript-fs0em My goal is to have Table.tsx act as the base component, with the App component serving as a wrapper. The JSX is being returned ...

Retrieving a variable value set within a jQuery function from within an Angular 2 component

In the current project, I am facing a situation where I need to work around and initialize jQuery datetimepicker inside an Angular 2 application (with plans to refactor it later). However, when I assign a datetime value to a variable, I encounter a proble ...

Out-of-order calling of React hooks detected

It's puzzling that the useMemo hook appears to be running before the useEffect hook directly above it. The reason for my suspicion is the error message I'm encountering: TypeError: Cannot read property 'timestamp' of undefined This ...

Alternative for using useRouteMatch to retrieve parameters

I'm currently refactoring this code and struggling to find a suitable replacement for this section. This is due to the react-router-dom v6 no longer having the useRouteMatch feature, which I relied on to extract parameters from the URL: import React, ...

Categorizing the types of dynamically retrieved object values

I've developed a unique class with two properties that store arrays of tuples containing numbers. Additionally, I've implemented a method called "getIndex" which dynamically accesses these properties and checks for duplicate number values within ...

Issue with typing in subscription encountered in Typescript RXjs version 6.6.0

Have you attempted using the code snippet below? const data = new Data() data.subscribe({ next: (value) => {}, error: (value) => {}, complete: (value) => {}, }); Encountering this error: Compilation failed. sr ...

Using Generic Types in TypeScript for Conditional Logic

To better illustrate my goal, I will use code: Let's start with two classes: Shoe and Dress class Shoe { constructor(public size: number){} } class Dress { constructor(public style: string){} } I need a generic box that can hold either a ...

Utilizing TypeScript to mandate properties in a React component

When trying to apply TypeScript type enforcement on React components, I encountered some unexpected behavior. Here are simplified examples: function FunctionalComponent(props: { color: string }) { return <></>; } type ComponentWithName<I ...

Incorporating responsive design with React and Typescript

Trying to utilize React with TypeScript, I aim to dynamically generate components based on a field name // Storing all available components const components = { ComponentA, ComponentB, }; // Dynamically render the component based on fieldName const di ...

Adding a cell break line within AG-GRID in an Angular application

I'm trying to display two sets of data in a single cell with ag-grid, but I want the data to be on separate lines like this instead: Data with break line I attempted to use "\n", "\r", and "\br" but it didn't work. Here is my code ...

Switching to Next.js

In my Next JS application, I have a div that dynamically displays the currency and price of a product when a user visits a product page. <div className="flex"> <Image src={EuroCurrency} alt="Euro Sign} /> <h1 className=" ...

Employing Class Categories in Static Procedures

I am currently working on developing a foundational Model that will serve as the base for a specific model class, which will have an interface detailing its attributes. Within the base Model class, I am aiming to incorporate a static factory() function th ...

Tips for incorporating the closeAutocomplete function into ng4-geoautocomplete

Hey there! I've incorporated the ng4-autocomplete component into my custom component and now I'm trying to figure out how to detect when the autocomplete dropdown closes. Can you help me out with implementing the "closeAutocomplete" method? Let& ...