Storing asynchronous values in a local variable and showcasing them in a template the Angular way

I have an array of Objects from my service component that I want to read five random Objects from and display them one after another in the template with a delay. Once the last Object has been shown, it should start again from the beginning. I managed to achieve this without using Observables or RxJS library, but I believe there might be a more efficient way to do it. I'm curious about the Angular approach to accomplish this task.

Just to note, I am working with Angular 4.

service component:

getWeatherData(): Observable<IWeatherdata[]> {
        return this.http.get("./assets/weatherdata.json")
            .map(res => {
                let data = res.json();
                return data; // the data consists of 100 Objects
            });
}
app component:

itemsWeatherData: IWeatherdata[] = [];
weatherObject: IWeatherdata[] = [];

ngOnInit() {
  this.handleWeatherData();
}

handleWeatherData() {
    return this._searchService.getWeatherData()
      .subscribe (res => {
        while (this.itemsWeatherData.length <= 4) {
          let randomNum = this.randomNumber(0, 99); 
          let randomWeatherData = res[randomNum];
          if (!this.itemsWeatherData.includes(randomWeatherData)) {
            this.itemsWeatherData.push(randomWeatherData);
          }
        }
        (function getWeatherObject(index = 0) {
          this.weatherObject[0] = this.itemsWeatherData[index];
          index < this.itemsWeatherData.length - 1 
          ? setTimeout(getWeatherObject.bind(this), 20000, index += 1) 
          : setTimeout(getWeatherObject.bind(this), 20000, index = 0); 
        }).bind(this)(); 
      })
    }

in the template:

<div *ngFor="let data of weatherObject">
   {{data}} 
</div>

edit /*

The input is an array of Objects like so:

[Obj1, Obj2, Obj3, Obj4, Obj5]

The output is an array where the element is updated with a 20-second delay like this (displaying only one element at a time from the input array and repeating):

[Obj1] // delay 20sec
[Obj2] // delay 20sec
[Obj3] // delay 20sec
[Obj4] // delay 20sec
[Obj5] // delay 20sec
[Obj1] // delay 20sec
and so on ...

*/ edit

Although my current implementation works as expected, it seems a bit performance-heavy. I'm interested in understanding the Angular way of achieving this and would appreciate any guidance on concepts or operators I should explore for a more efficient solution.

Any insights on the advantages of using observables or other methods over my current approach would also be helpful.

Thank you from an Angular newbie!

Answer №1

Utilize the async pipe to connect Observables to the template.

weatherObject: Observable<IWeatherdata[]>;

ngOnInit() {
  this.weatherObject = this._searchService.getWeatherdata()
      .pipe(map(res => {
        while (this.itemsWeatherdata.length <= 4) {
          let randomNum = this.randomNumber(0, 99); //randomNumber generates a random number between 0 and 100.
          let randomWeatherdata = res[randomNum];
          if (!this.itemsWeatherdata.includes(randomWeatherdata)) {
            this.itemsWeatherdata.push(randomWeatherdata);
          }
        }
        (function getWeatherObject(index = 0) {
          this.weatherObject[0] = this.itemsWeatherdata[index];
          index < this.itemsWeatherdata.length - 1 
          ? setTimeout(getWeatherObject.bind(this), 20000, index += 1) 
          : setTimeout(getWeatherObject.bind(this), 20000, index = 0); 
        }).bind(this)(); 
      }));
}

Here is an abbreviated initialization example:

weatherObject: Observable<IWeatherdata[]>;

ngOnInit() {
  this.weatherObject = this._searchService.getWeatherdata();
}

Simple template illustration:

<div *ngFor="let data of weatherObject | async">
   {{data}} 
</div>

An example template with an alias and loading message:

<div *ngIf="(weatherObject | async) as weather; else loading">
  <div *ngFor="let data of weather">
    {{data}} 
  </div>
</div>

<ng-template #loading>
  <p>Loading weather information...</p>
</ng-template>

Answer №2

Consider simplifying your code by implementing an "index" array:

    itemsWeatherData: IWeatherData[] = [];
    maxSize = 5;
    positions = new Set<number>(); // selected positions to show
    position = -1; // position to show

    ngOnInit() {
        setTimeout(() => this.position = ++this.position % this.maxSize, 2000);
        this.handleWeatherData();
    }

    handleWeatherData() {
        return this._searchService.getWeatherdata()
            .subscribe(res => this.selectRandomNumbers());
    }

    selectRandomNumbers() {
        this.position = -1;
        this.positions.clear();
        while (this.positions.size < this.maxSize) {
            this.positions.add(this.randomNumber(0, this.itemsWeatherData.length-1));
        }
    }

You can display a randomly selected object from the array using the following html:

<div *ngIf="position > -1">
    {{positions[position]}}
</div>

The displayed position changes every 2 seconds, as per your code.

The selectRandomNumbers function efficiently selects a small number of distinct random numbers (5) from a larger set (100). For a different ratio, a more complex method may be required, such as choosing a random number from a smaller array on each step...

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

Exploring the world of lighting and shadows in WebGL and Three.js

I'm having difficulty focusing lights on specific targets, specifically the main character, while also darkening the background. Additionally, I'm experiencing issues with the shadows not working properly in my code snippet related to lights and ...

