Tips for patiently awaiting data before constructing an object

Currently, I am uploading image files to a server and returning the download URL and upload percentage with my upload method.

Looking ahead, I plan to enhance this functionality to allow for the upload of multiple images using the same component. The goal is to display a table below the upload form with the names of successfully uploaded files and buttons to delete them.

I am seeking guidance on how to utilize a promise or async callback to append a new

{ downloadURL : Observable<string>, uploadPercentage : Observable<number> }
object to an array whenever a newly uploaded file becomes available.

The upload method is located inside upload-form component.ts


  upload(file) {
    // this.id = Math.random().toString(36).substring(2);
    
    const folder = this.mode == 'single' ? 'titleCardImages' : 'contentImages' 


    const filePath = `${folder}/${file.name}`;
    const fileRef = this.afStorage.ref(filePath);
    const task = this.afStorage.upload(filePath, file)
    // observe percentage changes
    this.uploadPercent = task.percentageChanges();
    // get notified when the download URL is available
    task.snapshotChanges().pipe(
        finalize(() => this.downloadURL = fileRef.getDownloadURL() )
    )
    .subscribe()
    }

editNewsComponent.html (excerpt) mat dialog for

<h2 mat-dialog-title>Create, Update or delete news</h2>

<mat-dialog-content [formGroup]="form">

  <mat-form-field>
      <input matInput
              placeholder="Title"
              formControlName="title">
  </mat-form-field> <br>


  <br><label for="titleCardImage">Title Card Image:</label> <br>
  <upload-image
    mode="single"
    label="Enter a title card image">
  </upload-image>


  <!-- <button (click)="fileInput.click()">
    <span>Select</span>
    <input #fileInput type="file" (change)="upload($event)" style="display:none;" />
    
  
  </button> <br> -->


  


  <mat-form-field>
    <textarea matInput
            placeholder="a short description"
            formControlName="description"></textarea>
  </mat-form-field> <br>

  <mat-form-field>
    <textarea matInput
            placeholder="Body/Content of the news piece"
            formControlName="body"></textarea>
  </mat-form-field> <br>


  <label id="example-radio-group-label">Tags: <br></label>
  <mat-chip-list aria-label="tag selection" >
    <mat-chip>One fish</mat-chip>
    <mat-chip>Two fish</mat-chip>
    <mat-chip color="primary" selected>Primary fish</mat-chip>
    <mat-chip color="accent" selected>Accent fish</mat-chip>
  </mat-chip-list>
  
</mat-dialog-content>

<mat-dialog-actions>
    
  <button
    mat-stroked-button
    (click)="reset()"
    >
    Clear
  </button>
  <button
    mat-dialog-close
    mat-stroked-button>
    Cancel
  </button>
  <button
    (click)="save()"
    mat-raised-button>
    Save
  </button>

  <p>Form data: {{ form.value | json }}</p>

</mat-dialog-actions>

Any help or guidance would be greatly appreciated.

Answer №1

If you're looking to adapt to the design of your project, it's definitely possible.

For a fantastic solution using Angular/Fire, check out Fireship.io: Source:

upload-task.component.ts

import { Component, OnInit, Input, ChangeDetectorRef } from '@angular/core';
import { AngularFireStorage, AngularFireUploadTask } from '@angular/fire/storage';
import { AngularFirestore } from '@angular/fire/firestore';
import { Observable } from 'rxjs';
import { finalize, tap } from 'rxjs/operators';

@Component({
  selector: 'upload-task',
  templateUrl: './upload-task.component.html',
  styleUrls: ['./upload-task.component.scss']
})
export class UploadTaskComponent implements OnInit {

  @Input() file: File;

  task: AngularFireUploadTask;

  percentage: Observable<number>;
  snapshot: Observable<any>;
  downloadURL: string;

  constructor(private storage: AngularFireStorage, private db: AngularFirestore) { }

  ngOnInit() {
    this.startUpload();
  }

  startUpload() {

    // The storage path
    const path = `test/${Date.now()}_${this.file.name}`;

    // Reference to storage bucket
    const ref = this.storage.ref(path);

    // The main task
    this.task = this.storage.upload(path, this.file);

    // Progress monitoring
    this.percentage = this.task.percentageChanges();

    this.snapshot   = this.task.snapshotChanges().pipe(
      tap(console.log),
      // The file's download URL
      finalize( async() => {
        this.downloadURL = await ref.getDownloadURL().toPromise();

        this.db.collection('files').add( { downloadURL: this.downloadURL, path });
      }),
    );
  }

  isActive(snapshot) {
    return snapshot.state === 'running' && snapshot.bytesTransferred < snapshot.totalBytes;
  }

}

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

Restricting types does not appear to be effective when it comes to properties that are related

I am working with a specific type that looks like this: type Props = { type: 'foo'; value: string; } | { type: 'baz'; value: number; }; However, when using a switch statement with the type property in TypeScript, the program in ...

Using Angular 2/4/5 to Bind UTC-formatted Date to Datepicker

