The Angular service retrieves only the default values

I'm currently following an Angular tutorial and encountering some issues.

Problem #1: The problem arises when using two services, recipe.service.ts (handles local data manipulation) and data-storage.service.ts (stores data in Firebase). When the getRecipes() method is called from data-storage.service.ts, only the default values are returned instead of the updated ones.

For better clarification, refer to the image: Browser Dev Tools Console. The point where the Save Data button is clicked is highlighted in red in the above image.

Problem #2: The instructor uses this.recipes.slice() without any parameter to fetch data. However, if I use it, the newly added or deleted values are not reflected. To view the changes, I used this.recipes.slice(0,this.recipes.length). My question is, if the length changes, what is the correct way to reflect it?

Contents of recipe.service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

import { Recipe } from './recipe.model';
import { Ingredient } from '../shared/ingredient.model';
import { ShoppingListService } from '../shopping-list/shopping-list.service';

@Injectable()
export class RecipeService {
  recipesChanged = new Subject<Recipe[]>();

  // private recipes: Recipe[] = [];
  private recipes: Recipe[] = [
    new Recipe(
      'Tasty Schnitzel',
      'A super-tasty Schnitzel - just awesome!',
      'https://upload.wikimedia.org/wikipedia/commons/7/72/Schnitzel.JPG',
      [
        new Ingredient('Meat', 1),
        new Ingredient('French Fries', 20)
      ]),
    new Recipe('Big Fat Burger',
      'What else do you need to say?',
      'https://upload.wikimedia.org/wikipedia/commons/b/be/Burger_King_Angus_Bacon_%26_Cheese_Steak_Burger.jpg',
      [
        new Ingredient('Buns', 2),
        new Ingredient('Meat', 1)
      ])
  ];

  constructor(private slService: ShoppingListService) {}

  setRecipes(recipes: Recipe[]) {
    this.recipes = recipes;
    this.recipesChanged.next(this.recipes.slice(0,this.recipes.length));
    console.log('setRecipes');
    console.log(this.recipes.slice(0,this.recipes.length));
  }

  getRecipes() {
    console.log('getRecipes');
    console.log(this.recipes.slice(0,this.recipes.length)); 
    return this.recipes.slice(0,this.recipes.length);
  }

  // More methods...

}

contents of data-storage.service.ts

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { RecipeService } from "../recipes/recipe.service";
import { Recipe } from "../recipes/recipe.model";
import { map, tap } from 'rxjs/operators';

@Injectable({providedIn: 'root'})
export class DataStorageService {
    constructor(private http: HttpClient, private recipeService: RecipeService) {}

    storeRecipes() {
        const recipes = this.recipeService.getRecipes();
        console.log('storeRecipes');
        console.log(recipes);

        // Code for storing recipes

    }

    fetchRecipes() {
        return this.http
        .get<Recipe[]>(
          'https://....../recipes.json'
        )
        .pipe(
          map(recipes => {
            return recipes.map(recipe => {
              return { ...recipe, ingredients: recipe.ingredients ? recipe.ingredients : [] };
            });
          }),
          tap(recipes => {
            this.recipeService.setRecipes(recipes);
          })
        )
    }
}

Thank you in advance.

I expected data-storage.service.ts to retrieve the updated values from recipe.service.ts. Am I creating a separate object by mistake somewhere?

Answer №1

UPDATE

The issue originated from the creation of two instances of RecipeService

  1. In the app.module.ts, there is one instance of recipe service, which is the correct placement as it needs to be at the same level as the data service!

  2. The problem arose from the second instance created when RecipeService was added to the providers array in recipes.component.ts. This resulted in a duplicate instance that received all updates but was not accessible to the data service as Dependency Injection assigned the component instance instead of the module instance.

recipes.component.ts

import { Component, OnInit } from '@angular/core';
import { Recipe } from './recipe.model';
import { RecipeService } from './recipe.service';

@Component({
  selector: 'app-recipes',
  templateUrl: './recipes.component.html',
  styleUrl: './recipes.component.css',
  // providers: [RecipeService] // <- this led to a separate instance; removing it resolved the issue!
})
export class RecipesComponent implements OnInit {
...

Check out Stackblitz Demo (could have issues due to commit problems)


Arrays are stored as references in memory rather than actual values.

When manipulating arrays by adding or removing elements, ensure to update the array reference using array de-structuring so that the new reference reflects the latest changes, potentially resolving any issues encountered!

addRecipe(recipe: Recipe) 
  {    
    this.recipes.push(recipe);
    this.recipes = [...this.recipes];  // <- modification made here!        
    this.recipesChanged.next(this.recipes);
    console.log('addRecipe');
    console.log(this.recipes);
  } 

