You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular双服务数据异常:默认值返回及slice使用问题

Angular学习中的两个技术问题

问题1:DataStorageService调用getRecipes()仅返回默认数据

使用recipe.service.ts(负责本地数据操作)和data-storage.service.ts(负责Firebase数据存储)时,从data-storage.service.ts调用getRecipes()方法仅返回默认数据,而非更新后的数据。原本预期能获取更新后的数据,怀疑是否创建了独立对象?

问题2:数组slice()用法疑问

教程中使用this.recipes.slice()获取数据,但我使用该写法时无法反映新增或删除的内容,改用this.recipes.slice(0,this.recipes.length)才能看到变化,想询问数组长度变化时的正确用法。


相关服务代码

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 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);
  }

  getRecipe(index: number) 
  {
    console.log('getRecipe'); 
    return this.recipes[index];
  }

  addIngredientsToShoppingList(ingredients: Ingredient[]) 
  {
     
    this.slService.addIngredients(ingredients);
    console.log('addIngredientsToShoppingList');
    console.log(this.recipes.slice(0,this.recipes.length));
  }

  addRecipe(recipe: Recipe) 
  {    
    this.recipes.push(recipe);
    this.recipesChanged.next(this.recipes.slice(0,this.recipes.length));
    console.log('addRecipe');
    console.log(this.recipes.slice(0,this.recipes.length));
  }

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

  deleteRecipe(index: number) 
  {
     
    this.recipes.splice(index, 1);       
    this.recipesChanged.next(this.recipes.slice(0,this.recipes.length));
    console.log('deleteRecipe');
    console.log(this.recipes.slice(0,this.recipes.length));
  }
}

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);
        // this.http.put('https://....../recipes.json', recipes)
        // .subscribe(response => console.log(response));
    }

    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);
          })
        )
    }
}

问题解答

针对问题1

问题出在RecipeService的注入方式上:RecipeService使用的是@Injectable()(未指定providedIn),而DataStorageService是providedIn: 'root'。如果RecipeService在多个模块中声明提供,会导致创建多个实例,DataStorageService注入的实例和你操作数据的实例不是同一个,因此获取不到更新后的数据。

解决办法:
修改RecipeService的注入配置为providedIn: 'root',确保整个应用只有一个实例:

@Injectable({providedIn: 'root'})
export class RecipeService {
  // ... 原有代码不变
}

这样就能保证DataStorageService和组件中注入的是同一个RecipeService实例,调用getRecipes()就能拿到更新后的数据。

针对问题2

Array.slice()方法不传参数时,默认等同于slice(0),会复制整个数组,和slice(0, this.recipes.length)功能完全一致。你遇到的差异大概率是其他因素导致的:

  1. 组件未订阅recipesChanged Subject:如果组件仅通过getRecipes()获取一次数据,未监听recipesChanged的变化,新增/删除操作后组件不会主动刷新数据。确保组件中订阅了数据变化:
// 在组件中
recipes: Recipe[];
subscription: Subscription;

constructor(private recipeService: RecipeService) {}

ngOnInit() {
  this.recipes = this.recipeService.getRecipes();
  this.subscription = this.recipeService.recipesChanged.subscribe(
    (recipes: Recipe[]) => {
      this.recipes = recipes;
    }
  );
}

ngOnDestroy() {
  this.subscription.unsubscribe();
}
  1. 控制台打印的引用问题:浏览器控制台有时会引用原数组,导致看起来数据没更新,但实际已经变化。可以用JSON.stringify()打印确认:
console.log('getRecipes', JSON.stringify(this.recipes.slice()));

结论:this.recipes.slice()是正确写法,和slice(0, length)无功能差异,你的问题应该是组件未正确订阅数据变化导致的。


内容的提问来源于stack exchange,提问作者DipayanRay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 18:07:11