Guide on Retrieving an Array from an Observable

Hey there! I've come across a function that is supposed to return an Array. In the function below,

this.cordovaFile.readAsArrayBuffer(this.cordovaFile.dataDirectory, storageId)
actually returns a Promise Array. I'm converting it into an Observable and storing it in the timesheetArray variable. Now, timesheetArray will return as an Observable array, but I simply want it to return as just an Array. Here's the code:

I could really use some assistance here. If it could return just an array, then I wouldn't have to make any changes anywhere else because this function is used throughout the application.

public getAllTimesheets(): TimesheetModel[] {
  const storageId = TIMESHEET_KEYS.ALL_TIMESHEET;

  const timesheetArray = from(
    this.cordovaFile
    .readAsArrayBuffer(this.cordovaFile.dataDirectory, storageId)
    .then((compressedTimesheet) => {
      const start = moment();
      const uint8array = new Uint8Array(compressedTimesheet);
      const jsonTimeSheet = this.LZString.decompressFromUint8Array(uint8array);
      this.log.debug(`LocalStorageMaterialService: getMaterials() from files: Decompression took ${moment().subtract(start.valueOf()).valueOf()} ms`);
      return <TimesheetModel[]> JSON.parse(jsonTimeSheet) || [];
    })
    .catch((error) => {
      this.log.debug('LocalStorageMaterialService: Retrieving materials from file storage was not possible: ', JSON.stringify(error));
      return [];
    })
  )
  timesheetArray.subscribe((timesheet) => {
    // How can I return an Array here?
  });
}

Here's just one example of why I want to return an array instead of observable:

let matchedTimesheet = _.find<TimesheetModel>(this.getAllTimesheets(),
(timesheet) => travelToDate 
&& timesheet.startOfWork.isSame(travelToDate.startDate.value, 'days')
            ); 
In the above code snippet, it expects an array rather than an Observable. I could achieve this by subscribing here as well, but if the function were to return an array instead of an observable, then I would need to make changes everywhere.

Answer №1

It seems like your approach may need a shift in perspective. The readAsArrayBuffer function operates asynchronously, returning a promise.

In your getAllTimesheets() method, you should not simply return an array of TimesheetModel. Instead, consider returning an

Observable<TimesheetModel[]>
. This change will require adjustments wherever the method is called.

With an

Observable<TimesheetModel[]>
return type, you must either subscribe to each call of getAllTimesheets() or utilize the async pipe in your template to read the Observable data.

The latter option is recommended for simplicity.

To implement this modification in your getAllTimesheets() method, make the following alterations:

public getAllTimesheets(): Observable<TimesheetModel[]> {
  const storageId = TIMESHEET_KEYS.ALL_TIMESHEET;
  return from(
    this.cordovaFile
    .readAsArrayBuffer(this.cordovaFile.dataDirectory, storageId)
    .then((compressedTimesheet) => {
      const start = moment();
      const uint8array = new Uint8Array(compressedTimesheet);
      const jsonTimeSheet = this.LZString.decompressFromUint8Array(uint8array);
      this.log.debug(`LocalStorageMaterialService: getMaterials() from files: Decompression took ${moment().subtract(start.valueOf()).valueOf()} ms`);
      return <TimesheetModel[] > JSON.parse(jsonTimeSheet) || [];
    })
    .catch((error) => {
      this.log.debug('LocalStorageMaterialService: Retrieving materials from file storage was not possible: ', JSON.stringify(error));
      return [];
    })
  );
}

When using this method and subscribing to it, remember to unsubscribe the subscription in the component's ngOnDestroy method:

// unsubscribe this subscription on ngOnDestroy()
const subscription = getAllTimesheets()
  .subscribe(timesheetData => {
    // Handle the data here.
  })

Answer №2

To avoid registering your entire promise as an observable, it's recommended to set up a BehaviorSubject and update the subject with the promise result using .next(). Subscribing to this updated result is then possible.

If you're working within a service, you can establish this setup by following these steps:

@Injectable({ providedIn: 'root' })
export class TimesheetsService {
  /**
   * Initialize a new behavior subject with an empty array which will be
   * accessed from outside of the service through subscription.
   */
  public timesheets$: BehaviorSubject<TimesheetModel[]> = new BehaviorSubject([]);

  constructor() {
    // Invoke your service method to retrieve data via GET request.
    this.getAllTimesheets();
  }

  /**
   * This method updates the behavior subject and can be triggered externally
   * each time there is a need to refresh the result.
   */
  public getAllTimesheets() {
    return this.cordovaFile
      .readAsArrayBuffer(this.cordovaFile.dataDirectory, storageId)
      .then((compressedTimesheet) => {
        const uint8array = new Uint8Array(compressedTimesheet);
        const jsonTimeSheet = this.LZString.decompressFromUint8Array(uint8array);
        // Upon success, update the behavior subject with the fetched result.
        return timesheets$.next(JSON.parse(jsonTimeSheet));
      })
      // In case of error, update the behavior subject accordingly.
      .catch(() => timesheets$.next([]));
  }
}

In your component, subscribe to your result$ observable like so:

export class YourComponent implements OnInit {
  timesheets: TimesheetModel[];

  constructor(private timesheetsService: TimesheetsService) {
    // Optionally trigger your service method again for fresh data retrieval.
    this.timesheetsService.getAllTimesheets();
  }

  ngOnInit() {
    /**
     * Subscribe to the outcome of your service call and assign the result to a
     * variable to be utilized within your component.
     */
    this.timesheetsService.timesheets$
      .subscribe((timesheets) => this.timesheets = timesheets);
  }
}

