Encountering a delay in receiving server data causing errors in Angular 2

Within the service class constructor, I make an http call to the server to fetch product category data and store it in the productCategories array. However, when I try to retrieve the productCategories array in the ngInit() function of my component, I encounter an error stating that the array is undefined.

It seems that the service constructor is making the call to the server and returning before the product category data is actually fetched. So, when the component requests the data using the getCategory(x, y) method of the service, the server call initiated in the constructor is still in progress, resulting in the productCategories array being declared as undefined. Eventually, the server call completes and the productCategories array is logged to the console.

How can I solve this issue?

@Injectable()
export class ProductCategoryService {
  private productCategories: ProductCategory[];
  private categoriesUrl = "/api/product/categories";

  constructor(private http: Http) { 
    console.log('ProductCategory.constructor() called');
    this.getProductCategoriesDb();
  }

  private getProductCategoriesDb() {
    console.log('ProductCategory.getCategories() called');

    this.http.get(this.categoriesUrl)
      .toPromise()
      .then((response: Response) => {
        this.productCategories = response.json();
        console.log('this.productCategories = ' + 
JSON.stringify(this.productCategories));
      })
      .catch(this.handleError);
  }

  public getProductCategory(name, type) {
    return this.productCategories
      .filter(category => (category.name == name && category.type == type))[0];
  }
}

Component:

export class ProductCategoryComponent implements OnInit {
  category: string;
  type: string;
  productCategory: ProductCategory = new ProductCategory();

  constructor(private route: ActivatedRoute, private productCategoryService: 
ProductCategoryService) { }

 ngOnInit() {
    this.route.params.subscribe(params => {
      this.category = params["category"];
      this.type = params["type"];

      console.log("category = " + this.category)
      console.log("type = " + this.type)


      this.productCategory = 
this.productCategoryService.getProductCategory(this.category, this.type);
      console.log("productCategory = " + this.productCategory);

    })  
  }

}

Sequence of logged statements:

ProductCategory.constructor() called ---- product-category.service.ts:27

ProductCategory.getCategories() called ---- product-category.component.ts:23

Error: productCategories array undefined:

