Angular双服务数据异常:默认值返回及slice使用问题
问题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)功能完全一致。你遇到的差异大概率是其他因素导致的:
- 组件未订阅
recipesChangedSubject:如果组件仅通过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(); }
- 控制台打印的引用问题:浏览器控制台有时会引用原数组,导致看起来数据没更新,但实际已经变化。可以用
JSON.stringify()打印确认:
console.log('getRecipes', JSON.stringify(this.recipes.slice()));
结论:this.recipes.slice()是正确写法,和slice(0, length)无功能差异,你的问题应该是组件未正确订阅数据变化导致的。
内容的提问来源于stack exchange,提问作者DipayanRay

