Subscribing to an observable and storing it as a string in a collection is

Being relatively new to TypeScript and JavaScript, I am struggling with understanding how collections and file input/output operations function. Specifically, my challenge lies in retrieving data from a JSON file and storing it in a collection.

Below is the code snippet:

In my service class:

  private configuration = "../../assets/Configurations/testConfiguration.json";

  constructor(private http: HttpClient) {}

  getBlogs(blog: string): Observable<IBlogPost[]> {
    return this.http.get<IBlogPost[]>(blog);
  }

  getConfigurations() {
    var configurationsData = [];
    this.http.get(this.configuration).subscribe(data => {
      configurationsData.push(data);
      console.log(data);
      // This successfully prints all the paths
    });

    //Although, this loop does not work as expected and does not print the data as if the collection is empty
    configurationsData.forEach(x => console.log(x));

    return configurationsData;
  }

Where I inject my service class:

blogs: IBlogPost[] = [];

  private blogsPaths: string[] = [];

  errorMessage = "";

  constructor(private appUtilityService: AppUtilityServices) {}

  ngOnInit() {
    //This does not populate 'blogsPaths' properly
    this.blogsPaths = this.appUtilityService.getConfigurations();



    this.blogsPaths.forEach(blogPath =>
      this.appUtilityService.getBlogs(blogPath).subscribe(
        b =>
          b.forEach(blog => {
            this.blogs.push(blog);
          }),
        error => (this.errorMessage = <any>error)
      )
    );
  }

testConfiguration.json:

[
  "../../assets/BlogPosts/testBlog1.json",
  "../../assets/BlogPosts/testBlog2.json"
]

If you have any recommendations for a helpful tutorial on handling collections in JavaScript and how to efficiently return them, that would be greatly appreciated!

Answer №1

It turns out that this is the solution...

Here's a refined version:

During OnInit:

this.appUtilityService.getConfigurations().subscribe(
  data =>
    data.forEach(item => {
      this.appUtilityService.getBlogs(item).subscribe(
        blogs =>
          blogs.forEach(blog => {
            this.blogs.push(blog);
          }),
        error => (this.errorMessage = <any>error)
      );
    }),
  error => (this.errorMessage = <any>error)
);

In AppUtilityService:

  getBlogs(blog: string): Observable<IBlogPost[]> {
    return this.http.get<IBlogPost[]>(blog);
  }

  getConfigurations(): Observable<string[]> {
    return this.http.get<string[]>(this.configuration);
  }

A more elegant version:

  ngOnInit() {
    this.initializeBlog();
  }

  private initializeBlog(): void {
    this.appUtilityService.getConfigurations().subscribe(
      data =>
        data.forEach(item => {
          this.appUtilityService.getBlogs(item).subscribe(
            blogs =>
              blogs.forEach(blog => {
                this.blogs.push(blog);
              }),
            error => (this.errorMessage = <any>error)
          );
        }),
      error => (this.errorMessage = <any>error)
    );
  }

Answer №2

When you enter the getConfigurations function, you trigger an asynchronous http request. As a result, it's expected that the variable configurationsData will be empty outside of the subscribe method.

In this scenario, I'm harnessing the capabilities of RxJS to streamline the code and eliminate the need for Array.push and nested subscribe().

1) Streamlining async service methods to fetch data

The getConfigurations function should now return an Observable:

getConfigurations(): Observable<string[]> {
  return this.http.get<string[]>(this.configuration);
}

Similarly, the getBlogs function should also return an Observable:

getBlogs(blogsPath: string): Observable<BlogPost[]> {
  return this.http.get<BlogPost[]>(blogsPath);
}

To adhere to Angular best practices, I have omitted the I prefix from each interface, such as BlogPost.

2) Unleashing the power of RxJS

Next, you'll create another method within your service to retrieve an Observable containing blog posts, structured like so:

getBlogPosts(): Observable<BlogPost[]> {
  return this.getConfigurations().pipe(
    mergeAll(),
    concatMap(blogsPath => this.getBlogs(blogsPath)),
    concatAll(),
    toArray()
  )
}

This sequence involves fetching all collections of blog paths, acquiring blog posts for each path in every array of paths, and eventually consolidating them into a single array of posts.

Finally, within your component, you can invoke getBlogPosts using a subscription:

ngOnInit() {
  this.service.getBlogPosts().subscribe(blogs => {
    ...perform actions with your blogs array.
  });
}

You may even enhance this further by utilizing the async pipe directly in your template :

.ts file:

blogs$: Observable<BlogPost[]>;
...
this.blogs$ = this.appService.getBlogPosts();

.html file:

<ul *ngIf="blogs$ | async as blogs">
    <li *ngFor="let blog of blogs">
        #{{ blog.id }} - {{ blog.title }}
    </li>
</ul>

Feel free to explore my comprehensive demonstration of this RxJS approach on Stackblitz.

For more insights, check out an article by Tomas Trajan on Medium

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

Enhance the speed of AJAX search within a UL LI list containing over 5000 objects

