Ok, let me start by saying that Angular is relatively new to me. However with the help of the Angular Hero tutorials I did manage to create a website which can do the following:
Show cooking recipes. The recipes are feed from a Recipe service. When clicked on a recipe, it shows the details of that particular recipe. It is all based on this tutorial: https://angular.io/tutorial/toh-pt4
But I am missing one functionality: showing three random recipes on the homepage. I assume that I don't have to loop through all recipes again. I would rather make 3 random numbers (randomNumber1 etc..) and then display them like this:
<div class="dish-block">
<a routerLink="/detail/{{ recipe[randomNumber1].id }}">
<img src="assets/img/{{ recipe[randomNumber1].image }}"/>
<div class="dish-block-caption">
<p>{{ recipe[randomNumber1].name }}</p>
</div>
</a>
</div>
However, this does not seem to work. It gives me: TypeError: Cannot read property '1' of undefined
So the question remains: How can I display a random item from an array in a service?
mock-recipes.ts
import { Recipe } from './recipe';
export const RECIPES: Recipe[] = [
{
id: 2,
name: 'Dish 1',
time: '10min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 2,
name: 'Dish 2',
time: '20min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 3,
name: 'Dish 3',
time: '30min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 4,
name: 'Dish 4',
time: '40min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 5,
name: 'Dish 5',
time: '50min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 6,
name: 'Dish 6',
time: '60min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 7,
name: 'Dish 7',
time: '70min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 8,
name: 'Dish 8',
time: '80min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
},
{
id: 9,
name: 'Dish 9',
time: '90min',
image: 'dish.jpg',
persons: 2,
steps: [
'eerste stap',
'tweede stap',
'derde stap'
],
ingredients: [
'vier',
'vijf',
'zes'
]
}
];
recipe.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Recipe } from './recipe';
import { RECIPES } from './mock-recipes';
import { MessageService } from './message.service';
@Injectable({ providedIn: 'root' })
export class RecipeService {
constructor(private messageService: MessageService) { }
getRecipes(): Observable<Recipe[]> {
this.messageService.add('RecipeService: fetched recipes');
return of(RECIPES);
}
getRecipe(id: number): Observable<Recipe> {
this.messageService.add(`RecipeService: fetched recipe id=${id}`);
return of(RECIPES.find(recipe => recipe.id === id));
}
}
suggestion.component.ts (this is the ts file of the html that should display 3 random recipes)
import { Component, OnInit } from '@angular/core';
import { Recipe } from '../recipe';
import { RecipeService } from '../recipe.service';
@Component({
selector: 'app-suggestion',
templateUrl: './suggestion.component.html',
styleUrls: ['./suggestion.component.scss']
})
export class SuggestionComponent implements OnInit {
recipes: Recipe[];
randomNumber: number;
constructor(private recipeService: RecipeService) { }
ngOnInit() {
this.getRecipes();
this.randomNumber = Math.floor(Math.random() * 9) + 1;
}
getRecipes(): void {
this.recipeService.getRecipes()
.subscribe(recipes => this.recipes = recipes);
}
}
I am really stuck at this moment and would love to receive some advice. Thanks in advance!
Update I have managed to create a JS function which generates an array with 3 random values. However, I can't find a way to connect my recipe.service to that JS file. Should I change something in the suggestion.component.ts in order to let it communicate with the mock.recipes?
Update2 It now works with the help of the community, thanks!
My TS file now looks like this:
suggestion.component.ts
import { Component, OnInit } from '@angular/core';
import { Recipe } from '../recipe';
import { RecipeService } from '../recipe.service';
declare function getRandom(): any;
@Component({
selector: 'app-suggestion',
templateUrl: './suggestion.component.html',
styleUrls: ['./suggestion.component.scss']
})
export class SuggestionComponent implements OnInit {
recipes: Recipe[];
threeRandomRecipes: any;
randomId: number;
constructor(private recipeService: RecipeService) { }
ngOnInit() {
this.getRecipes();
this.getThreeRandomRecipes();
}
getThreeRandomIds() {
const randomIds = [];
while (1) {
this.randomId = Math.floor(Math.random() * 10);
if (randomIds.includes(this.randomId)) {
continue;
} else {
randomIds.push(this.randomId);
if (randomIds.length === 3) {
break;
}
}
}
return randomIds;
}
getRecipes(): void {
this.recipeService.getRecipes()
.subscribe(recipes => this.recipes = recipes);
}
getRecipe(id) {
return this.recipes.filter(recipe => recipe.id === id)[0];
}
getThreeRandomRecipes() {
const randomIds = this.getThreeRandomIds();
this.threeRandomRecipes = randomIds.map(id => this.getRecipe(id));
}
}
After that I had to add some interpolation like this:
<div class="dish">
<div class="row">
<div class="col-12 col-md-4" *ngFor="let suggestion of threeRandomRecipes">
<div class="dish-block">
<a routerLink="/detail/{{ suggestion.id }}">
<img src="assets/img/{{ suggestion.image }}"/>
<div class="dish-block-timer">
<p>{{ suggestion.time }}</p>
</div>
<div class="dish-block-caption">
<p>{{ suggestion.name }}</p>
</div>
</a>
</div>
</div>
</div>
</div>