JavaScript: Constructing an array composed of multiple arrays

I am attempting to create an array containing four arrays. Each of these four arrays will contain three numbers, with two of them being randomly selected from a set of numbers. Although I am not encountering any errors when running the code below, I am al ...

Error: Unable to dispatch a Redux-thunk action with additional arguments in React

Currently, I am attempting to work with TypeScript alongside Redux-Thunk and Redux. My goal is to pass an object to any action (I am utilizing dependency injection and need to pass either an object service or a string). However, I encountered the followin ...

Does JSON hijacking play a role with IE versions greater than 10 or Chrome versions greater than 30?

OWASP suggests wrapping json response with an object rather than returning a direct array. For instance: [{"id":5}] Is this vulnerability still relevant? Could it be exploited? After testing in Chrome, IE, and FF, I couldn't find a way to 'h ...

Creating a dynamic input box that appears when another input box is being filled

I have a form set up like this: <FORM method="post"> <TABLE> <TR> <TD>Username</TD> <TD><INPUT type="text" value="" name="username" title="Enter Username"/><TD> </TR> <TR> <TD>A ...

Tips for sorting the echarts line chart on a weekly basis using angular

How can I filter the echarts line chart by week in Angular? Currently, it displays all records like 2019-10-27 10:15:00, 2019-10-27 10:15:30, 2019-10-27 10:16:00, 2019-10-27 10:19:00. Instead, it should only show dates like 2019-10-27. For example, if ther ...

Ways to render an input field uneditable, yet permit certain keys to retain functionality

I have a single input box displaying a value that I want to be read-only, but still allowing the arrow keys, Home key, and End key to function properly. The input box contains multiple URLs separated by commas, and I need to be able to navigate between th ...

What is causing the col divs to stack instead of aligning next to each other?

I am a newcomer to Vue.JS and front-end programming in general. I wanted to create a page with a fixed sidebar and content below a navbar. Here is the configuration I tried: <template> <div class="container-fluid"> <div class ...

Adding HTML elements into an iframe's document and/or shadow DOM

Using a Chrome extension, I am injecting an iframe onto a webpage through JavaScript. By creating the iframe with JavaScript, I can access my HTML's URL and thought to fetch the DOM using $.get. var elt = document.createElement('iframe'); e ...

Leverage the pre-defined Ionic Sass variables for optimal styling

Is it possible to utilize existing Sass Variables in Ionic 5 for our custom CSS classes? The specific variables I am referring to can be found here: https://ionicframework.com/docs/v3/theming/overriding-ionic-variables/ I'm interested in creating som ...

experiencing difficulties utilizing the prompt package in nodeJS

Exploring the world of nodeJS as a novice, I recently dived into taking user input through the console in nodeJS. My journey led me to discover the prompt package. Here is snippet of the code I've been experimenting with: var prompt = require('p ...

I'm struggling to make the jquery parentsUntil function work properly

Would appreciate some help with using the jquery parentsUntil method to hide a button until a radio box is selected. I've been struggling with this for a few days now and can't seem to figure out what I'm doing wrong. Any insights would be g ...

Is it time to initialize the document with a specific parameter?

Can someone please explain the purpose of this code snippet to me? $(function($) { $.supermodal(); }); I understand that it's similar to waiting for the document to finish loading before running supermodal. However, I'm confused about the 2n ...

Using Node.js to inject dependencies into the app.js file

As I work on my node.js and typescript application, I followed the approach outlined in an article by Brian Love. You can find a sample code for the server.ts file below: import * as bodyParser from "body-parser"; import * as cookieParser from "cookie-par ...

Learn how to automatically set the checked state of a radio button by toggling another radio button

I have two sets of radio buttons, each with four options. The first set has no default selection (unchecked) using jQuery, while the second set has the first option checked by default. What I'm looking to achieve is that when a radio button in the f ...

Show the GitHub repositories of the user within a React application

Even though I am relatively new to React, I managed to create a GitHub search application. In this app, you can input a user's name in a search box and view their avatar, bio, username, etc. However, I'm facing an issue with fetching their reposi ...

Rearrange the sequence of numbers using JQuery when an HTML element is deleted

I'm currently working on a simple functionality where I have 5 rows, each with its own number. So initially, the rows are numbered from 5 to 1. If I remove a value like 3, the sequence should adjust to 4, 2, 1, indicating that I now have only 4 rows ...

Tips for utilizing Protractor.addMockModule during end-to-end testing

I am encountering an issue with my TypeScript test: describe ('User login functionality test', () => { beforeEach(()=> { browser.addMockModule('mockLogin', function() { angular.module('mockLogin', []).value('foo&a ...

Trouble with navigation following ng build --prod execution

Once I've set everything up and run ng build --prod, my navigation stops working. While links on buttons function correctly, trying to directly enter a URL like www.domain.tld/login results in an error message: The requested URL /login was not foun ...

How can one access DOM elements (getting and setting values) that are nested within an *ngFor loop?

How can I access the <span> and <select> elements in my code template shown below? <div *ngFor="---"> <div> <span></span> <select> <option></option> <option></option> ...