Answer №3

If you want to convert an array into a specific type, you can use the as [] syntax in TypeScript.

timesheetArray.subscribe((timesheet) => {
    const time_sheet = timesheet as [];
});

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

Is there a way to incorporate several select choices using specific HTML?

I am currently trying to dynamically populate a select tag with multiple option tags based on custom HTML content. While I understand how to insert dynamic content with ng-content, my challenge lies in separating the dynamic content and wrapping it in mat ...

Revamp the Bootstrap Form Upload feature

I am looking to redesign the Bootstrap Form Upload field. The main reason behind this is that in Bootstrap, the file upload field is treated as one single component, making it impossible to separate the file upload button from the file upload text. The fi ...

Is there a way in TypeScript to automatically assign object properties to a class in an efficient manner?

Imagine a scenario where I have a unique class structured like this: class Organization { title: string; website: string; location: string constructor() { } } Now, picture retrieving an object from a database that is known to conta ...

Is it possible for us to integrate the Neo4j application's output into our Angular and NodeJS front-end?

Is there a way to integrate the Neo4j application output into my Angular Front-end application? I am open to using Nodejs for backend if necessary. Could you kindly provide guidance on how to specifically include just the middle section of a graph diagram ...

Using a form template to bind radio buttons and automatically populate fields based on the selected radio button

My form has three radio buttons. The first one is selected by default. The second one should display an input field conditionally upon clicking it, and when the third option is selected, it should populate that input field with a certain value. div> ...

Pattern Matching for Excluding Multiple Specific Phrases

I need to restrict what a user can enter into a field based on previous entries that are already in the system. For instance, the user has already entered these values into the database: ["typescript", "C#", "python"] If they type one of these existing ...

Angular: Navigating through two levels of fetched data from Firebase

I'm currently working on parsing retrieved data from Firebase within an Angular (Typescript) project. The structure of my JSON data in Firebase resembles the following: "customer" : { "customerId1" : { "documents" : { "documentId1" : { ...

The PrimeNg p-calendar is showing an incorrect month for the selected date

Utilizing the p-calendar component from primeNg has resulted in a discrepancy when comparing or checking dates, specifically returning an incorrect month. <p-calendar [locale]="nl" [inline]="true" [showOtherMonths]=" ...

Declaring and accessing class variables in Angular 4

I am facing an issue with the following TypeScript model: export class User { email: string; token: string; username: string; bio: string; image: string; constructor() {} } When I attempt to instantiate this model in another TypeScript file, ...

Can you guide me on implementing AWS SDK interfaces in TypeScript?

Attempting to create an SES TypeScript client using AWS definitions file downloaded from this link My approach so far: /// <reference path="../typings/aws-sdk.d.ts" /> var AWS = require('aws-sdk'); var ses:SES = new AWS.SES(); The error ...

Display JSON data in a table format using Angular

I have received a JSON result that looks like this: { "discipline":"ACLS", "course": [ { "value":"ACLS Initial", "classes":"0", "students":"0" }, { "BLS":"CPR Inst ...

Importing in ES6/Typescript: combine imports with names onto a single line

Is it possible to condense this code into a single line? import * as Express from 'express'; import { Application, NextFunction, Request, Response } from 'express'; Signed, Dan the Dev ...

In TypeScript, this regular expression dialect does not permit the use of category shorthand

Recently, I attempted to implement a regular expression in TypeScript: I ran the following code: const pass = /^[\pL\pM\pN_-]+$/u.test(control.value) || !control.value; To my surprise, an error occurred: "Category shorthand not allowed in ...

Transforming a Bootstrap 4 HTML project into Angular 9

After stumbling upon a beautiful HTML template that caught my eye, I realized it was built using Bootstrap. Luckily, I came across tutorials on how to convert such templates into Angular 6 projects, making the process seem quite straightforward (like this ...

Ways to mock a static method within an abstract class in TypeScript

Having a difficult time testing the function Client.read.pk(string).sk(string). This class was created to simplify working with dynamoDB's sdk, but I'm struggling to properly unit test this method. Any help or guidance would be greatly appreciate ...

Inconsistency in product returns following promise mapping - Utilizing Ionic, Angular, and Typescript

When I retrieve items from a database for a feed, it is crucial that they remain in the same order as retrieved. However, despite mapping the array from the query, the displayed feed objects end up out of sequence. Here's the snippet of code: let ...

Sending the id from a component to a service in Angular

In my current project, I am working on a chat application using Angular with WebSocket integration. Let me provide an overview of the architecture I have designed for this project. I created a module called 'chatting' which contains a list of use ...

Generate an array of identifiers from a pre-existing array

I am facing a challenge where I need to create an array of IDs for each element in an existing array whose size is unknown. The twist here is that every set of four elements should have the same ID. As an illustration, if the original array (let's ca ...

Angular 14 encountered an unexpected issue: There was an unhandled exception with the script file node_modules/tinymce/themes/modern/theme.min.js missing

After attempting to run ng serve, an error message appears: ⠋ Generating browser application bundles (phase: setup) ...An unhandled exception occurred: Script file node_modules/tinymce/themes/modern/theme.min.js does not exist. ⠋ Generating browser ap ...

Stop the transmission of ts files

Recently, I noticed that when using node JS with Angular-CLI, my .ts files are being transmitted to the client through HTTP. For example, accessing http://localhost/main.ts would deliver my main.ts file to the user. My understanding is that ts files are s ...