From 52454741f337e1c3b5ea3b1eb9d28850c14d4aac Mon Sep 17 00:00:00 2001 From: Chandrabhatta Sriram Date: Thu, 27 Feb 2020 19:24:36 +0530 Subject: [PATCH] 190 Forkify: Likes View (Front-End) - Added 7.7.17 named as "Building the Likes View (Front-End)" --- .../forkify_project/src/js/index.js | 53 +++++++++++++----- .../forkify_project/src/js/views/base.js | 4 +- .../forkify_project/src/js/views/likesView.js | 56 +++++++++++++++++++ .../src/js/views/recipeView.js | 4 +- .../src/js/views/searchView.js | 4 +- 5 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/likesView.js diff --git a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/index.js b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/index.js index da69e5e..ee1a22a 100644 --- a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/index.js +++ b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/index.js @@ -1,9 +1,22 @@ /******************************************************************************************************************** - * We make the controller for the likes in here. The event for liking an item happens inside the - * .recipe class. - * Therefore, we handle the event again at the .recipe class, where we only catch the event that pertains to the - * 'click' event occurring on the like button, which is the button with .recipe__love class under the - * .recipe class generated dynamically, and this is the reason we use event delegation. + * Now we will handle the UI for the likes. Whenever the user clicks on the love button for a recipe, + * the love button has to be shown as it is a clicked love button. For that, we add logic inside the recipeView + * Module found at ./src/js/views/recipeView.js. + * + * Also, the love button should be persistent, it means that even if we reload the page, the recipe should be liked + * and that love button should show that the recipe was liked. We also implement that functionality + * by changing the recipeView module where we modify the renderRecipe() method. Note that the recipe won't be rendered + * when we reload and that's because state.likes object is not available at the time of the call of renderRecipe() + * inside the controlRecipe() controller. For that reason, we simply use a global state.likes object for testing + * purposes. We can see that global state.likes object on top of the actual Likes Controller which is controlLikes() + * controller method. + * + * Also, we don't want to show the heart icon at the top right of the webapp if there are no recipes that are liked + * by the user yet. Therefore, we will implement the functionality to hide the heart icon at the top right in the + * likesView Module. + * + * As the final step, we will render the likes into the heart icon's list when we hover over it. + * Implementation of the method related to rendering the likes can be found in the likesView Module. */ // Import Data Models @@ -16,6 +29,7 @@ import Likes from './models/Likes'; import * as searchView from './views/searchView'; import * as recipeView from './views/recipeView'; import * as listView from './views/listView'; +import * as likesView from './views/likesView'; // Import Common Code Base import { elements, renderLoader, clearLoader, elementStrings } from './views/base'; @@ -130,7 +144,10 @@ const controlRecipe = async () => { // Render Recipe -- console.log(state.recipe); clearLoader(); - recipeView.renderRecipe(state.recipe); + recipeView.renderRecipe( + state.recipe, + state.likes.isLikedItem(id) + ); } catch (error) { clearLoader(); @@ -193,9 +210,13 @@ elements.shopping.addEventListener('click', event => { }); -// LIKE CONTROLLER + +// LIKES CONTROLLER +state.likes = new Likes(); // TESTING +likesView.toggleLikeMenu(state.likes.getNumberOfLikedItems()); // TESTING + const controlLike = () => { - if (!state.likes) state.likes = new Likes(); + // if (!state.likes) state.likes = new Likes(); const currentID = state.recipe.id; // If a recipe is liked, then we have to include it in state.likes map, otherwise we don't. @@ -207,23 +228,27 @@ const controlLike = () => { const newLike = state.likes.addLikedItem(currentID, state.recipe.title, state.recipe.author, state.recipe.img); // Toggle the love button - + likesView.toggleLikeBtn(true); // Add like to the UI, i.e., add the liked recipe to the liked list - - console.log(state.likes); // TESTING + likesView.renderLike(newLike); + // console.log(state.likes); // TESTING } else { // when the current recipe is liked // Remove like from the state state.likes.deleteLikedItem(currentID); // Toggle the love button + likesView.toggleLikeBtn(false); // Remove like from the UI, i.e. from the liked list - - - console.log(state.likes); // TESTING + likesView.deleteLike(currentID); + // console.log(state.likes); // TESTING } + + likesView.toggleLikeMenu( + state.likes.getNumberOfLikedItems() + ); }; diff --git a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/base.js b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/base.js index 0d85b59..65cd0be 100644 --- a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/base.js +++ b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/base.js @@ -8,7 +8,9 @@ export const elements = { searchRes: document.querySelector('.results'), searchResPages: document.querySelector('.results__pages'), recipe: document.querySelector('.recipe'), - shopping: document.querySelector('.shopping__list') + shopping: document.querySelector('.shopping__list'), + likesMenu: document.querySelector('.likes__field'), + likesList: document.querySelector('.likes__list') }; diff --git a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/likesView.js b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/likesView.js new file mode 100644 index 0000000..a8eb1cd --- /dev/null +++ b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/likesView.js @@ -0,0 +1,56 @@ +import { elements, elementStrings } from './base'; +import { limitRecipeTitle } from './searchView'; + + +export const toggleLikeBtn = isLiked => { + /** + * + */ + + // we have to change 'icon-heart-outlined' to 'icon-heart' + const iconString = isLiked ? 'icon-heart' : 'icon-heart-outlined'; + + // change the href of the .recipe__love button, for which we select the use element inside the + // .recipe__love class as follows: document.querySelector('.recipe__love use') and then change the + // href attribute to the one we want. + // document.querySelector('.recipe__love use').setAttribute('href', `img/icons.svg#${iconString}`) + + document.querySelector(`.${elementStrings.loveButton} use`).setAttribute('href', `img/icons.svg#${iconString}`); +}; + +// called inside the controlLike() method at ./src/js/index.js, which is the controller for the likes. +export const toggleLikeMenu = numLikes => { + elements.likesMenu.style.visibility = numLikes > 0 ? 'visible' : 'hidden'; +}; + + +export const renderLike = like => { + // Taken from ./src/index.html under the .likes__list class + // console.log(like); // TESTING + + const markup = ` +
  • + +
  • + `; + + elements.likesList.insertAdjacentHTML('beforeend', markup); +}; + + +export const deleteLike = id => { + const el = document.querySelector(`.likes__link[href="#${id}"]`).parentElement; + if (el) el.parentElement.removeChild(el); +}; \ No newline at end of file diff --git a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/recipeView.js b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/recipeView.js index 66015ce..6236806 100644 --- a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/recipeView.js +++ b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/recipeView.js @@ -47,7 +47,7 @@ const createIngredient = ingredient => ` `; -export const renderRecipe = recipe => { +export const renderRecipe = (recipe, isLiked) => { // we get the markup under .recipe class in index.html const markup = `
    @@ -91,7 +91,7 @@ export const renderRecipe = recipe => { diff --git a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/searchView.js b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/searchView.js index 2150d6f..4e7e26f 100644 --- a/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/searchView.js +++ b/Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/views/searchView.js @@ -33,12 +33,12 @@ export const highlightSelected = id => { }); // highlight the selected recipe from .results__list class - document.querySelector(`a[href="#${id}"]`).classList.add('results__link--active'); + document.querySelector(`.results__link[href="#${id}"]`).classList.add('results__link--active'); }; // function to limit the recipe name in the .results__list class -const limitRecipeTitle = (title, limit = 17) => { // 17 is the sweet spot for limiting the no. of letters +export const limitRecipeTitle = (title, limit = 17) => { // 17 is the sweet spot for limiting the no. of letters let newTitle = []; if (title.length > limit) { title.split(' ').reduce((acc, curr) => {