Typescript: Subscribed information mysteriously disappeared

[ Voting to avoid putting everything inside ngOnit because I need to reuse the API response and model array in multiple functions. Need a way to reuse without cluttering up ngOnInit.

I could simply call subscribe repeatedly in each function to solve the problem.

Tried other solutions on SO, but most suggest putting everything inside ngOnInit. I prefer to keep only necessary functions there.

Please assist in creating a function for reusing API responses or initialized models like this.menu = data;]

Attempting to display a menu from an API response.

Additionally, requiring the response to be used several times in different functions, facing null values outside of the subscribe block.

Code snippet:

import { Component, OnInit } from '@angular/core';

// import { LoginModel } from "../../models/login/login-model";
import { MenuModel } from "../../models/menu/menu-model";
import { SubmenuModel } from "../../models/submenu/submenu-model";
// import { AccessModel } from "../../models/access/access-model";

import { MenuService } from "../../services/menu/menu.service";
import { SubmenuService } from "../../services/submenu/submenu.service";

@Component({
  selector: 'app-admin-access',
  templateUrl: './admin-access.component.html',
  styleUrls: ['./admin-access.component.css']
})
export class AdminAccessComponent implements OnInit {

  menu: MenuModel[] = null;
  submenu: SubmenuModel[] = null;

  constructor(private menuService: MenuService, private submenuSerive: SubmenuService) { }

  ngOnInit(): void {
    this.getMenu();
    this.printMenu();
  }

  getMenu() {
    this.menuService.GetAllMenu().subscribe((data: MenuModel[]) => {
      this.menu = data;
      console.log("first use : ");
      console.log(data);

      console.log("second use : ");
      console.log(this.menu);
    })
  }

  printMenu(){
    console.log("third use : ");
    console.log(this.menu);
  }
}

Output :

https://i.stack.imgur.com/r7tMp.png

All responses are null from the printMenu() function. Why is this happening? I subscribed and stored the value previously.

How can I permanently retain a value from an API response?

Answer №1