ProductCategoryComponent_Host.html:1 ERROR TypeError: Cannot read property 'filter' of undefined at ProductCategoryService.webpackJsonp.../../../../../src/app/product-category/product-category.service.ts.ProductCategoryService.getProductCategory (product-category.service.ts:39) at SafeSubscriber._next (product-category.component.ts:27) at SafeSubscriber.webpackJsonp.../../../../rxjs/Subscriber.js.SafeSubscriber.__tryOrSetError (

Finally, the productCategories array is printed --- product-category.service.ts:33

this.productCategories = [{"_id":"599c5d93f36d286317108ffd","name":"Furniture","type":"Living Room","image":[{"label":"Living Room","name":"LivingRoom1.jpg"},{"label":"Sofas","name":"LivingRoom2.jpg"},{"label":"Sleeper","name":"LivingRoom3.jpg"},{"label":"Loveseats","name":"LivingRoom4.jpg"},{"label":"Chairs","name":"LivingRoom5.jpg"},{"label":"Ottomans","name":"LivingRoom6.jpg"},{"label":"Chaisers","name":"LivingRoom7.jpg"},{"label":"Recliners","name":"LivingRoom8.jpg"},{"label":"Sectionals","name":"LivingRoom9.jpg"},{"label":"Occasional Tables","name":"LivingRoom10.jpg"}],"items":["Sofas","Sleepers","Loveseats","Chairs","Ottomans","Chaises","Recliners","Sectionals","Occasional Tables"]}]

Answer №1

To solve the issue, it's best to rework the code rather than attempting to patch it. Your understanding of why the error is occurring is accurate, but you may want to consider a different method for handling data from the server.

This initial solution can be refined through continued iterations.

@Injectable()
export class ProductCategoryService {
  productCategoriesSbj: ReplaySubject<ProductCategory[]> = new ReplaySubject();
  private categoriesUrl = "/api/product/categories";

  constructor(private http: Http) { 
  }

  getProductCategoriesDb(): Observable<ProductCategory[]> {
    return this.http.get(this.categoriesUrl)
      .subscribe(response => this.productCategoriesSbj.next(response.json()))
      .catch(this.handleError);
  }

  getProductCategory(categories: ProductCategory[], name: string, type: string) {
    return categories.filter(category =>
        (category.name == name && category.type == type)
    )[0];
  }
}

Then, in your component:

export class ProductCategoryComponent implements OnInit, OnDestroy {

  category: string;

  type: string;

  productCategory: ProductCategory = new ProductCategory();

  productCategorySub: Subscription = new Subscription();

  constructor(private route: ActivatedRoute,
              private productCategoryService: ProductCategoryService){
  }

 ngOnInit() {
    this.route.params.subscribe(params => {
      this.category = params["category"] || '';
      this.type = params["type"] || '';

      this.productCategoryService.getProductCategoriesDb();
      productCategorySub = this.productCategoryService
          .productCategoriesSbj
          .subscribe(categories => {
              // now categories contain parsed response from the server
              this.productCategory = this.productCategoryService
                  .getProductCategory(categories, this.category, this.type);
          });
    })  
  }
}

 ngOnDestroy() {
     // unsubscribe from observable to avoid memory leaks
     this.productCategorySub.unsubscribe();
 }

Offered without many details, but I trust it will provide some assistance.

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

Develop a reusable input component in Angular

I have developed a custom text component with validation capabilities export class CustomInputComponent implements OnInit{ @Input() formGroup: FormGroup; @Input() label: string; @Input() placeholder: string; @Input() name: string; @Input() errorM ...

When variables are destroyed

Is it necessary to set every variable created within a component to null using ngOnDestroy in order to prevent memory leaks? While it makes sense for destroying plugins like audio players, what about regular variables that we create ourselves? ...

Associating function parameters with object types in TypeScript

In the conclusion of this post, I provide operational code for associating object types with a function that accepts an object containing matching properties. The code snippet I shared results in 'result' being resolved as: type result = { GE ...

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 ...

Converting javascript html object lowercase

Is there a way to dynamically adjust the height of specific letters in my label? Right now, I am overriding the text for the elements: let element = document.getElementById('xxx') element.textContent = 'Label' I attempted using <sup ...

Personalized prefix for Angular and WebStorm components

After starting a project in Angular with Visual Studio Code a few months ago, I decided to switch to WebStorm and upgraded the project to Angular 4.0 today. Although my project is running smoothly without any errors, I encountered an issue in WebStorm: ...

Disable dates that are more than 7 days from the current date using Material UI's Date

How can I restrict users from selecting dates more than 7 days after their initial selection? In the example image provided, the date of January 30th should be disabled for selection. https://i.stack.imgur.com/iTem4.png Below is the code snippet: const ...

angular table disabled based on condition

I have a table in my HTML file and I am trying to figure out how to disable the onClick function if the start date is greater than the current date. <ng-container matColumnDef="d"> <th mat-header-cell ...

How to manage Angular production site redirection

Having trouble with my application in a production environment. The routes are not working and causing 404 errors. What changes do I need to make in my project to ensure the routes work in this environment? Local: http://localhost:4200/test Production: h ...

Creating one-to-one relationships in sequelize-typescript can be achieved by setting up multiple foreign keys

I have a question about adding multiple foreign keys to an object. Specifically, I have a scenario with Complaints that involve 2 Transports. One is used to track goods being sent back, and the other is used for goods being resent to the customer. @Table({ ...

Creating a numeric sequence based on the date of a corresponding transaction - a step-by-step guide

INTRO I built an e-commerce app with TypeScript and Sequelize ORM. In the app, I have a table that generates sequential invoice numbers based on the current day. CREATE TABLE `dm_generate_trx` ( `id` int NOT NULL AUTO_INCREMENT, `date` date NOT NULL, ...

Getting a TypeError following the execution of 'npm update' and 'ng build -prod' commands

Recently, I updated my npm modules and attempted to run ng build -prod, a command that had previously been running smoothly. However, I encountered the following error: TypeError: extractedChunk.getNumberOfModules is not a function at ExtractTextPlugi ...

Unable to organize the data associated with a specific column in the header within an Angular material Table

viewing clinical history data the output I'm receiving is not in ascending or descending order Trying to organize the data in the table, utilizing the MatTableModule module alongside other required modules. However, after conducting research, I have ...

Jasmine's await function often leads to variables remaining undefined

When testing a function, I encountered an issue where a variable is created and assigned a value, but the payload constant always remains undefined. campaigns-card.component.ts async ngOnInit() { const { uid } = await this.auth.currentUser const { ...

Tips for effectively crafting a component capable of managing both a value and an observable for that specific value

I'm actually curious about two things. When is it appropriate to pass an observable of an object into a component versus resolving it with the | async method? If I want to create a versatile reusable component that can handle both scenarios - accept ...

What's the best way to provide route access to the same menu for two distinct user roles using AuthGuard within Angular?

I developed an application that includes three different user roles: ROLE_USER, ROLE_MODERATOR, and ROLE_ADMIN as shown below: export enum Role { User = 'ROLE_USER', Moderator = 'ROLE_MODERATOR', Admin = 'ROLE_ADMIN&apo ...

Angular APP_INITIALIZER Function Redirects Prematurely Prior to Completion of Observable List

Utilizing the rxjs from method to transform an array of Observable<void> elements into a single observable that is then passed through concatAll(): export const initApplication = (): (() => Observable<any>) => { // Inject all services ...

Dynamically access nested objects by utilizing an array of strings as a pathway

Struggling to find a solution for accessing nested object properties dynamically? The property path needs to be represented as an array of strings. For example, to retrieve the label, use ['type', 'label'] I'm at a roadblock wit ...

Is it possible for an app's feature module to access routing configurations from another lazily loaded module in Angular routing?

The functionality of our App is divided into multiple feature modules that are lazily loaded. Each module is loaded under different path matches, and some modules import a shared module containing reusable components. Everything seems to be working well so ...

Align the running of Angular code with a for loop

I am currently working on a project with Angular 9 and Spring Boot as the backend. The nature of the project is CMS based, where users can create forms using reactive form technology. These forms have the capability to include multiple file upload fields. ...