Skip to content

Commit

Permalink
Course Project: Forms using reactive approach
Browse files Browse the repository at this point in the history
  • Loading branch information
Renante D. Entera committed Aug 26, 2020
1 parent 01897c7 commit e7b02b3
Show file tree
Hide file tree
Showing 19 changed files with 339 additions and 47 deletions.
5 changes: 5 additions & 0 deletions course-project/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions course-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@angular/router": "~10.0.9",
"bootstrap": "^3.4.1",
"rxjs": "~6.5.5",
"rxjs-compat": "^6.6.2",
"tslib": "^2.0.0",
"zone.js": "~0.10.3"
},
Expand Down
2 changes: 1 addition & 1 deletion course-project/src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<app-header></app-header>
<app-header (featureSelected)="onNavigate($event)"></app-header>
<div class="container">
<div class="row">
<div class="col-md-12">
Expand Down
5 changes: 5 additions & 0 deletions course-project/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ import { Component } from '@angular/core';
styleUrls: ['./app.component.css']
})
export class AppComponent {
loadedFeature = 'recipe';

onNavigate(feature: string) {
this.loadedFeature = feature;
}
}
15 changes: 11 additions & 4 deletions course-project/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';


import { AppComponent } from './app.component';
import { HeaderComponent } from './header/header.component';
Expand All @@ -15,6 +16,7 @@ import { ShoppingListService } from './shopping-list/shopping-list.service';
import { AppRoutingModule } from './app-routing.module';
import { RecipeStartComponent } from './recipes/recipe-start/recipe-start.component';
import { RecipeEditComponent } from './recipes/recipe-edit/recipe-edit.component';
import { RecipeService } from './recipes/recipe.service';

@NgModule({
declarations: [
Expand All @@ -30,8 +32,13 @@ import { RecipeEditComponent } from './recipes/recipe-edit/recipe-edit.component
RecipeStartComponent,
RecipeEditComponent
],
imports: [BrowserModule, FormsModule, AppRoutingModule],
providers: [ShoppingListService],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
AppRoutingModule
],
providers: [ShoppingListService, RecipeService],
bootstrap: [AppComponent]
})
export class AppModule {}
export class AppModule { }
2 changes: 1 addition & 1 deletion course-project/src/app/header/header.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<a href="#" class="navbar-brand">Recipe Book</a>
<a routerLink="/" class="navbar-brand">Recipe Book</a>
</div>

<div class="collapse navbar-collapse">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h1>{{ recipe.name }}</h1>
<ul class="dropdown-menu">
<li><a (click)="onAddToShoppingList()" style="cursor: pointer;">To Shopping List</a></li>
<li><a style="cursor: pointer;" (click)="onEditRecipe()">Edit Recipe</a></li>
<li><a style="cursor: pointer;">Delete Recipe</a></li>
<li><a style="cursor: pointer;" (click)="onDeleteRecipe()">Delete Recipe</a></li>
</ul>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ export class RecipeDetailComponent implements OnInit {
// this.router.navigate(['../', this.id, 'edit'], {relativeTo: this.route});
}

