~$ git clone https://github.com/Pieter-hogent/recipeapp.git (or git pull) ~$ cd recipeapp ~/recipeapp$ npm install ~/recipeapp$ git checkout -b mybranch aa93285
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { RecipeComponent } from './recipe/recipe.component';
import { IngredientComponent } from './ingredient/ingredient.component';
@NgModule({
declarations: [AppComponent, RecipeComponent, IngredientComponent],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
~/recipapp$ ng generate module recipe --module=app
~/recipapp$ cd src/app ~/recipapp/src/app$ mkdir recipe/recipe ~/recipapp/src/app$ git mv recipe/recipe.component* recipe/recipe ~/recipapp/src/app$ git mv ingredient recipe/
src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { RecipeComponent } from './recipe/recipe.component';
import { IngredientComponent } from './ingredient/ingredient.component';
@NgModule({
declarations: [AppComponent, RecipeComponent, IngredientComponent],
imports: [BrowserModule, RecipeModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
src/app/recipe/recipe.module.ts
import { IngredientComponent } from './ingredient/ingredient.component';
import { RecipeComponent } from './recipe/recipe.component';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [RecipeComponent, IngredientComponent],
imports: [CommonModule],
exports: [RecipeComponent]
})
export class RecipeModule {}
b6df265
~/recipeapp$ npm install --save @angular/material @angular/cdk @angular/animations @angular/flex-layout
src/app/material/material.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { MatCardModule } from '@angular/material/card';
import { FlexLayoutModule } from '@angular/flex-layout';
@NgModule({
declarations: [],
imports: [
CommonModule,
BrowserAnimationsModule,
FlexLayoutModule,
MatListModule,
MatCardModule,
MatIconModule
],
exports: [
BrowserAnimationsModule,
FlexLayoutModule,
MatListModule,
MatCardModule,
MatIconModule
]
})
export class MaterialModule {}
src/app/recipe/recipe.module.ts
import { MaterialModule } from './../material/material.module';
import { IngredientComponent } from './ingredient/ingredient.component';
import { RecipeComponent } from './recipe/recipe.component';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [RecipeComponent, IngredientComponent],
imports: [CommonModule, MaterialModule],
exports: [RecipeComponent]
})
export class RecipeModule {}
5005567
src/styles.css
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
src/index.html
<head>
<!-- [...] -->
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
</head>
src/app/recipe/recipe/recipe.component.html
<mat-card>
<mat-card-header>
<mat-card-title><mat-icon>local_dining</mat-icon> <p>recipe for {{ name }} </mat-card-title>
<mat-card-subtitle>added on {{ dateAdded | date: longDate }}</mat-card-subtitle> </p>
</mat-card-header>
<mat-card-content>
<h3 mat-subheader>ingredients</h3>
<mat-listul>
<mat-list-itemli *ngFor="let ingredient of ingredients">
<app-ingredient [name]="ingredient"></app-ingredient>
</mat-list-itemli>
</mat-listul>
</mat-card-content>
</mat-card>
6778d63
src/app/recipe/recipe.model.ts
interface RecipeJson {
name: string;
ingredients: string[];
dateAdded: string;
}
export class Recipe {
constructor(
private _name: string,
private _ingredients = new Array<string>(),
private _dateAdded = new Date()
) {}
static fromJSON(json: RecipeJson): Recipe {
const rec = new Recipe(json.name, json.ingredients, new Date(json.dateAdded));
return rec;
}
// [...] other getters
get name(): string {
return this._name;
}
addIngredient(name: string, amount?: number, unit?: string) {
this._ingredients.push(`${amount || 1} ${unit || ''} ${name}`);
}
}
src/app/recipe/mock-recipes.ts
import { Recipe } from './recipe.model';
const JsonRecipes = [
{
name: 'spaghetti',
ingredients: ['tomato', 'onion', 'celery', 'carrot', 'minced meat'],
dateAdded: '2020-02-07T18:25:43.511Z'
},
{
name: 'risotto',
ingredients: ['rice', 'onion', 'parmesan', 'butter'],
dateAdded: '2020-02-08T16:25:43.511Z'
}
];
export const RECIPES: Recipe[] = JsonRecipes.map(Recipe.fromJSON);
src/app/recipe/recipe.component.ts
export class RecipeComponent implements OnInit {
name: string;
ingredients: string[];
dateAdded: Date;
@Input() public recipe: Recipe;
constructor() {
this.name = 'spaghetti';
this.ingredients = ['tomato', 'onion', 'celery', 'carrot', 'minced meat'];
this.dateAdded = new Date();
}
ngOnInit() {}
}
src/app/recipe/recipe.component.html
<mat-card>
<mat-card-header>
<mat-card-title>
<mat-icon>local_dining</mat-icon> {{ recipe.name }}</mat-card-title
>
<mat-card-subtitle
>added on {{ recipe.dateAdded | date: longDate }}</mat-card-subtitle
>
</mat-card-header>
<mat-card-content>
<h3 mat-subheader>ingredients</h3>
<mat-list>
<mat-list-item *ngFor="let ingredient of recipe.ingredients">{{
ingredient
}}</mat-list-item>
</mat-list>
</mat-card-content>
</mat-card>
~/recipeapp$ cd src/app/recipe ~/recipeapp/src/app/recipe$ ng g c recipe-list --module=recipe --dry-run ~/recipeapp/src/app/recipe$ ng g c recipe-list --module=recipe
src/app/recipe/recipe-list/recipe-list.component.ts
import { RECIPES } from './mock-recipes';
export class RecipeListComponent {
private _recipes = RECIPES;
constructor() {}
get recipes() {
return this._recipes;
}
}
src/app/recipe/recipe-list/recipe-list.component.html
<div
fxLayout="row wrap"
fxLayout.xs="column"
fxLayoutGap="0.5%"
fxLayoutAlign="start"
>
<div
class="recipe"
*ngFor="let localRecipe of recipes"
fxFlex="0 0 calc(25%-0.5%)"
fxFlex.xs="100%"
>
<app-recipe [recipe]="localRecipe"></app-recipe>
</div>
</div>
src/app/recipe/recipe.module.ts
import { MaterialModule } from './../material/material.module';
import { IngredientComponent } from './ingredient/ingredient.component';
import { RecipeComponent } from './recipe/recipe.component';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RecipeListComponent } from './recipe-list/recipe-list.component';
@NgModule({
declarations: [RecipeComponent, IngredientComponent, RecipeListComponent],
imports: [CommonModule, MaterialModule],
exports: [RecipeComponentRecipeListComponent]
})
export class RecipeModule {}
src/app/app.component.html
<div style="text-align:center">
<h1>welcome to {{ title }}</h1>
</div>
<div>
<app-recipe></app-recipe>
<app-recipe-list></app-recipe-list>
</div>
9bb3c80
src/app/recipe/add-recipe/add-recipe.component.html
<mat-card>
<mat-form-field>
<input matInput placeholder="name" type="text"
#newrecipename />
</mat-form-field>
<button (click)="addRecipe(newrecipename)" mat-raised-button>
add recipe
</button>
</mat-card>
src/app/recipe/add-recipe/add-recipe.component.ts
import { Component, OnInit, Output, EventEmitter } from '@angular/core';
import { Recipe } from '../recipe.model';
export class AddRecipeComponent implements OnInit {
@Output() public newRecipe = new EventEmitter<Recipe>();
constructor() {}
ngOnInit() {}
addRecipe(recipeName: HTMLInputElement): boolean {
const recipe = new Recipe(recipeName.value, []);
this.newRecipe.emit(recipe);
console.log(recipeName.value);
return false;
}
}
21a08c3
src/app/recipe/recipe-list/recipe-list.component.html
<div fxLayout="column" fxLayoutGap="2%">
<app-add-recipe (newRecipe)="addNewRecipe($event)"></app-add-recipe>
<div
fxLayout="row wrap"
fxLayout.xs="column"
fxLayoutGap="0.5%"
fxLayoutAlign="start"
>
<div
class="recipe"
*ngFor="let localRecipe of recipes"
fxFlex="0 0 calc(25%-0.5%)"
fxFlex.xs="100%"
>
<app-recipe [recipe]="localRecipe"></app-recipe>
</div>
</div>
</div>
src/app/recipe/recipe-list/recipe-list.component.ts
export class RecipeListComponent implements OnInit {
private _recipes = RECIPES;
constructor() {}
get recipes() {
return this._recipes;
}
addNewRecipe(recipe: Recipe) {
this._recipes.push(recipe);
}
7557a1d
~/recipeapp/src/app/recipe$ ng generate service RecipeData
CREATE src/app/recipe/recipe-data.service.spec.ts (354 bytes) CREATE src/app/recipe/recipe-data.service.ts (159 bytes)
src/app/recipe-data.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class RecipeDataService {
constructor() { }
}
src/app/recipe-data.service.ts
@Injectable({
providedIn: 'root'
})
export class RecipeDataService {
private _recipes = RECIPES;
constructor() {}
get recipes(): Recipe[] {
return this._recipes;
}
addNewRecipe(recipe: Recipe) {
this._recipes.push(recipe);
}
}
src/app/recipe/recipe-list/recipe-list.component.ts
export class RecipeListComponent{
constructor(private _recipeDataService: RecipeDataService) {}
get recipes(): Recipe[] {
return this._recipeDataService.recipes;
}
addNewRecipe(recipe) {
this._recipeDataService.addNewRecipe(recipe);
}
}
f76e8b8
~/recipeapp/src/app/recipe$ ng generate pipe RecipeFilter --module=recipe
CREATE src/app/recipe/recipe-filter.pipe.spec.ts (212 bytes) CREATE src/app/recipe/recipe-filter.pipe.ts (213 bytes) UPDATE src/app/recipe/recipe.module.ts (971 bytes)
src/app/recipe-filter.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'recipeFilter'
})
export class RecipeFilterPipe implements PipeTransform {
transform(recipes: Recipe[]value: unknown, name: string...args?: unknown): Recipe[]unknown {
return null;
if (!name || name.length === 0) {
return recipes;
}
return recipes.filter(rec =>
rec.name.toLowerCase().startsWith(name.toLowerCase())
);
}
}
src/app/recipe/recipe-list.component.html
<div fxLayout="column" fxLayoutGap="2%">
<app-add-recipe (newRecipe)="addNewRecipe($event)"></app-add-recipe>
<mat-card>
<mat-form-field>
<input matInput placeholder="filter" type="text" #filter />
</mat-form-field>
<button (click)="applyFilter(filter.value)" mat-raised-button>
filter
</button>
</mat-card>
<div
fxLayout="row wrap"
fxLayout.xs="column"
fxLayoutGap="0.5%"
fxLayoutAlign="start"
>
<div
class="recipe"
*ngFor="let localRecipe of (recipes | recipeFilter: filterRecipeName)"
fxFlex="0 0 calc(25%-0.5%)"
fxFlex.xs="100%"
>
<app-recipe [recipe]="localRecipe"></app-recipe>
</div>
</div>
</div>
src/app/recipe/recipe-list.component.ts
export class RecipeListComponent {
constructor(private _recipeDataService: RecipeDataService) {}
public filterRecipeName: string;
applyFilter(filter: string) {
this.filterRecipeName = filter;
}
get recipes(): Recipe[] {
return this._recipeDataService.recipes;
}
// [...]
e62e438
src/app/recipe/recipe-filter.pipe.ts
@Pipe({
name: 'recipeFilter',
pure: false
})
src/app/recipe/recipe-data.service.ts
addNewRecipe(recipe: Recipe) {
this._recipes = [...this._recipes, recipe];
}