From 6894474d12ce06e2b8ac579e9105f97344a4e4a8 Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Sun, 13 Aug 2023 20:44:11 +0700 Subject: [PATCH] Refactoring code to MVC architecture --- package-lock.json | 18 ++++- package.json | 4 +- src/js/controller.js | 147 +++---------------------------------- src/js/model.js | 31 ++++++++ src/js/views/recipeView.js | 135 ++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 138 deletions(-) create mode 100644 src/js/model.js create mode 100644 src/js/views/recipeView.js diff --git a/package-lock.json b/package-lock.json index 1e217b9..f0495af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,13 @@ "license": "ISC", "dependencies": { "core-js": "^3.32.0", + "fractional": "^1.0.0", "regenerator-runtime": "^0.14.0" }, "devDependencies": { "@parcel/transformer-sass": "^2.9.3", - "parcel": "^2.9.3" + "parcel": "^2.9.3", + "process": "^0.11.10" } }, "node_modules/@babel/code-frame": { @@ -2592,6 +2594,11 @@ "node": ">=8" } }, + "node_modules/fractional": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fractional/-/fractional-1.0.0.tgz", + "integrity": "sha512-4huGcVdAAr0ffcQ7wQY3dhAdZqKd6QgGOP4Kd7ioNoBAS7GdzolwnqgjH/jTzzCpg71oTLbOVwcOWOH+BmwqvA==" + }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -3337,6 +3344,15 @@ "node": ">=12" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/react-error-overlay": { "version": "6.0.9", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", diff --git a/package.json b/package.json index eca44e3..b2d8201 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,12 @@ "license": "ISC", "devDependencies": { "@parcel/transformer-sass": "^2.9.3", - "parcel": "^2.9.3" + "parcel": "^2.9.3", + "process": "^0.11.10" }, "dependencies": { "core-js": "^3.32.0", + "fractional": "^1.0.0", "regenerator-runtime": "^0.14.0" } } diff --git a/src/js/controller.js b/src/js/controller.js index 66a4104..4c61901 100644 --- a/src/js/controller.js +++ b/src/js/controller.js @@ -1,6 +1,8 @@ -import icons from '../img/icons.svg'; +import * as model from './model.js'; +import recipeView from './views/recipeView.js'; -const recipeContainer = document.querySelector('.recipe'); +import 'core-js/stable'; +import 'regenerator-runtime/runtime'; const timeout = function (s) { return new Promise(function (_, reject) { @@ -14,150 +16,23 @@ const timeout = function (s) { /////////////////////////////////////// -const renderSpinner = function (parentEl) { - const markup = ` -
- - - -
- `; - parentEl.innerHTML = ''; - parentEl.insertAdjacentHTML('afterbegin', markup); -}; - -const showRecipe = async function () { +const controlRecipes = async function () { try { const id = window.location.hash.slice(1); if (!id) return; + recipeView.renderSpinner(); // 1)Loading recipe - renderSpinner(recipeContainer); - const res = await fetch( - `https://forkify-api.herokuapp.com/api/v2/recipes/${id}` - ); - const data = await res.json(); - - if (!res.ok) throw new Error(`${data.message} (${res.status})`); - - let { recipe } = data.data; - - recipe = { - id: recipe.id, - title: recipe.title, - publisher: recipe.publisher, - sourceUrl: recipe.source_url, - image: recipe.image_url, - servings: recipe.servings, - cookingTime: recipe.cooking_time, - ingredients: recipe.ingredients, - }; - - console.log(recipe); + await model.loadRecipe(id); // 2) Rendering recipe - const markup = ` -
- ${recipe.title} -

- ${recipe.title} -

-
- -
-
- - - - ${ - recipe.cookingTime - } - minutes -
-
- - - - ${ - recipe.servings - } - servings - -
- - -
-
- -
- - - -
- -
- -
-

Recipe ingredients

- -
- -
-

How to cook it

-

- This recipe was carefully designed and tested by - ${ - recipe.publisher - }. Please check out - directions at their website. -

- - Directions - - - - -
- `; - recipeContainer.innerHTML = ''; - recipeContainer.insertAdjacentHTML('afterbegin', markup); + recipeView.render(model.state.recipe); } catch (error) { alert(error); } }; -['hashchange', 'load'].forEach(ev => window.addEventListener(ev, showRecipe)); +['hashchange', 'load'].forEach(ev => + window.addEventListener(ev, controlRecipes) +); diff --git a/src/js/model.js b/src/js/model.js new file mode 100644 index 0000000..8485bbc --- /dev/null +++ b/src/js/model.js @@ -0,0 +1,31 @@ +export const state = { + recipe: {}, +}; + +export const loadRecipe = async function (id) { + try { + const res = await fetch( + `https://forkify-api.herokuapp.com/api/v2/recipes/${id}` + ); + const data = await res.json(); + + if (!res.ok) throw new Error(`${data.message} (${res.status})`); + + const { recipe } = data.data; + + state.recipe = { + id: recipe.id, + title: recipe.title, + publisher: recipe.publisher, + sourceUrl: recipe.source_url, + image: recipe.image_url, + servings: recipe.servings, + cookingTime: recipe.cooking_time, + ingredients: recipe.ingredients, + }; + + console.log(state.recipe); + } catch (error) { + alert(error); + } +}; diff --git a/src/js/views/recipeView.js b/src/js/views/recipeView.js new file mode 100644 index 0000000..79a7275 --- /dev/null +++ b/src/js/views/recipeView.js @@ -0,0 +1,135 @@ +import icons from '../../img/icons.svg'; +import fractional from 'fractional'; + +class RecipeView { + #parentElement = document.querySelector('.recipe'); + #data; + + render(data) { + this.#data = data; + const markup = this.#generateMarkup(); + this.#clear(); + this.#parentElement.insertAdjacentHTML('afterbegin', markup); + } + + #clear() { + this.#parentElement.innerHTML = ''; + } + + renderSpinner = function () { + const markup = ` +
+ + + +
+ `; + this.#parentElement.innerHTML = ''; + this.#parentElement.insertAdjacentHTML('afterbegin', markup); + }; + + #generateMarkup() { + return ` +
+ ${
+      this.#data.title
+    } +

+ ${this.#data.title} +

+
+ +
+
+ + + + ${ + this.#data.cookingTime + } + minutes +
+
+ + + + ${ + this.#data.servings + } + servings + +
+ + +
+
+ +
+ + + +
+ +
+ +
+

Recipe ingredients

+ +
+ +
+

How to cook it

+

+ This recipe was carefully designed and tested by + ${ + this.#data.publisher + }. Please check out + directions at their website. +

+ + Directions + + + + +
+ `; + } + + #generateMarkupIngredient(ing) { + return ` +
  • + + + +
    ${ + ing.quantity ? new Fraction(ing.quantity).toString() : '' + }
    +
    + ${ing.unit} + ${ing.description} +
    +
  • + `; + } +} + +export default new RecipeView();