Dear Stack community, I've been working on a simple Ajax js search using .addClass and .removeClass as I discovered it's faster than using .show() and .hide(). However, the speed is still not up to par and I'm feeling quite lost. You can ...

What is the process for ensuring that an object's property values must include specific substrings?

Exploring possibilities for an API configuration system and seeking guidance on typing a config object. Clarification: This pertains to a resources/endpoints configuration for the API, where each resource/endpoint has a defined path containing specific pa ...

JavaScript: incorporating PHP into JavaScript

What is the proper way to incorporate PHP inside Javascript? var timeDiff = 60-20; if ( timeDiff <= 60 ) { var stamp = timeDiff <?php echo $this->__('second ago') . '.'; ?>; } A console error has been detected: Unca ...

Changing md-sidenav mode in Angular Material 2

Looking to modify the behavior of the md-sidenav in Angular Material 2, switching from side on desktops to over on mobile devices. Is there a method to achieve this programmatically? Appreciate any guidance! ...

Distinguishing between a regular JavaScript variable and one annotated with a dollar sign

Many responses have addressed the question of using a dollar sign in JavaScript variables. In essence, the dollar sign functions as an identifier in JavaScript variables. However, I am curious if there are other distinctions between regular variables and ...

Tips for eliminating repetitive code in JavaScript

I have a jQuery function that deals with elements containing a prefix 'receive' in their class or ID names. Now, I need to duplicate this function for elements with the prefix 'send'. Currently, it looks like this: function onSelectAddr ...

Is there a way to incorporate icons into the rows of a react ag-grid?

I'm currently using the react ag-grid library and I've attempted to use the cellRenderer function, but unfortunately, it's not working as expected. columnDefinationWaterUsage: [ { headerName: "", cellRenderer: count ...

Incorporate a script into your Angular-CLI project

Can you guide me on inserting a script file into the index file of an angular2 project that was generated using angular-cli? : <!doctype html> <html> <head> <meta charset="utf-8"> <title>MyAngularApp</title> ...

`Obtaining an array of selected values from a deeply nested JSON object`

Looking to extract specific key values from a nested JSON object based on the boolean value of another key. In this case, I need the "name" key values for items where the "checked" key is true, and return them in a result array. Here is an example of the ...

Ways to generate a customized template using the directive attribute parameter

I have developed a new directive and I am looking to incorporate a dynamic template using the attribute wm.data.typeName. wm.data.typeName = "<span>html code</span>" <fill-choose model-input="wm.data.modelInput" text="wm.data.typeName"&g ...

Having difficulty retrieving the value of a form using javascript

I've been struggling to retrieve the value of a form using JavaScript, but for some reason it keeps coming up as undefined, almost as if the input doesn't exist. Here's the HTML snippet: %@ Language="VBScript" CodePage=65001 %> ... Th ...

Encountering Zip File Corruption Issue post MongoDB Download

My goal is to attach a zip file to an object and request the zip file when the object is called from the client. I was successful in uploading the zip file using the following code: const upload = multer({ fileFilter(req, file, cb){ if (!file. ...

AngularJS: Utilizing angular-filter for grouping by two columns

I may come across as a bit confusing, so please allow me to clarify. Note: An operational piece of code (POC) has been included with this post. I have an array of objects with 3 properties: name name team team_rank $scope.players = [ {name: & ...

What is the best way to implement function chaining in TypeScript?

I'm interested in implementing function chaining in typescript. Let's consider a sample class: export class NumberOperator { private num; constructor(initialNum) { this.num = initialNum; } public add(inc = 1) { this.num += inc ...

Issue with uploading files in headless mode using Webdriverio V9

After upgrading from v8 to v9, I encountered an issue with file uploads in headless mode. The code worked fine in non-headless mode, but failed in headless mode. I would appreciate any assistance with this. capabilities: [ { maxInst ...

Next.js displays an error when attempting to update the `AuthContextProvider` component while rendering the `Login` component

I have developed a basic next.js application that involves user login functionality through a graphql-api. The login process utilizes the react context-API to update the context once the user successfully logs in. Upon successful login, the intention is to ...

Access the Google Picker API using pre-saved login credentials

Recently, I successfully integrated the Google Picker API into my project. This feature prompts a window for Google authentication and then displays all the files stored in Google Drive. However, I now have a specific requirement where I want to access th ...

The Date Filter is causing a glitch in formatting the date value

I have a variable called dateSubmitted with the value of "dateSubmitted": "07-09-20:11:03:30" Currently, I am utilizing Angular Version 7 Within my HTML code, I am using the date filter to format the date like so: <td> {{element.dateSubmi ...

Converting audio information into a string format using Flask, followed by decoding it using Javascript

I have created a Python Flask application with the functionality displayed below. Here, I am utilizing Azure text to speech to synthesize voice from text. @app.route("/retrieve_speech", methods=['POST']) def retrieve_speech(): text= ...

Steps for displaying detailed information about a single product on an Ecommerce page

Currently in the process of developing my Ecommerce project, I have successfully created a product grid with links to each specific product. However, I am facing an issue where I am unable to view the data of each individual item. Below is the code for my ...