Course completed following the module

As a newcomer to Angular, I am facing an issue with saving data in a class and reading it into a component. It seems that the component is rushing to display results before the class has finished processing them, resulting in an error message being printed prematurely:

https://i.sstatic.net/FZCuJ.png

The correct values are only displayed later on, but strictly within the subscribe block, as shown in the image.
Here's my class implementation:

import { Injectable } from '@angular/core';
import { WeatherForecastApiService } from '../weatherForecastApiService/weather-forecast-api.service';

@Injectable({
    providedIn: 'root',
})
export class WeatherClass {
    public weather: WeatherFeature;

    constructor(
        private wfas: WeatherForecastApiService,
    ) {
        this.wfas.getItalyWeatherData('Pisa').subscribe((response) => {
            const ks: string[] = ['name', 'main', 'temp'];
            this.weather = {
                cityName: response[ks[0]],
                degrees: response[ks[1]][ks[2]] - 273.15,
            }; 
            console.log('clean print', this.weather.cityName);
            console.log('clean print', this.weather.degrees);
        });
    }
    public showValues() {
        console.log('undefined print in component', this.weather.cityName);
        console.log('undefined print in component', this.weather.degrees);
    }
}

And here's my (premature) component code:

import { AfterViewInit, Component } from '@angular/core';
import { WeatherClass } from '../weatherObject/weather-class';

@Component({
    selector: 'app-weather-forecast',
    templateUrl: './weather-forecast.component.html',
    styleUrls: ['./weather-forecast.component.scss'],
})
export class WeatherForecastComponent implements AfterViewInit {
    constructor(
        private weather: WeatherClass,
    ) {}
    ngAfterViewInit() {
        this.weather.showValues();
    }
}

I have encountered a similar issue with JavaScript in the past, where asynchronous behavior caused complications. I am eager to resolve this question and understand the underlying concepts better.

Answer №1

When accessing the data from 'this.wfas.getItalyWeatherData', keep in mind that it's asynchronous. This means that your cityName and degree may not be set by the time the 'showValue()' method is called, even if you have it in ngAfterViewInit.</p>

<p>A solution could be to introduce a subject in your WeatherClass, which can be subscribed to inside your WeatherForecastComponent. Consider using BehaviorSubject as they provide a default value and allow you to retrieve a value upon first subscription.</p>

<pre><code>@Injectable({
    providedIn: 'root',
})
export class WeatherService {

    public weatherInfo = new BehaviorSubject<WeatherFeature>(undefined);

    constructor(
        private wfas: WeatherForecastApiService,
    ) {
        this.wfas.getItalyWeatherData('Pisa').subscribe((response) => {
            const keys: string[] = ['name', 'main', 'temp'];
            // This updates the values for any subscribers.
            this.weatherInfo.next({
                cityName: response[keys[0]],
                degrees: response[keys[1]][keys[2]] - 273.15,
            }); 
        });
    }
}
@Component({
    selector: 'app-weather-forecast',
    templateUrl: './weather-forecast.component.html',
    styleUrls: ['./weather-forecast.component.scss'],
})
export class WeatherForecastComponent implements AfterViewInit {
    public weather: WeatherFeature;

    constructor(
        private weatherService: WeatherService,
    ) {}

    ngAfterViewInit() {
        // Subscribing here will fetch the current or future emitted values.
        this.weatherService.weatherInfo.subscribe(
            (weatherInfo) => this.weather = weatherInfo
        );
    }
}

In this example, I added a property to hold the value in your component.

I also included a behavior subject in your WeatherService so you can subscribe to get the value when needed.

To maintain good naming practices, I suggest renaming your WeatherClass to WeatherService. For further guidance on naming conventions, check out the Angular Style Guide which is a valuable resource for mastering the framework.

https://angular.io/guide/styleguide#service-names

Answer №2

The main issue here is that the function getItalyWeatherData does not finish executing before showValues is called. One common solution to this problem would be to structure your code like so:

WeatherClass ->

getItalyWeatherData() {
  return this.wfas.getItalyWeatherData('Pisa');
}

WeatherForecastComponent ->

ngOnInit() {
  this.weather.getItalyWeatherData().subscribe(res => {insert same code as your current implementation});
}

Implementing it this way ensures that the data will be displayed as soon as the API call returns a response.

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

Having a problem where the Next.js project is functioning in development mode, but encountering a "module not found" error

After following multiple tutorials to integrate Typescript into my existing app, I finally got it running smoothly in dev mode using cross-env NODE_ENV=development ts-node-script ./server/index.js However, when I execute next build, it completes successfu ...

Generating data types based on the output of functions

I'm currently working on optimizing my typescript react code by reducing repetition. I'm curious to know if there's a way to generate a type based on the return type of a known function? For example: const mapStateToProps = (state: StoreSt ...

Is it possible to include parameters in an HTML GET request with Electron.Net?