onDeleteRecipe() {
this.recipeService.deleteRecipe(this.id);
this.router.navigate(['/recipes']);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
input.ng-invalid.ng-touched,
textarea.ng-invalid.ng-touched {
border: 1px solid red;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,95 @@
<p>
recipe-edit works!
</p>
<div class="row">
<div class="col-xs-12">
<form [formGroup]="recipeForm" (ngSubmit)="onSubmit()">
<div class="row">
<div class="col-xs-12">
<button
type="submit"
class="btn btn-success"
[disabled]="!recipeForm.valid">Save</button>
<button type="button" class="btn btn-danger" (click)="onCancel()">Cancel</button>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
formControlName="name"
class="form-control">
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="imagePath">Image URL</label>
<input
type="text"
id="imagePath"
formControlName="imagePath"
class="form-control"
#imagePath>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<img [src]="imagePath.value" class="img-responsive">
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="description">Description</label>
<textarea
type="text"
id="description"
class="form-control"
formControlName="description"
rows="6"></textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12" formArrayName="ingredients">
<div
class="row"
*ngFor="let ingredientCtrl of controls; let i = index"
[formGroupName]="i"
style="margin-top: 10px;">
<div class="col-xs-8">
<input
type="text"
class="form-control"
formControlName="name">
</div>
<div class="col-xs-2">
<input
type="number"
class="form-control"
formControlName="amount">
</div>
<div class="col-xs-2">
<button
type="button"
class="btn btn-danger"
(click)="onDeleteIngredient(i)">X</button>
</div>
</div>
<hr>
<div class="row">
<div class="col-xs-12">
<button
type="button"
class="btn btn-success"
(click)="onAddIngredient()">Add Ingredient</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { FormGroup, FormControl, FormArray, Validators } from '@angular/forms';

import { RecipeService } from '../recipe.service';

@Component({
selector: 'app-recipe-edit',
Expand All @@ -9,17 +12,94 @@ import { ActivatedRoute, Params } from '@angular/router';
export class RecipeEditComponent implements OnInit {
id: number;
editMode = false;
recipeForm: FormGroup;

constructor(private route: ActivatedRoute) { }
constructor(private route: ActivatedRoute,
private recipeService: RecipeService,
private router: Router) {
}

ngOnInit() {
this.route.params
.subscribe(
(params: Params) => {
this.id = +params['id'];
this.editMode = params['id'] != null;
this.initForm();
}
);
}

onSubmit() {
// const newRecipe = new Recipe(
// this.recipeForm.value['name'],
// this.recipeForm.value['description'],
// this.recipeForm.value['imagePath'],
// this.recipeForm.value['ingredients']);
if (this.editMode) {
this.recipeService.updateRecipe(this.id, this.recipeForm.value);
} else {
this.recipeService.addRecipe(this.recipeForm.value);
}
this.onCancel();
}

onAddIngredient() {
(<FormArray>this.recipeForm.get('ingredients')).push(
new FormGroup({
'name': new FormControl(null, Validators.required),
'amount': new FormControl(null, [
Validators.required,
Validators.pattern(/^[1-9]+[0-9]*$/)
])
})
);
}

onDeleteIngredient(index: number) {
(<FormArray>this.recipeForm.get('ingredients')).removeAt(index);
}

onCancel() {
this.router.navigate(['../'], {relativeTo: this.route});
}

get controls() { // a getter!
return (<FormArray>this.recipeForm.get('ingredients')).controls;
}

private initForm() {
let recipeName = '';
let recipeImagePath = '';
let recipeDescription = '';
let recipeIngredients = new FormArray([]);

if (this.editMode) {
const recipe = this.recipeService.getRecipe(this.id);
recipeName = recipe.name;
recipeImagePath = recipe.imagePath;
recipeDescription = recipe.description;
if (recipe['ingredients']) {
for (let ingredient of recipe.ingredients) {
recipeIngredients.push(
new FormGroup({
'name': new FormControl(ingredient.name, Validators.required),
'amount': new FormControl(ingredient.amount, [
Validators.required,
Validators.pattern(/^[1-9]+[0-9]*$/)
])
})
);
}
}
}

this.recipeForm = new FormGroup({
'name': new FormControl(recipeName, Validators.required),
'imagePath': new FormControl(recipeImagePath, Validators.required),
'description': new FormControl(recipeDescription, Validators.required),
'ingredients': recipeIngredients
});
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';

import { Recipe } from '../recipe.model';
import { RecipeService } from '../recipe.service';
Expand All @@ -9,19 +10,30 @@ import { RecipeService } from '../recipe.service';
templateUrl: './recipe-list.component.html',
styleUrls: ['./recipe-list.component.css']
})
export class RecipeListComponent implements OnInit {
export class RecipeListComponent implements OnInit, OnDestroy {
recipes: Recipe[];
subscription: Subscription;

constructor(private recipeService: RecipeService,
private router: Router,
private route: ActivatedRoute) {
}

ngOnInit() {
this.subscription = this.recipeService.recipesChanged
.subscribe(
(recipes: Recipe[]) => {
this.recipes = recipes;
}
);
this.recipes = this.recipeService.getRecipes();
}

onNewRecipe() {
this.router.navigate(['new'], {relativeTo: this.route});
}

ngOnDestroy() {
this.subscription.unsubscribe();
}
}
17 changes: 17 additions & 0 deletions course-project/src/app/recipes/recipe.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';

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[] = [
new Recipe(
Expand Down Expand Up @@ -38,4 +40,19 @@ export class RecipeService {
addIngredientsToShoppingList(ingredients: Ingredient[]) {
this.slService.addIngredients(ingredients);
}

addRecipe(recipe: Recipe) {
this.recipes.push(recipe);
this.recipesChanged.next(this.recipes.slice());
}

updateRecipe(index: number, newRecipe: Recipe) {
this.recipes[index] = newRecipe;
this.recipesChanged.next(this.recipes.slice());
}

deleteRecipe(index: number) {
this.recipes.splice(index, 1);
this.recipesChanged.next(this.recipes.slice());
}
}
Loading

0 comments on commit e7b02b3

Please sign in to comment.