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