| @@ -5,7 +5,6 @@ import { HeaderComponent } from "./header/header.component"; | |||||
| import { HomeComponent } from "./home/home.component"; | import { HomeComponent } from "./home/home.component"; | ||||
| import { SharedModule } from "../shared/shared.module"; | import { SharedModule } from "../shared/shared.module"; | ||||
| import { AppRoutingModule } from "../app-routing.module"; | import { AppRoutingModule } from "../app-routing.module"; | ||||
| import { ShoppingListService } from "../shopping-list/shopping-list.service"; | |||||
| import { RecipeService } from "../recipes/recipe.service"; | import { RecipeService } from "../recipes/recipe.service"; | ||||
| import { DataStorageService } from "../shared/data-storage.service"; | import { DataStorageService } from "../shared/data-storage.service"; | ||||
| import { AuthService } from "../auth/auth.service"; | import { AuthService } from "../auth/auth.service"; | ||||
| @@ -26,7 +25,6 @@ import { LoggingInterceptor } from "../shared/logging.interceptor"; | |||||
| HeaderComponent | HeaderComponent | ||||
| ], | ], | ||||
| providers: [ | providers: [ | ||||
| ShoppingListService, | |||||
| RecipeService, | RecipeService, | ||||
| DataStorageService, | DataStorageService, | ||||
| AuthService, | AuthService, | ||||
| @@ -1,8 +1,11 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import { Router, ActivatedRoute, Params } from '@angular/router'; | import { Router, ActivatedRoute, Params } from '@angular/router'; | ||||
| import { Store } from '@ngrx/store'; | |||||
| import { Recipe } from '../recipe.model'; | import { Recipe } from '../recipe.model'; | ||||
| import { RecipeService } from '../recipe.service'; | import { RecipeService } from '../recipe.service'; | ||||
| import * as ShoppingListActions from '../../shopping-list/ngrx/shopping-list.actions'; | |||||
| import * as fromShoppingList from '../../shopping-list/ngrx/shopping-list.reducers'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-recipe-detail', | selector: 'app-recipe-detail', | ||||
| @@ -15,7 +18,8 @@ export class RecipeDetailComponent implements OnInit { | |||||
| constructor(private recipeService: RecipeService, | constructor(private recipeService: RecipeService, | ||||
| private route: ActivatedRoute, | private route: ActivatedRoute, | ||||
| private router: Router) { } | |||||
| private router: Router, | |||||
| private store: Store<fromShoppingList.AppState>) { } | |||||
| ngOnInit() { | ngOnInit() { | ||||
| this.route.params.subscribe( | this.route.params.subscribe( | ||||
| @@ -27,7 +31,7 @@ export class RecipeDetailComponent implements OnInit { | |||||
| } | } | ||||
| onAddToShoppingList() { | onAddToShoppingList() { | ||||
| this.recipeService.addIngredientsToShoppingList(this.recipe.ingredients); | |||||
| this.store.dispatch(new ShoppingListActions.AddIngredients(this.recipe.ingredients)); | |||||
| } | } | ||||
| onEditRecipe() { | onEditRecipe() { | ||||
| @@ -1,17 +1,13 @@ | |||||
| import { EventEmitter } from "@angular/core"; | |||||
| import { Subject } from "rxjs/Subject"; | |||||
| import { Recipe } from "./recipe.model"; | import { Recipe } from "./recipe.model"; | ||||
| import { EventEmitter, Injectable } from "@angular/core"; | |||||
| import { Ingredient } from "../shared/ingredient.model"; | import { Ingredient } from "../shared/ingredient.model"; | ||||
| import { ShoppingListService } from "../shopping-list/shopping-list.service"; | |||||
| import { Subject } from "rxjs/Subject"; | |||||
| import { nextTick } from "q"; | |||||
| @Injectable() | |||||
| export class RecipeService { | export class RecipeService { | ||||
| recipesChanged = new Subject<Recipe[]>(); | recipesChanged = new Subject<Recipe[]>(); | ||||
| private recipes: Recipe[] = []; | private recipes: Recipe[] = []; | ||||
| constructor(private shoppingListService: ShoppingListService) { } | |||||
| replaceRecipes(recipes: Recipe[]) { | replaceRecipes(recipes: Recipe[]) { | ||||
| this.recipes = recipes; | this.recipes = recipes; | ||||
| this.recipesChanged.next(this.recipes.slice()); | this.recipesChanged.next(this.recipes.slice()); | ||||
| @@ -25,10 +21,6 @@ export class RecipeService { | |||||
| return this.recipes[index]; | return this.recipes[index]; | ||||
| } | } | ||||
| addIngredientsToShoppingList(ingredients: Ingredient[]) { | |||||
| this.shoppingListService.addIngredients(ingredients); | |||||
| } | |||||
| addRecipe(recipe: Recipe) { | addRecipe(recipe: Recipe) { | ||||
| this.recipes.push(recipe); | this.recipes.push(recipe); | ||||
| this.recipesChanged.next(this.recipes.slice()); | this.recipesChanged.next(this.recipes.slice()); | ||||
| @@ -3,6 +3,11 @@ import { Action } from '@ngrx/store'; | |||||
| import { Ingredient } from '../../shared/ingredient.model'; | import { Ingredient } from '../../shared/ingredient.model'; | ||||
| export const ADD_INGREDIENT = 'ADD_INGREDIENT'; | export const ADD_INGREDIENT = 'ADD_INGREDIENT'; | ||||
| export const ADD_INGREDIENTS = 'ADD_INGREDIENTS'; | |||||
| export const UPDATE_INGREDIENT = 'UPDATE_INGREDIENT'; | |||||
| export const DELETE_INGREDIENT = 'DELETE_INGREDIENT'; | |||||
| export const START_EDIT = 'START_EDIT'; | |||||
| export const STOP_EDIT = 'STOP_EDIT'; | |||||
| export class AddIngredient implements Action { | export class AddIngredient implements Action { | ||||
| readonly type = ADD_INGREDIENT; | readonly type = ADD_INGREDIENT; | ||||
| @@ -10,4 +15,32 @@ export class AddIngredient implements Action { | |||||
| constructor(public payload: Ingredient) {} | constructor(public payload: Ingredient) {} | ||||
| } | } | ||||
| export type ShoppingListActions = AddIngredient; | |||||
| export class AddIngredients implements Action { | |||||
| readonly type = ADD_INGREDIENTS; | |||||
| constructor(public payload: Ingredient[]) {} | |||||
| } | |||||
| export class UpdateIngredient implements Action { | |||||
| readonly type = UPDATE_INGREDIENT; | |||||
| constructor(public payload: Ingredient) {} | |||||
| } | |||||
| export class DeleteIngredient implements Action { | |||||
| readonly type = DELETE_INGREDIENT; | |||||
| } | |||||
| export class StartEdit implements Action { | |||||
| readonly type = START_EDIT; | |||||
| constructor(public payload: number) {} | |||||
| } | |||||
| export class StopEdit implements Action { | |||||
| readonly type = STOP_EDIT; | |||||
| } | |||||
| export type ShoppingListActions = AddIngredient | AddIngredients | UpdateIngredient | DeleteIngredient | StartEdit | StopEdit; | |||||
| @@ -2,12 +2,27 @@ import * as ShoppingListActions from './shopping-list.actions'; | |||||
| import { Ingredient } from '../../shared/ingredient.model'; | import { Ingredient } from '../../shared/ingredient.model'; | ||||
| export interface AppState { | |||||
| shoppingList: State | |||||
| } | |||||
| export interface State { | |||||
| ingredients: Ingredient[]; | |||||
| editedIngredient: Ingredient; | |||||
| editedIngredientIndex: number; | |||||
| } | |||||
| export const ADD_INGREDIENT = 'ADD_INGREDIENT'; | export const ADD_INGREDIENT = 'ADD_INGREDIENT'; | ||||
| export const ADD_INGREDIENTS = 'ADD_INGREDIENTS'; | |||||
| export const UPDATE_INGREDIENT = 'UPDATE_INGREDIENT'; | |||||
| export const DELETE_INGREDIENT = 'DELETE_INGREDIENT'; | |||||
| const initialState = { | |||||
| const initialState: State = { | |||||
| ingredients: [ | ingredients: [ | ||||
| new Ingredient('Banana', 10) | new Ingredient('Banana', 10) | ||||
| ] | |||||
| ], | |||||
| editedIngredient: null, | |||||
| editedIngredientIndex: -1 | |||||
| }; | }; | ||||
| export function shoppingListReducer(state = initialState, action: ShoppingListActions.ShoppingListActions) { | export function shoppingListReducer(state = initialState, action: ShoppingListActions.ShoppingListActions) { | ||||
| @@ -17,6 +32,47 @@ export function shoppingListReducer(state = initialState, action: ShoppingListAc | |||||
| ...state, | ...state, | ||||
| ingredients: [...state.ingredients, action.payload] | ingredients: [...state.ingredients, action.payload] | ||||
| }; | }; | ||||
| case ShoppingListActions.ADD_INGREDIENTS: | |||||
| return { | |||||
| ...state, | |||||
| ingredients: [...state.ingredients, ...action.payload] | |||||
| }; | |||||
| case ShoppingListActions.UPDATE_INGREDIENT: | |||||
| const ingredient = state.ingredients[state.editedIngredientIndex]; | |||||
| const updatedIngredient = { | |||||
| ...ingredient, | |||||
| ...action.payload | |||||
| } | |||||
| const ingredients = [...state.ingredients]; | |||||
| ingredients[state.editedIngredientIndex] = updatedIngredient; | |||||
| return { | |||||
| ...state, | |||||
| ingredients: ingredients, | |||||
| editedIngredient: null, | |||||
| editedIngredientIndex: -1 | |||||
| }; | |||||
| case ShoppingListActions.DELETE_INGREDIENT: | |||||
| const oldIngredients = [...state.ingredients]; | |||||
| oldIngredients.splice(state.editedIngredientIndex, 1); | |||||
| return { | |||||
| ...state, | |||||
| ingredients: oldIngredients, | |||||
| editedIngredient: null, | |||||
| editedIngredientIndex: -1 | |||||
| }; | |||||
| case ShoppingListActions.START_EDIT: | |||||
| const editedIngredient = {...state.ingredients[action.payload]}; | |||||
| return { | |||||
| ...state, | |||||
| editedIngredient: editedIngredient, | |||||
| editedIngredientIndex: action.payload | |||||
| } | |||||
| case ShoppingListActions.STOP_EDIT: | |||||
| return { | |||||
| ...state, | |||||
| editedIngredient: null, | |||||
| editedIngredientIndex: -1 | |||||
| } | |||||
| default: | default: | ||||
| return state; | return state; | ||||
| } | } | ||||
| @@ -4,8 +4,8 @@ import { Subscription } from 'rxjs/Subscription'; | |||||
| import { Store } from '@ngrx/store'; | import { Store } from '@ngrx/store'; | ||||
| import { Ingredient } from '../../shared/ingredient.model'; | import { Ingredient } from '../../shared/ingredient.model'; | ||||
| import { ShoppingListService } from '../shopping-list.service'; | |||||
| import * as ShoppingListActions from '../ngrx/shopping-list.actions'; | import * as ShoppingListActions from '../ngrx/shopping-list.actions'; | ||||
| import * as fromShoppingList from '../ngrx/shopping-list.reducers'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-shopping-edit', | selector: 'app-shopping-edit', | ||||
| @@ -16,22 +16,23 @@ export class ShoppingEditComponent implements OnInit, OnDestroy { | |||||
| @ViewChild('f') shoppingListForm: NgForm; | @ViewChild('f') shoppingListForm: NgForm; | ||||
| subscription: Subscription | subscription: Subscription | ||||
| editMode = false; | editMode = false; | ||||
| editedItemIndex: number; | |||||
| editedItem: Ingredient; | editedItem: Ingredient; | ||||
| constructor(private shoppingListService: ShoppingListService, | |||||
| private store: Store<{shoppingList: {ingredients: Ingredient[]}}>) { } | |||||
| constructor(private store: Store<fromShoppingList.AppState>) { } | |||||
| ngOnInit() { | ngOnInit() { | ||||
| this.subscription = this.shoppingListService.startedEditing.subscribe( | |||||
| (index: number) => { | |||||
| this.editedItemIndex = index; | |||||
| this.editMode = true; | |||||
| this.editedItem = this.shoppingListService.getIngredient(index); | |||||
| this.shoppingListForm.setValue({ | |||||
| name: this.editedItem.name, | |||||
| amount: this.editedItem.amount | |||||
| }) | |||||
| this.subscription = this.store.select('shoppingList').subscribe( | |||||
| data => { | |||||
| if (data.editedIngredientIndex > -1) { | |||||
| this.editedItem = data.editedIngredient; | |||||
| this.editMode = true; | |||||
| this.shoppingListForm.setValue({ | |||||
| name: this.editedItem.name, | |||||
| amount: this.editedItem.amount | |||||
| }) | |||||
| } else { | |||||
| this.editMode = false; | |||||
| } | |||||
| } | } | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -40,7 +41,7 @@ export class ShoppingEditComponent implements OnInit, OnDestroy { | |||||
| const value = form.value | const value = form.value | ||||
| const newIngredient = new Ingredient(value.name, value.amount); | const newIngredient = new Ingredient(value.name, value.amount); | ||||
| if (this.editMode) { | if (this.editMode) { | ||||
| this.shoppingListService.updateIngredient(this.editedItemIndex, newIngredient); | |||||
| this.store.dispatch(new ShoppingListActions.UpdateIngredient(newIngredient)); | |||||
| } else { | } else { | ||||
| this.store.dispatch(new ShoppingListActions.AddIngredient(newIngredient)); | this.store.dispatch(new ShoppingListActions.AddIngredient(newIngredient)); | ||||
| } | } | ||||
| @@ -54,11 +55,12 @@ export class ShoppingEditComponent implements OnInit, OnDestroy { | |||||
| } | } | ||||
| onDelete() { | onDelete() { | ||||
| this.shoppingListService.deleteIngredient(this.editedItemIndex); | |||||
| this.store.dispatch(new ShoppingListActions.DeleteIngredient()); | |||||
| this.onClear(); | this.onClear(); | ||||
| } | } | ||||
| ngOnDestroy() { | ngOnDestroy() { | ||||
| this.store.dispatch(new ShoppingListActions.StopEdit()); | |||||
| this.subscription.unsubscribe(); | this.subscription.unsubscribe(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -3,7 +3,8 @@ import { Store } from '@ngrx/store'; | |||||
| import { Observable } from 'rxjs/Observable'; | import { Observable } from 'rxjs/Observable'; | ||||
| import { Ingredient } from '../shared/ingredient.model'; | import { Ingredient } from '../shared/ingredient.model'; | ||||
| import { ShoppingListService } from './shopping-list.service'; | |||||
| import * as ShoppingListActions from './ngrx/shopping-list.actions'; | |||||
| import * as fromShoppingList from './ngrx/shopping-list.reducers'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-shopping-list', | selector: 'app-shopping-list', | ||||
| @@ -13,14 +14,13 @@ import { ShoppingListService } from './shopping-list.service'; | |||||
| export class ShoppingListComponent implements OnInit { | export class ShoppingListComponent implements OnInit { | ||||
| shoppingListState: Observable<{ingredients: Ingredient[]}>; | shoppingListState: Observable<{ingredients: Ingredient[]}>; | ||||
| constructor(private shoppingListService: ShoppingListService, | |||||
| private store: Store<{shoppingList: {ingredients: Ingredient[]}}>) { } | |||||
| constructor(private store: Store<fromShoppingList.AppState>) { } | |||||
| ngOnInit() { | ngOnInit() { | ||||
| this.shoppingListState = this.store.select('shoppingList'); | this.shoppingListState = this.store.select('shoppingList'); | ||||
| } | } | ||||
| onEditItem(index: number) { | onEditItem(index: number) { | ||||
| this.shoppingListService.startedEditing.next(index); | |||||
| this.store.dispatch(new ShoppingListActions.StartEdit(index)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,27 +0,0 @@ | |||||
| import { Ingredient } from "../shared/ingredient.model"; | |||||
| import { Subject } from "rxjs/Subject"; | |||||
| export class ShoppingListService { | |||||
| ingredientsChanged = new Subject<Ingredient[]>(); | |||||
| startedEditing = new Subject<number>(); | |||||
| private ingredients: Ingredient[] = []; | |||||
| getIngredient(index: number) { | |||||
| return this.ingredients[index]; | |||||
| } | |||||
| addIngredients(ingredients: Ingredient[]) { | |||||
| this.ingredients.push(...ingredients); | |||||
| this.ingredientsChanged.next(this.ingredients.slice()); | |||||
| } | |||||
| updateIngredient(index: number, newIngredient: Ingredient) { | |||||
| this.ingredients[index] = newIngredient; | |||||
| this.ingredientsChanged.next(this.ingredients.slice()); | |||||
| } | |||||
| deleteIngredient(index: number) { | |||||
| this.ingredients.splice(index, 1); | |||||
| this.ingredientsChanged.next(this.ingredients.slice()); | |||||
| } | |||||
| } | |||||