As someone who is just starting out with Angular and Angular Material, I have encountered an issue regarding zonedDate format for dates in my backend. The backend requires dates to be in zonedDate Format like this: 2018-04-11T02:12:04.455Z[UTC]. However, ...

What sets apart TypeScript's indexed types from function signatures?

I am curious about the distinction between indexed type and function signatures in TypeScript. type A = { _(num: number): void; }['_']; type B = { _(ten: 10): void; }['_']; let a: A = (num) => { console.log(num); }; let b: B ...

Can you explain the variance between Next.js and Create React App?

I've been curious about understanding the distinctions between Next.js and Create React App (CRA). Both aim to simplify our lives when developing front-end applications with React. While researching online, I came across a key difference: Next.js o ...

Using TypeScript path aliases to resolve import errors

After creating a Vue.js project using Vue CLI 3 and setting up the import statement with the @ alias, I encountered an error when running npm run build. Can you explain why this happened? Error Message $ npm run build > [email protected] build / ...

The error message indicates that the property `v.context.$implicit` is not callable

I am a beginner with Typescript and it has only been 3 days. I am trying to access data from Firebase and display it in a list. However, I keep encountering an error when trying to navigate to another page using (Click) ="item ()". Can someone point out wh ...

Jest is simulating a third-party library, yet it is persistently attempting to reach

Here's a function I have: export type SendMessageParameters = { chatInstance?: ChatSession, // ... other parameters ... }; const sendMessageFunction = async ({ chatInstance, // ... other parameters ... }: SendMessageParameters): Promise<vo ...

Is there a method to implement retries for inconsistent tests with jest-puppeteer?

Currently, I am struggling with an issue where there is no built-in method to retry flaky tests in Jest. My tech stack includes Jest + TypeScript + Puppeteer. Has anyone else encountered this problem or have any suggestions for possible solutions? I attem ...

Tips for dynamically modifying style mode in Vue.js

I am looking to implement a feature where the style can be changed upon clicking a button. In my scenario, I am using Sass/SCSS for styling. For example, I have three different styles: default.scss, dark.scss, and system.scss. The code for dark.scss look ...

Mastering GraphQL querying in React using TypeScript

After successfully setting up a graphql and being able to use it in Postmen, here is how it looks: query listByName($name: String!) { listByName(name: $name) { id name sortOrder } } My variable is defined as {"name&quo ...

How to enable Autocomplete popper to expand beyond the menu boundaries in Material UI

Within my Menu component, I have an Autocomplete element. When the Autocomplete is clicked, the dropdown list/Popper appears but it's confined within the Menu parent. How can I make it so that the Autocomplete's dropdown list/Popper isn't re ...

I must remove the thumb from the input range control

I am looking for a way to remove the thumb from the progress bar in my music player. I have tried various methods without success, and I simply want the progress bar to move forward with color change as it advances based on the Progress variable. For refe ...

Obtain the data from a service that utilizes observables in conjunction with the Angular Google Maps API

In my Angular project, I needed to include a map component. I integrated the Google Maps API service in a file called map.service.ts. My goal was to draw circles (polygons) on the map and send values to the backend. To achieve this, I added event listeners ...

Utilizing Angular for Webcam Integration

After trying out this code snippet: <video autoplay playsinline style="width: 100vw; height: 100vh;"></video> <script> navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' } }) .then(stream =&g ...

Error: The default export is not a component compatible with React in the specified page: "/"

I'm facing an issue while building my next app. Despite using export default, I keep encountering an error that others have mentioned as well. My aim is to create a wrapper for my pages in order to incorporate elements like navigation and footer. vi ...

Anticipating the outcome of various observables within a loop

I'm facing a problem that I can't seem to solve because my knowledge of RxJs is limited. I've set up a file input for users to select an XLSX file (a spreadsheet) in order to import data into the database. Once the user confirms the file, v ...

The .slice() function in TypeScript has the ability to alter the initial array

I am diving into TypeScript and just tackled my first slice() method. My understanding is that the slice() method is supposed to create a copy of an array. Here's a snippet of the code: class ChapterOne { // Gauss Jordan Elimination // No ...

Turn off the touch events system for Ionic 2 on the leaflet map's draw controller

Seeking guidance on how to disable data-tap functionality in Ionic 2 within a leaflet map div. Anyone familiar with this? In Ionic-v1, the solution involved adding data-tap-disabled="true" to the map container (ion-content). I recently integrated the lea ...

Comparing the cost of memory and performance between using classes and interfaces

As I delve into writing TypeScript for my Angular project, one burning question arises — should I use an Interface or a Class to create my domain objects? My quest is to uncover solid data regarding the actual implications of opting for the Class route. ...

Nested forwardRef in React is a powerful feature that allows

Within my React application, specifically utilizing typescript, I have implemented a form using react-hook-form to handle all the necessary logic. Afterwards, I proceeded to customize the select element with various CSS and additional features. To simplif ...