Search code examples
angularangular17

Issue Changing Resolve class to ResolveFn for Angular course


I am following along with the Angular course, but the video uses the resolve class which is now deprecated and not available in Angular 17. I have reviewed a few different articles, but I am not able to get it to return the data as the course is expecting.

Below is the code from the class

enter image description here

I tried following along with other articles and changing it from a class to a const, but I'm having trouble understanding how to return the call on the fetch recipes method.

Below is the modified code:

import { Recipe } from "./recipe.model";
import { DataStorageService } from "../shared/data-storage.service";


@Injectable({providedIn: 'root'})

export const RecipesResolverService: ResolveFn<Recipe[]> = 

    (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { 
        const dataStorageService = inject(DataStorageService);
        
        return this.dataStorageService.fetchRecipes();
    }

Below is the data-storage.service.ts that the above was referred to:

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { map, tap } from "rxjs/operators";

import { Recipe } from "../recipes/recipe.model";
import { RecipeService } from "../recipes/recipe.service";

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

    storeRecipes() {
        const recipes = this.recipesService.getRecipes();
        this.http.put(
            'https://ng-course-recipe-book-af2f9-default-rtdb.firebaseio.com/recipes.json', 
            recipes
        )
        .subscribe(response => { 
            console.log(response);
        });
    }

    fetchRecipes() {
        return this.http
            .get<Recipe[]>(
                'https://ng-course-recipe-book-af2f9-default-rtdb.firebaseio.com/recipes.json'
            )
            .pipe(
                map(recipes => {
                    return recipes.map(recipe => {
                        return {
                            ...recipe, 
                            ingredients: recipe.ingredients ? recipe.ingredients : []
                        };
                    });
                }),
                tap(recipes => {
                    this.recipesService.setRecipes(recipes);
                })
            )
    }
}

And below is the app-routing.module.ts file, with resolve used in the RecipeDetail and Edit components:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RecipesComponent } from './recipes/recipes.component';
import { ShoppingListComponent } from './shopping-list/shopping-list.component';
import { RecipeStartComponent } from './recipes/recipe-start/recipe-start.component';
import { RecipeDetailComponent } from './recipes/recipe-detail/recipe-detail.component';
import { RecipeEditComponent } from './recipes/recipe-edit/recipe-edit.component';
import { RecipesResolverService } from './recipes/recipes-resolver.service';

const appRoutes: Routes = [
    {path: '', redirectTo: '/recipes', pathMatch: 'full'},
    { path: 'recipes', component: RecipesComponent, children: [
        { path: '', component: RecipeStartComponent},
        { path: 'new', component: RecipeEditComponent},
        { path: ':id', component: RecipeDetailComponent, resolve: [RecipesResolverService]},
        { path: ':id/edit', component: RecipeEditComponent, resolve: [RecipesResolverService]}
    ]},
    { path: 'shopping-list', component: ShoppingListComponent},
];

@NgModule({
    imports: [RouterModule.forRoot(appRoutes)],
    exports: [RouterModule]
})

export class AppRoutingModule {

}

Please let me know what I am doing wrong. all of the articles I have looked at seem to take different approaches. Below are a link to the few I looked at:

https://medium.com/codex/functional-resolvers-in-angular-43951c50c4e https://blog.bitsrc.io/how-ive-replaced-deprecated-resolvers-in-angular-16-59811f79cd5b https://angularjobs.com/curated/how-ive-replaced-deprecated-resolvers-in-angular-16-59811f79cd5b-on-blog-bitsrc-io


Solution

  • Below are the list of changes to be done.

    When working with resolverFn you do not need @Injectable(...) since it's only meant for a service and not a function:

    import { Recipe } from "./recipe.model";
    import { DataStorageService } from "../shared/data-storage.service";
    
    export const RecipesResolverService: ResolveFn<Recipe[]> = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { 
        const dataStorageService = inject(DataStorageService);
        
        return this.dataStorageService.fetchRecipes();
    }
    

    Then you can try importing the resolver, as a property of an object instead:

    ...
    { path: ':id', component: RecipeDetailComponent, resolve: { data: RecipesResolverFn } },
    { path: ':id/edit', component: RecipeEditComponent, resolve: { data: RecipesResolverFn } }
    ...
    

    Then we can subscribe to get the data:

    ...
    constructor(private activatedRoute: ActivatedRoute) { }
    
    ngOnInit() {
      this.activatedRoute.data.pipe(map(data => data['data'])).subscribe((response: any) => {
         // use the data
    });
    ...