  deleteRecipe(index: number) 
  {
     
    this.recipes.splice(index, 1);       
    this.recipes = [...this.recipes];  // <- modification made here!
    this.recipesChanged.next(this.recipes);
    console.log('deleteRecipe');
    console.log(this.recipes);
  }

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

"Troubleshooting: Why are errors not appearing in ts-node

Whenever I encounter an error in my code while compiling with ts-node, the error does not seem to appear in the console. For instance:let data = await fs.readFileSync(path); In the following code snippet, I am using "fs" to read a file by passing a path ...

The useEffect hook is failing to resolve a promise

I have received a response from an API that I need to display. Here is a snippet of the sample response (relevant fields only): [ { ...other fields, "latitude": "33.5682166", "longitude": "73 ...

Contrary to GraphQLNonNull

I am currently working on implementing GraphQL and I have encountered a problem. Here is an example of the code I wrote for GraphQL: export const menuItemDataType = new GraphQL.GraphQLObjectType({ name: 'MenuItemData', fields: () => ...

Working with intricately structured objects using TypeScript

Trying to utilize VS Code for assistance when typing an object with predefined types. An example of a dish object could be: { "id": "dish01", "title": "SALMON CRUNCH", "price": 120, ...

Dynamic placeholder modification depending on form selection

How can I dynamically change the placeholder text based on user selection? //div with a toggle for two options <div fd-form-item> <label fd-form-label for="select-targetType">Showroom type:</label> <select class=&q ...

Leveraging TypeScript unions within functions to handle and throw errors

As a newcomer to TypeScript, I've encountered an odd error that I need help with. I have various objects sending data to the server and receiving fresh data back of the same object type. These objects use a shared method for sending the data, so I ap ...

Enhance the capabilities of a basic object by incorporating a superclass through the creation of

I'm currently developing a library using Typescript 2.0 that can be utilized from both Typescript and JavaScript. Within the library, there is a class called Component and a function named registerComponent, both written in Typescript. My goal is to ...

Stop unauthorized access to specific pages on ionic platform unless the user is logged in

I have a scenario where I want to redirect users from the welcome page (welcome.page.ts) when they click on the login button. If they are already logged in, they should be redirected to the home page (home.page.html). Below is the code snippet from my welc ...

Animated CSS side panel

I'm currently working on creating an animation for a side menu. The animation works perfectly when I open the menu, but the problem arises when I try to animate it back closed. Is there a property that allows the animation to play in reverse when the ...

Prevent authenticated users in Angular2 from accessing certain routes

In the main.ts file, I have defined a set of routes: const routes: RouterConfig = [ { path: '', component: HomeComponent }, { path: '', redirectTo: 'home', terminal: true }, { path: 'dashboard', component: Das ...

Would it be frowned upon to rely on store instead of data binding for inter-component communication when accessing my data?

Within my current framework, I house the primary business logic within selectors and effects. Components are able to request data by triggering an action that queries the necessary information through selectors. Apart from instances where *ngFor is utili ...

Enhancing Responses in NestJS with External API Data

I'm a beginner in NestJs, Graphql, and typescript. I am trying to make an external API call that is essentially a Graphql query itself. The goal is to modify the response, if necessary, and then return it for the original request or query, in this ca ...

Angular2's integration of backend API calls

My backend calls are functioning correctly, but I'm encountering an issue with promises. I am unable to retrieve the data from the first promise in order to make the second call. Any insights on where I might be going wrong? login() { if (thi ...

Encountering this issue: Unable to access the property 'length' of an undefined variable

I'm currently developing an app using nuxt, vuetify 2, and typescript. Within the app, I have radio buttons (referred to as b1 and b2) and text fields (referred to as t1, t2, t3). When a user clicks on b1, it displays t1 and t3. On the other hand, w ...

Is there a way to transfer innerHTML to an onClick function in Typescript?

My goal is to pass the content of the Square element as innerHTML to the onClick function. I've attempted passing just i, but it always ends up being 100. Is there a way to only pass i when it matches the value going into the Square, or can the innerH ...

"Stylish form field design with outlined borders that displays a subtle hover

I am attempting to modify the background color of a mat-form-field outlined when hovering with the mouse. .mat-form-field.mat-form-field-appearance-outline.mat-form-field-outline-thick { // HOVER EFFECT background-color: $dark-blue-200; } The above ...

I am facing difficulty in retrieving data from Firestore using Angular

I've been utilizing the AngularFireList provided by @angular/fire/database to retrieve data from firestore. However, despite having data in the firestore, I am unable to fetch any information from it. import { Injectable } from '@angular/core&apo ...

What is the process for ensuring that the "ng-multiselect-dropdown" is a mandatory field within Angular 7?

Is there a way to require the ng-multiselect-dropdown field to have at least one selected item? <ng-multiselect-dropdown [placeholder]="'Select countries'" [data]="countries" [(ngModel)]="countriesSelectedItems" [settings]="co ...

Set up local npm packages for easy access by different projects

Can someone explain to me how npm works compared to Maven (I have a background in Java) when it comes to package management? I've developed a generic component using Angular 4 that will be used across multiple projects. I've published it to our n ...

Error: Unable to locate the variable 'content' in the TypeScript code

Having an issue with my navigateToApp function. In the else condition, I am calling another function called openModalDialog(content). Unfortunately, I am encountering an error stating Cannot find name content. Can someone help me identify what is wrong h ...