Melanjutkan modul sebelumnya, sekarang kita akan menambahkan route untuk mengakses halaman recipe detail berdasarkan id.
Buka file app-routing.module.ts, tambahkan child route berikut
import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { RecipeDetailComponent } from "./recipes/recipe-detail/recipe-detail.component"; import { RecipeHomeComponent } from "./recipes/recipe-home/recipe-home.component"; import { RecipesComponent } from "./recipes/recipes.component"; import { ShoppingListComponent } from "./shopping-list/shopping-list.component"; const appRoutes: Routes = [ {path: '', redirectTo:'/recipes', pathMatch: 'full'}, {path: 'recipes', component: RecipesComponent, children:[ {path: '', component: RecipeHomeComponent}, {path: ':id', component: RecipeDetailComponent} //child route untuk recipedetail by id ]}, {path: 'shopping-list', component: ShoppingListComponent} ]; @NgModule({ imports: [RouterModule.forRoot(appRoutes)], exports: [RouterModule] }) export class AppRoutingModule{ }
Kemudian buka file recipe-list.component.html dan recipe-list.component.ts, buang code event binding click, karena sudah tidak digunakan.
Pada recipe-list.component.html, ditambahkan property binding, untuk mendapatkan index dari recipe item.
<div class="row"> <div class="col-xs-12"> <button class="btn btn-success">New Recipe</button> </div> </div> <hr> <div class="row"> <!-- tidak digunakan click binding, diganti routing --> <!-- <div class="col-xs-12"> <app-recipe-item *ngFor="let recipeLoop of recipes" [recipe]="recipeLoop" (click)="onSelect(recipeLoop)" > </app-recipe-item> </div> --> <div class="col-xs-12"> <!-- ganti click binding menjadi property binding untuk mendapatkan index recipe item --> <app-recipe-item *ngFor="let recipeLoop of recipes; let i = index;" [recipe]="recipeLoop" [index] ="i"> </app-recipe-item> </div> </div>
import { Component, OnInit } from '@angular/core'; import { Recipe } from '../recipe.model'; import { RecipeService } from '../recipe.service'; // RecipeService @Component({ selector: 'app-recipe-list', templateUrl: './recipe-list.component.html', styleUrls: ['./recipe-list.component.css'] }) export class RecipeListComponent implements OnInit { recipes!: Recipe[]; constructor(private recipeService: RecipeService) { } ngOnInit(): void { this.recipes = this.recipeService.getRecipes(); } //tidak digunakan, diganti menggunakan router // onSelect(recipe: Recipe){ // this.recipeService.recipeSelected.emit(recipe); // } }
Buka file recipe-detail.component.ts, kita ubah
- Membuang import library yang tidak diperlukan.
- Inisialisasi variable recipe tidak menggunakan decorator input.
- Menambahkan inisialisasi variable id.
- Pada constructor tambahkan akses terhadap instance active route.
- Pada ngOnInit tambahkan kode untuk subscribe perubahan parameter routing.
//hapus input module karena sudah tidak digunakan import { Component, OnInit, Input } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { Recipe } from '../recipe.model'; import { RecipeService } from '../recipe.service'; @Component({ selector: 'app-recipe-detail', templateUrl: './recipe-detail.component.html', styleUrls: ['./recipe-detail.component.css'] }) export class RecipeDetailComponent implements OnInit { //tidak digunakan input, namun digunakan route // @Input() recipe! : Recipe; recipe! : Recipe; id! : number; //tambahkan akses ke router instance constructor(private recipeServ : RecipeService, private route: ActivatedRoute) { } //subscribe untuk mendapatkan perubahan id pada routing ngOnInit(): void { this.route.params.subscribe( (params: Params) =>{ this.id = +params['id']; this.recipe = this.recipeServ.getRecipe(this.id); } ); } onAddShoppingList(){ this.recipeServ.addIngToShopList(this.recipe.ingredients); } }
Berikutnya kita buka file recipe.service.ts, tambahkan method untuk mengambil data recipe detail by id.
import { EventEmitter, Injectable } from "@angular/core"; import { Ingredient } from "../shared/ingredient.model"; import { ShoppingListService } from "../shopping-list/shopping-list.service"; import { Recipe } from "./recipe.model"; @Injectable() export class RecipeService{ recipeSelected = new EventEmitter<Recipe>(); private recipes: Recipe[] = [ new Recipe( 'Oreo Dorayaki 1', 'Doriyaki dengan bahan utama Oreo', 'https://i.ytimg.com/vi/nJGmdqb892Y/maxresdefault.jpg', [ new Ingredient('Oreo', 1), new Ingredient('Susu', 1), ]), new Recipe( 'Oreo Dorayaki 2', 'Doriyaki dengan bahan utama Oreo 2', 'https://i.ytimg.com/vi/nJGmdqb892Y/maxresdefault.jpg', [ new Ingredient('Oreo', 1), new Ingredient('Gula', 1), ]) ]; constructor (private shoppingListServ: ShoppingListService){} getRecipes(){ return this.recipes.slice(); } //tambahkan method untuk mengambil recipe by id getRecipe(idx : number){ return this.recipes[idx]; } addIngToShopList(ingredients : Ingredient[]){ this.shoppingListServ.addIngredients(ingredients); } }
Sampai disini jika Anda memasukan link langsung, recipe detail akan ditampilkan. Misalnya http://localhost:4200/recipes/0, recipe index ke 0 akan ditampilkan.
Menggunakan Dynamic Parameter Link
Langkah berikutnya adalah menambahkan parameter id tersebut kedalam link. Link menu recipe ada didalam file recipe-item.component.html.
Buka file recipe-item.component.html, tambahkan binding directive routerLink dengan index. (index didapatkan dari parent component, yaitu recipe-list.component.html).
<!-- <tambahkan routerLink --> <a class="list-group-item clearfix" style="cursor: pointer;" [routerLink]="[index]" > <div class="float-start"> <h4 class="list-group-item-heading">{{recipe.name}}</h4> <p class="list-group-item-text">{{recipe.description}}</p> </div> <span class="float-end"> <img src="{{recipe.imgPath}}" alt="" class="img-fluid" style="max-height: 50px;"> </span> </a>
Terakhir, tambahkan property index yang akan digunakan oleh routerLink. Buka file recipe-item.component.ts
import { Component, OnInit, Input} from '@angular/core'; import { Recipe } from '../../recipe.model'; @Component({ selector: 'app-recipe-item', templateUrl: './recipe-item.component.html', styleUrls: ['./recipe-item.component.css'] }) export class RecipeItemComponent implements OnInit { @Input() recipe!: Recipe; @Input() index! : number; constructor() {} ngOnInit(): void {} }
Styling Aktif Menu Recipe
Tahap ini optional, lebih ke mempercantik tampilan.
Bootstrap memiliki class active untuk menunjukan menu recipe yang sedang aktif.
Buka file recipe-item.component.html, tambahkan directive routerLinkActive=”Active”.
<!-- <tambahkan directive routerLink dan routerLinkActive--> <a class="list-group-item clearfix" style="cursor: pointer;" [routerLink]="[index]" routerLinkActive="active"> <div class="float-start"> <h4 class="list-group-item-heading">{{recipe.name}}</h4> <p class="list-group-item-text">{{recipe.description}}</p> </div> <span class="float-end"> <img src="{{recipe.imgPath}}" alt="" class="img-fluid" style="max-height: 50px;"> </span> </a>
Sesuai ekspektasi, routing sudah berfungsi dengan baik, dan recipe yang sedang dipilih akan ditunjukan highlight warna biru.
Sampai disini kita sudah berhasil menambahkan routing untuk menampilkan halaman recipe detail.
Pada modul selanjutnya kita akan menambahkan route untuk membuat recipe baru.