(I am sharing this solution to help beginners who may struggle with similar issues in the future, so they don't have to waste time and effort trying to solve problems that others have already faced.

There are two key points I want to emphasize, especially for beginners [please refrain from commenting on them - everyone is entitled to express their feelings about their questions without judgment]:

1. pros:

Many people did not grasp what I was asking for. While some provided suggestions (which I appreciate), most didn't make an effort to understand or inquire further. So, beginners, be patient until you find someone who truly understands your question.

2. Docs

In short - the documentation can be a challenge for beginners.

My experience with Angular's documentation reveals that it may not be helpful unless you have a certain level of programming knowledge. Although I had never used `observable` or `ngOnInit` before in such a manner, someone guided me through it. I still have much to learn about `observable` and `ngOnInit`, but I now have a better understanding.

For beginners: If you find the documentation difficult to comprehend, consider turning to YouTube tutorials instead of relying solely on official docs.

Issue Explanation:

Remember my ngOnInit?

ngOnInit(): void {

    //line 1

    this.getMenu();

    //line 2

    this.printMenu();
    
}

`ngOnInit` does not execute line by line. If it did, I could have corrected the value earlier rather than ending up with a null value (as seen in the screenshot). `ngOnInit` executes all at once, not sequentially like in a regular function.

Solution

When I found the solution, I realized there was no coding error. I simply needed to call `printMenu()` outside of `ngOnInit` after initializing `getMenu()` within `ngOnInit`.

How do I call it outside? I need an event.

The easiest way to trigger an event is by creating a button with a click event.

Here is my .html:

<button (click)="this.printMenu()"> Let's test it </button>

.ts:

import { MenuService } from "../../services/menu/menu.service";
import { SubmenuService } from "../../services/submenu/submenu.service";

@Component({
  selector: 'app-admin-access',
  templateUrl: './admin-access.component.html',
  styleUrls: ['./admin-access.component.css']
})
export class AdminAccessComponent implements OnInit {

  menu: MenuModel[] = null;
  submenu: SubmenuModel[] = null;

  constructor(private menuService: MenuService, private submenuSerive: Sub
menuService) {
}

  ngOnInit(): void {
    this.getMenu();
  }

  getMenu() {
    this.menuService.GetAllMenu().subscribe((data: MenuModel[]) => {
      this.menu = data;

    })
  }

  printMenu(){
    console.log("third use : ");
    console.log(this.menu);
  }
}

With minimal changes, I removed `printmenu` from `ngonit` and called it using an event (such as a button click).

[I also added comments indicating "first use" and "second use" in my code]

https://i.stack.imgur.com/2RD02.png

Answer №2

Based on your response, it seems like there is still a misunderstanding about the reason why the variable remains undefined when you try to print it outside of the subscription. The assignment of this.menu happens asynchronously. To gain more insights into this issue, I recommend taking some time to go through the answer provided in the attached link: this.

The variable doesn't get assigned any value when printed within the printMenu() function. Therefore, your explanation is still incorrect as it assumes that the variable has been defined by the time you press the button, which may not always be the case. Remember, with asynchronous operations, you can never guarantee that a variable has already been defined.

To Replicate the Error

If you want to recreate this error intentionally, you can introduce an artificial delay using the RxJS delay operator. In my example, I have added a 10-second delay. So, if you click the button within the first 10 seconds after the application launches, the printMenu() function will still output null. In real-world situations, such delays might be caused by the processing time of the GetAllMenu() function over which the frontend has no control.

import { delay } from "rxjs/operators";

@Component({
  selector: 'app-admin-access',
  templateUrl: './admin-access.component.html',
  styleUrls: ['./admin-access.component.css']
})
export class AdminAccessComponent implements OnInit {
  menu: MenuModel[] = null;
  submenu: SubmenuModel[] = null;

  constructor(private menuService: MenuService, private submenuSerive: SubmenuService) { }

  ngOnInit(): void {
    this.getMenu();
  }

  getMenu() {
    this.menuService.GetAllMenu().pipe(
      delay(10000)             // <-- simulating a 10-second delay
    ).subscribe((data: MenuModel[]) => {
      this.menu = data;        
    });
  }

  printMenu() {
    console.log(this.menu);    // <-- will still show `null` if called within 10 seconds
  }
}

A better approach would be to make all subsequent statements asynchronous as well.

Solution: Leveraging RxJS ReplaySubject

ReplaySubject serves as a multi-cast observable that buffers and emits the last n emitted values immediately upon subscription. A buffer size of 1 should suffice for this scenario. You can push the source notification from GetAllMenu() into this observable where other components can subscribe to it.

import { ReplaySubject } from "rxjs";

@Component({
  selector: 'app-admin-access',
  templateUrl: './admin-access.component.html',
  styleUrls: ['./admin-access.component.css']
})
export class AdminAccessComponent implements OnInit {
  menu$: ReplaySubject<MenuModel[]> = new ReplaySubject<MenuModel[]>(1);
  submenu: SubmenuModel[] = null;

  constructor(private menuService: MenuService, private submenuSerive: SubmenuService) { }

  ngOnInit(): void {
    this.getMenu();
    this.printMenu();
  }

  getMenu() {
    this.menuService.GetAllMenu().subscribe((data: MenuModel[]) => {
      this.menu$.next(data);        // <-- pushing value to the `ReplaySubject` observable
      console.log("first use : ");
      console.log(data);
    });
  }

  printMenu(){
    this.menu$.subscribe((data: MenuModel[]) => {     // <-- subscribing to the `ReplaySubject` observable
      console.log("third use : ");
      console.log(data);
    });
  }
}

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

There was an issue encountered while attempting to differentiate an object in the Angular datatable results. The data table only accepts arrays and iterables

Looking to retrieve user data from an API using the httpClient post method in Angular 5, I faced a challenge as I couldn't properly display the retrieved information in a datatable. Below are snippets of the code that I have experimented with: User ...

Accessing the OSRM API allows users to determine the distance to the closest emergency station

I am currently working on a typescript project where I need to calculate the distance to the nearest police station and fire station. My approach involves utilizing typescript for this task. Initially, I attempted to use the following URL that I discovere ...

Is there a different way to retrieve the tag name of an element besides using

Currently, I am dealing with an outdated version (Chromium 25) of chromium. My goal is to utilize the tagName method in order to retrieve the name of the specific HTML tag being used. While I am aware that Element.tagName functions for versions 43 and ab ...

Navigating a page without embedding the URL in react-router-dom

In my application, I am utilizing react-router-dom v5 for routing purposes. Occasionally, I come across routes similar to the following: checkup/step-1/:id checkup/step-2/:id checkup/step-3/:id For instance, when I find myself at checkup/step-1/:id, I int ...

Conflicting React types found in pnpm monorepo

In the process of converting an inherited monorepo from yarn+lerna to pnpm workspaces, I am encountering some issues. Specifically, there are mismatching React versions causing errors in typescript. It seems that TypeScript is not recognizing the closest @ ...

Enhance your Primeng split button with an added icon when selected

Is it possible to add a tick icon when the user selects the click option in a split button? Any advice on how to achieve this would be appreciated. Thank you. For example, similar to the image below: https://i.stack.imgur.com/owOgE.png ...

Sharing information among components in Angular

This is the structure of my page: app.component.html <app-header></app-header> <router-outlet></router-outlet> <app-footer></app-footer> The data loading process occurs in the app.component.ts file via a shared ser ...

Looking to implement nested routes in Angular to ensure that each component's view updates properly

app-routing.module.ts import { NgModule } from '@angular/core'; import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; import { BreadcrumbComponent } from './main-layout/breadcrumb/breadcrumb.component'; im ...

Components loading in Angular result in lat long being undefined in google map integration

I am currently utilizing Ionic-angular AGM. I am facing an issue where I am unable to retrieve my current location when the component loads, as it seems like the component is being fired before the latitude and longitude are ready. How can I make this proc ...

What steps can be taken to initiate Nx Release on the apps/* when modifications are made to the connected libs/* modules?

Trying out the nx release command but struggling to get it to release an app when there are changes to a dependent module. Examining the graph below, you can see that I have 3 apps, with 2 depending on the shared-ui module. If I directly modify the apps, ...

Tips for asynchronously updating a model in TypeScript

I have been working on a function to hide the element for connecting to Facebook upon successful connection. I have implemented two functions, success and error, which trigger after Firebase successfully logs in the user. While I can confirm that these fun ...

Setting the initial state for a React toggle custom hook

After clicking a chevron button, an accordion component below it expands to display its children components. The goal is to ensure that each chevron button operates independently so that only the clicked button expands its related accordion. Upon initial ...

Tips for effectively managing components during navigation within dynamically loaded components

Our interface includes 3 main navigations for tab views: tab1, tab2, and tab3. Additionally, there is a navigation from the side menu. Each tab dynamically loads components, allowing seamless navigation between different parts of the application. When sw ...

Trapped in a never-ending cycle caused by failing to dispatch an action within ngrx/effects

My current setup involves using Angular2, ngrx/store, and ngrx/effects for state management. I have encountered an issue where I am unable to display an error message when a specific action fails within an @Effects() block. Here is the problematic code sn ...

What is the process for implementing a unique Angular theme across all components?

I created a unique Angular theme called 'my-theme.scss' and added it to the angular.json file. While it works with most Angular Elements, I am facing issues getting it to apply to certain HTML Elements inside Angular Components. For example: < ...

The initial invocation of OidcSecurityService.getAccessToken() returns null as the token

Our internal application requires all users to be authenticated and authorized, including for the home page. To achieve this, we use an HttpInterceptor to add a bearer token to our API requests. Initially, when rendering the first set of data with the fir ...

Steps to implement the click functionality on the alert controller and modify the language in Ionic 4

I am currently developing a multilingual Ionic 4 app and have implemented the alert controller to display language options. However, I am facing an issue on how to dynamically change the language based on user selection. Below is my app.component.ts code ...

IntelliJ is unable to locate the scss import when using the includePaths option in stylePreprocessorOptions

Having trouble with IntelliJ 2019.2.2 Ultimate not detecting scss imports from stylePreprocessorOptions - includePaths Here is the directory structure: https://i.stack.imgur.com/SQEDT.png In my angular.json file, I have specified: "stylePreprocessorOpt ...

What is the process to generate a universal error page in Angular?

I have implemented a simple example in Angular 6 using a globalErrorHandler for error cases. I have also included a loader that runs after the login operation and before loading data into a table. Everything seems to be working fine, but I have encountered ...

What is the best way to utilize a service that has been imported using forRoot() within a feature module

Currently utilizing the package found at: https://github.com/troyanskiy/ng2-resource-rest, I am encountering difficulties when attempting to correctly import it into my feature modules. The documentation provides the following instructions: @NgModule({ ...