##Project Setup Steps
ionic start recipe-book
create new project$ cd ecipe-book
change the dir to the new projct foldercode .
open the project in VS code.
- Create a git repostory in github
echo "# recipe-book" >> README.md
create a read me file for the projectgit add README.md
,git commit -m "Add README.md"
commit file to the gitgit remote add origin https://github.com/{{your-git-account>}}/recipe-book.git
Add remote repogit push -u origin master
push to remote branch
###Main functionality of the App
- Display List of Recipes
- Create New Recipe
- Receipe Info
- Add/Remove Ingrediants
- Add ingridiants to shopping list
- Shopping List
- Add Items to Shopping list
- Tabs panel to toggle between Recipes and Shopping List
ShoppingListPage RecipesPage RecipePage RecipeFormPage TabsPage
##Page Navigation TabsPage -> RecipesPage , ShoppingListPage RecipesPage -> New Recipe->(Manage Ingrediant)->(Add Ingrediant)->Add Ingrediant[Alert Input] RecipesPage -> RecipePage->(Add Ingrediants to Shopping List)->ShoppingListPage
-
Genertae Pages (
$ ionic g page <pagename>
)- ionic g page tabs
- ionic g page recipes
- ionic g page recipe
- ionic g page recipe-form
- ionic g page shopping-list
-
Implement navigation
- Udpate the tabs.html file add the tabs
<ion-tabs> <ion-tab [root]="shopingListPage" tabIcon="list-box" tabTitle="Shopping List"></ion-tab> <ion-tab [root]="recipeBook" tabIcon="book" tabTitle="Recipes"></ion-tab> </ion-tabs>
- Update the tabs.ts file with the tabPage pages variables
shopingListPage = ShoppingListPage; recipeBook = RecipesPage;
- Udpate the tabs.html file add the tabs
-
Implement the Shopping List input form
- Add Shpping Item
<form #f="ngForm" (ngSubmit)="onAddItem(f)"> <!-- Get a reference to ngForm via template variable and pass it to the onAddItem method --> <ion-list> <ion-item> <ion-label fixed>Name</ion-label> <ion-input type="text" name="ingredientName" placeholder="Milk" required></ion-input> </ion-item> <ion-item> <ion-label fixed>Amount</ion-label> <ion-input type="number" name="amount" placeholder="2" required></ion-input> </ion-item> </ion-list> <button ion-button type="submit" block>Add Item</button> </form>
- Add validation and Disable the Add Item Button
<ion-input ngModel type="text" name="ingredientName" placeholder="Milk" required></ion-input> <ion-input ngModel type="number" name="amount" placeholder="2" required></ion-input> <button ion-button type="submit" block [disabled]="!f.valid">Add Item</button>
- Add Shpping Item
-
Implement the ShoppingList Service
- Cretae Ingrediant Model class
- Implement Shopping List Service Methods
addItem(name:string, amount:numner)
addItems(ingrediants:Ingrediant[])
getItems(): Ingrediant[]
removeItem(index:number)
@Injectable() export class ShppingListService { private shoppingList :Ingrediant[] =[]; addItem(name:string,amount:number){ this.shoppingList.push(new Ingrediant(name,amount)); } addItems(items:Ingrediant[]){ this.shoppingList.push(...items); } getItems(): Ingrediant[] { return this.shoppingList.slice(); } removeItem(index: number){ this.shoppingList.splice(index,1); } }
-
Saving Items with ShopingList Service
- Update the ShoppingList Compoent to call the service to add new Item
- Reset the form after the Item is added.
- Display the newly added items on the below the form
-
Implement Delete Item from List
- Implement slide to remove item
<ion-item-sliding *ngFor="let item of shoppingList; let i = index"> <ion-item> <h2>{{item.name}}</h2> <ion-note item-end>{{item.amount}}</ion-note> </ion-item> <ion-item-options side="right"> <button ion-button color="danger" (tap)="onRemoveItem(i)"> <ion-icon name="trash"></ion-icon> Delete </button> </ion-item-options> </ion-item-sliding>
- Implement slide to remove item
-
Implement Create New Recipe Page
- Update the Recipe component's heard tpo add + button
- Implement the onNewRecipe method to go to the Receipe Form Page
- Implement Receipe Form Component with Reactive Approch
- HTML implementation
<form [formGroup]="recipeForm" (ngSubmit)="onSubmit()"> <ion-list> <ion-item> <ion-label floating>Title</ion-label> <ion-input type="text" formControlName="title"></ion-input> </ion-item> <ion-item> <ion-label floating>Description</ion-label> <ion-textarea formControlName="description"></ion-textarea> </ion-item> <ion-item> <ion-label floating>Difficulty</ion-label> <ion-select formControlName="difficulty" > <ion-option *ngFor="let option of selectDifficultyOptions" [value]="option">{{ option }}</ion-option> </ion-select> </ion-item> </ion-list> <button type="submit" block ion-button [disabled]="recipeForm.invalid">{{mode}}</button> </form>
- Initialize the form via FormGrop and Form Controll classes
private initializeForm(){ this.recipeForm = new FormGroup({ title : new FormControl(null,Validators.required), description : new FormControl(null,Validators.required), deficulty: new FormControl('Medium',Validators.required) }); }
- HTML implementation
-
Implment Add Ingreediants to the Recipe Form
- Add manager ingredient Button to Open the Action Sheet.
<button type="button" clear ion-button block clear (click)="onManageIngredients()">Manage Ingredients</button>
- Show Action sheet on Manage Ingredegent Button clicked
onManageIngredients(){ const actionSheet = this.actionSheetCtr.create({ title:'What do you want to do?', buttons: [ { text: 'Add Ingredient', handler: () => { this.createNewIngredientAlert().present(); } }, { text: 'Remove all Ingredients', handler: () => { } }, { text:'Cancel', role:'cancel' } ] }); actionSheet.present(); }
- Show Aler button to on Add Ingredient Button Selected
private createNewIngredientAlert(){ const newIngredientAlert = this.alertCtrl.create({ title: 'Add Ingredient', inputs: [ { name: 'name', placeholder: "Name" }, ], buttons: [ { text: 'Cancel', role:'cancel' }, { text: 'Add', handler: data => { if(data.name.trim()=='' || data.name == null){ return; } (<FormArray>this.recipeForm.get('ingredients')). push(new FormControl(data.name,Validators.required)); } } ] }); return newIngredientAlert; }
- Implement Removing Ingredients Controls
{ text: 'Remove all Ingredients', handler: () => { const fArray: FormArray = <FormArray>this.recipeForm.get('ingredients'); const len = fArray.length; if(len>0){ for(let i = len-1;i>=0; i--){ fArray.removeAt(i); } } } },
- Show toast when user did not enter valid value
if(data.name.trim()=='' || data.name == null){ this.toastCtrl.create({ message: 'Please Enter Valid Value', duration: 1000, position: 'bottom' }).present(); return; }
- Add manager ingredient Button to Open the Action Sheet.
-
Implement Recipe Model and Service
- Recipe Model
import { Ingrediant } from './ingrediant.model'; export class Recipe{ constructor( public title: string, public description: string, public difficulty: string, public ingredients: Ingrediant[]) {} }
- Recipe Service
addRecipe
removeRecipe
getRecipes
updateRecipe
-
Save and display recipes
- Update the Recipe form onSave method to use the RecipeService to store data
- Update the RecipeComponent to dipsplay recipe list
-
Implement the Edit Page
- Push data via Nav Controller
onEditRecipe(){ this.navCtrl.push(RecipeFormPage, { mode:'Edit', recipe:this.recipe, index:this.index } ); }
- Update the initializeForm method to handle the edit mode
private initializeForm(){ let title = null; let description = null; let difficulty = 'Medium'; let ingredients = []; if(this.mode =='Edit'){ title = this.recipe.title; description = this.recipe.description; difficulty = this.recipe.difficulty; for( let ingredient of this.recipe.ingredients ){ console.log(ingredient); ingredients.push(new FormControl(ingredient.name,Validators.required)); } }
- Update the init method to suppror both edit and new mode
ngOnInit(){ this.mode = this.navParams.get('mode'); if(this.mode=='Edit'){ this.recipe = this.navParams.get('recipe'); this.index = this.navParams.get('index'); } this.initializeForm(); }
- Update onSubmit method to call the server correct service method
- Push data via Nav Controller
-
Implement send ingrediants to shopping list and delete recipe
#Advance Features
- User Signup Authentication
- Save/Load Data from/to Firebase
- Resticted acces to pages
SigninPage SignupPage Menu
- Generate the Signup and SignIn pages
- Implement the Side Menu
- Implement the nav bar menu button
- Implement the Signup form
- Implement the Signin form
- Setup firebase app in firebase console
- Enable username password signin methodsf from authentication menue
- Install fire base package
npm install --save firebase
- Createa new firebase stting file "firebase-config" to store the firebase api key
- Update app.component class to init the firebase
firebase.initializeApp(firebaseSettings);
- Add new Service AuthService and Link the Signup and Signin Forms
- signup()
- signin()
- loguot()
- Update the SignupPage and SinginPage to use AuthService
- Update the app component to add implement the authchange event
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, private menuCtrl:MenuController, ..... firebase.auth().onAuthStateChanged( user => { if(user){ this.isAuthenticated = true; this.rootPage = TabsPage; }else{ this.isAuthenticated = false; this.rootPage = SigninPage; } }) }
- Implement the popver page to display save and load data to firebase and link to the shopping list page meue bar
- Update the AuthService to get Active User
getActiveUser() : firebase.User { return firebase.auth().currentUser; }
- Save/Load Shooping list to/from Firebase
- Upadate the firebase Rule
{ "rules": { ".read": "auth != null", ".write": "auth != null" } }
- Update the Shopping lsit service
storeList(token: string){ const userId= this.authService.getActiveUser().uid; return this.http.put('https://recipebook-f1cff.firebaseio.com/'+ userId +'/shopping-list.json?auth=' + token, this.shoppingList); } fetchList(token: string){ const userId= this.authService.getActiveUser().uid; return this.http.get('https://recipebook-f1cff.firebaseio.com/'+ userId +'/shopping-list.json?auth=' + token) .do(data=>{ this.shoppingList = data; }) }
- Save/Load Recipes to/from Firebase