I have successfully implemented a function in an Angular component within an Electron application using HttpClient: var auth = "Bearer" + "abdedede"; let header = new HttpHeaders({ "Content-Type": 'application/json&a ...

Disable TS4023 error in TypeScript: Unable to name external module "xyz"

//custom-slice.js import { createCustomSlice } from '@my/data-toolkit'; /* ***********************For Managing all the divisions data****************************** */ export const divisionDataSlice = createCustomSlice({ name: 'divisionda ...

What is the process for assigning a serial number to each row in the MUI DataGrid?

Initially, the server is accessed to retrieve some data. After that, additional data is added. While the data does not contain an ID, the form must still display a serial number. const columns: GridColDef[] = [ { field: 'id' ...

I am encountering an issue where body-parser is not functioning properly with typescript. Whenever I make a request, the request.body is returning as undefined

Below is the code snippet for my Express application using TypeScript version 3.7.4: import bodyParser from "body-parser"; import config from "config"; import cookieParser from "cookie-parser"; import express from "express"; import mongoose from "mongoose ...

Effect fails to activate on the third occurrence of the action

After successfully running on the first two action dispatches, the effects fail to trigger on the third time. I have tried the solutions provided in this thread and here, but none of them work for me. Here is the relevant code snippet: @Effect() get ...

Is it possible to use the `fill` method to assign a value of type 'number' to a variable of type 'never'?

interface Itype { type: number; name?: string; } function makeEqualArrays(arr1: Itype[], arr2: Itype[]): void { arr2 = arr2.concat([].fill({ type: 2 }, len1 - len2)); } What is the reason for not being able to fill an array with an ob ...

Comparing Angular 2's Seed and CLI: A closer look at

Exploring my options for beginning a fresh Angular 2 Project. I stumbled upon angular2-seed and angular-cli as potential tools for kickstarting the project. However, I am deliberating on which one to opt for and curious about their respective advantages a ...

What is the correct way to declare a variable with a generic type parameter?

Exploring the following code snippet that showcases a React component being defined with a type argument named TRow: function DataTable<TRow> ({ rows: TRow[] }) { return ( ) } Prior to this implementation, ES6 was utilized and components were c ...

angular material drag and drop triggers a callback once the css transition has completed

I have successfully implemented a list of elements that can be dragged and dropped using Angular Material's drag and drop feature, similar to the tutorial available at this link. In my implementation, I have a "drop(event)" function that not only mov ...

Group records in MongoDB by either (id1, id2) or (id2, id1)

Creating a messaging system with MongoDB, I have designed the message schema as follows: Message Schema: { senderId: ObjectId, receiverId: ObjectId createdAt: Date } My goal is to showcase all message exchanges between a user and other users ...

What is the method for accessing an anonymous function within a JavaScript Object?

Currently facing an issue with a Node.js package called Telegraf, which is a bot framework. The problem arises when trying to create typings for it in TypeScript. The package exports the following: module.exports = Object.assign(Telegraf, { Composer, ...

Concealing a VueJs component on specific pages

How can I hide certain components (AppBar & NavigationDrawer) on specific routes in my App.vue, such as /login? I tried including the following code in my NavigationDrawer.vue file, but it disables the component on all routes: <v-navigation-drawer ...

Is there a way to identify modifications in an Object and an Array when utilized as Input attributes in a sub-component?

After exploring the following two questions, I found that there weren't any helpful answers: Detect change in object of input array Detect changes in component input when it is a object In my current scenario, I am faced with the need to pass variou ...

Navigating with the Angular router and then triggering a reload

Is there a way to reload the app after navigating to a specific route? I am currently using router.navigate to direct users to different routes based on their roles. It's working fine, but I need to reload the page after routing when coming from the ...

Angular2 - Implementing Form Validation for Dynamically Generated Input Fields Based on Conditions

My goal is to create a basic form that allows users to select between two options: Local and Foreigner. If the user does not make a selection, the form should be marked as invalid. Choosing Local will make the form valid, while selecting Foreigner will rev ...

Paper-dropdown-menu component failing to render properly in web browser

Encountering an issue with the rendered HTML for a basic paper-dropdown-menu. Instead of displaying as a styled menu, the list items are just appearing as a plain list on the page. Upon clicking the rendered paper-input component within the dropdown, the ...

Ways to elegantly replace an object with thorough validation of its embedded properties

Consider the following code snippet: interface Human{ name:string age:number dimensions : { height:number width:number } } const base : Human ={ name:"Base", age:12, dimensions : { height:190, width:99 } }; const child ...

What is the best way to bundle a TypeScript package along with its dependencies for seamless integration with various Next.js projects on a local environment

Currently, I am immersed in a project with the following arrangement: / # repository root /core # A local, unpublished npm package used by both projectA and projectB /projectA # A Next.js app /projectB # Another Next.js app In my setup, I gene ...