How to fix problem with not working updateServing function in JavaScript? - javascript

I'm trying to implement a function which would calculate the servings for the ingredients from my website.
That function is in Recipe.js file and looks like that:
updateServings(type) {
// Servings
const newServings = type === 'dec' ? this.servings - 1 : this.servings + 1;
// Ingredients
this.ingredients.forEach((ingr) => {
ingr.count = this.capDecimal(ingr.count * (newServings / this.servings));
});
this.servings = newServings;
}
The problem is that when I console.log(state.recipe); in index.js this event Listener works, it will console log state.recipe after clicking - or + button on the website but it wont change the amount of serving in the recipe object:
elements.recipe.addEventListener('click', e => {
if(e.target.matches('.btn-decrease .btn-decrease *')){
//Decrease button is clicked
if(state.recipe.servings > 1){
state.recipe.updateServings('dec');
}
}else if(e.target.matches('.btn-increase .btn-increase *')){
//Increase button was clicked
state.recipe.updateServings('inc');
}
console.log(state.recipe);
});
I clicked 2 times but property serving still says 4 like here:
https://forum.toshitimes.com/uploads/toshitimes/original/2X/6/6bada9081879db1a14df9bad010382606fda253f.png
It a bigger project so I believe I need to include the whole repository from github: https://github.com/damianjnc/forkifyApp.git
What I need to change to make it work?

You need to update the view after the click event
elements.recipe.addEventListener('click', e => {
//....
try {
recipeView.clearRecipe();
recipeView.renderRecipe(state.recipe);
} catch (error) {
alert('error processing the recipe:(');
}
});
note: you need to declare your class properties
export default class Recipe {
ingredients;
servings;
constructor(id) {
this.id = id;
}
and you need map instead of forEach
this.ingredients = this.ingredients.map((ingr) => {
ingr.count = this.capDecimal(ingr.count * (newServings / this.servings));
return ingr;
});

Related

Filtering GALERY with Multiple Selection Tags Filter in WIX

I planning to have a big database/collection of images in Wix Gallery.
I want to filter them out by using numerous tags to find a very specific image in the gallery due to user needs.
As for now, I manage to adjust the filter for just one category working.
My website: My Galery
Code example:
import wixData from 'wix-data';
$w.onReady(function () {
$w('#roomcategory').onChange(() => {
const selectedTag = $w('#roomcategory').value;
let filter = wixData.filter();
if (selectedTag.length > 0) {
filter = filter.hasSome("roomcategory", selectedTag);
}
$w('#portfolioDataset').setFilter(filter);
})
});
/**
* Adds an event handler that runs when the element is clicked.
[Read more](https://www.wix.com/corvid/reference/$w.ClickableMixin.html#onClick)
* #param {$w.MouseEvent} event
*/
export function roomcategory_click(event) {
// This function was added from the Properties & Events panel. To learn more, visit http://wix.to/UcBnC-4
// Add your code for this event here:
}
/**
* Adds an event handler that runs when an input element's value
is changed.
[Read more](https://www.wix.com/corvid/reference/$w.ValueMixin.html#onChange)
* #param {$w.Event} event
*/
export function roomcategory_change(event) {
// This function was added from the Properties & Events panel. To learn more, visit http://wix.to/UcBnC-4
// Add your code for this event here:
}
Layout of tags
There's a video on YT I followed by this guy filtering Repeater not a gallery.
video link : Multiple Selection Tags Filter in WIX
I tried to edit his code as well but doesn't work for me.
import wixData from 'wix-data';
​
$w.onReady(function () {
$w("#roomcategory, #capacity, #roomrating, #colortag, #languagetag").onChange(function () {
search();
}
(function search() {
let filter = wixData.filter();
let roomcategoryIdx = $w("#roomcategory").selectedIndices;
let capacityIdx = $w("#capacity").selectedIndices;
let roomratingIdx = $w("#roomrating").selectedIndices;
let colortagIdx = $w("#colortag").selectedIndices;
let languagetagIdx = $w("#languagetag").selectedIndices;
let roomcategoryVal = $w("#roomcategory").value;
let capacityVal = $w("#capacity").value;
let roomratingVal = $w("#roomrating").value;
let colortagVal = $w("#colortag").value;
let languagetagVal = $w("#languagetag").value;
if (roomcategoryIdx.length > 0 || capacityIdx.length > 0 || roomratingIdx.length > 0) || colortagIdx.length > 0) || languagetagIdx.length > 0) {
filter = filter.hasAll("roomcategory", roomcategorysVal)
.and(filter = filter.hasAll("capacity", capacityVal))
.and(filter = filter.hasAll("roomrating", roomratingVal))
.and(filter = filter.hasAll("colortag", colortagVal))
.and(filter = filter.hasAll("languagetag", languagetagVal))
$w("#portfolioDataset").setFilter(filter)
.then(count)
} else {
$w("#portfolioDataset").setFilter(filter)
.then(count)
}
$w("#resetfilters").onClick(function () {
$w("#roomcategory, #capacity, #roomrating, #colortag, #languagetag").value = undefined;
$w("#portfolioDataset").setFilter(wixData.filter()).then(count);
});
}
//COUNT ITEM
function count() {
let count = $w("#portfolioDataset").getTotalCount();
if (count > 0) {
$w("#countText").text = `${count} rooms found`;
} else { $w("#countText").text = `No rooms found`; }
return count;
}
$w("#portfolioDataset").onReady(function () {
count();
});
});

dynamic import's on.("click", ...) can't find global variable

I am trying to create an application in electron (code in front end javascript side) to aid in my writing, and something I need is to dynamically load modules/plugins/mods. I am able to actually import a function that has a class returned from it (It was the only way so far that I could actually get dynamic import of a class to work...), and it assigns some jquery node.on("click") that do not run as expected. I have several object containers that I use to separate major portions of my code from any by chance matching variables. Anyways, the one for menus (MenuManager) throws an error that it cannot be found when I click the element for on("click").
I want the variables like MenuManager to be globally available inside the app, so plugins/mods can make any decided changes.
MenuManager.js
//Isolate MenuManager variables to itself, to majorly reduce variable naming mishaps and make it
//easier to modify something later
let MenuManager = {
menus: []
}
//import PluginDef from "../../Workspaces/Writing/define.js";
$(function () {
console.log("MenuManager Booting!");
//Create menus from plugin
LoadPlugins();
LoadPlugins();
});
function LoadPlugins() {
/*const testLoad = import("../../Workspaces/Writing/define.js");
console.log(testLoad.default.PluginDef().Name);*/
import("../../Workspaces/Writing/define.js").then((module) => {
let newModule = module.default();
newModule.Generate();
MenuManager.menus.push(newModule);
console.log("loading: " + MenuManager.menus[0].Name);
}).catch((err) => {
console.log(err);
});
}
imported define.js
export default function PluginDef() {
class def {
constructor() {
this.Name = "Writing";
this.MenuBase = "<h5>Menus HTML here</5>";//import from file later
this.workspaceBase = "<h5>Workspace HTML here</h5>";//import from file later
}
Generate() {
this.GenerateSelector();
this.GenerateMenu();
this.GenerateWorkspace();
this.Run_OnGenerated();
}
GenerateSelector() {
let Self = this;
//Generate Menu Selector Header
this.menuSelector = $([
"<li>",
this.Name,
"</li>"
].join("\n"));
//Add listener to selector
this.menuSelector.on("click", function () {
//let foundMenu = MenuManager.menus.find(x => x.Name == this.textContent);
MenuManager.currentMenu.menu.removeClass("selectedMenu");
MenuManager.currentMenu.workspace.removeClass("selectedWorkPanel");
MenuManager.currentMenu.Run_OnClose();//Ran whenever menu is deselected
MenuManager.currentMenu = Self;//Set 'this' to currently selected menu
MenuManager.currentMenu.menu.addClass("selectedMenu");
MenuManager.currentMenu.workspace.addClass("selectedWorkPanel");
MenuManager.currentMenu.Run_OnOpen();//Ran whenever menu is selected
});
//Add selector to header
AppPart.MenuBarSelector.find("ul").append(this.menuSelector);
}
GenerateMenu() {
//Generate Menu
this.menu = $([
"<div id='Menu_Writing' class='menuBarPanel'>",
this.MenuBase,
"</div>"
].join("\n"));
//Add menu to panels
AppPart.MenuBarOptionsContainer.append(this.menu);
}
GenerateWorkspace() {
//Generate Workspace
this.workspace = $([
"<div id='WorkPanel_Writing' class='workPanel'>",
this.workspaceBase,
"</div>"
].join("\n"));
//Add workspace to workspace
AppPart.WorkAreaContainer.append(this.workspace);
}
//When menu is generated, run this function
Run_OnGenerated() {
return;//does nothing
}
//When menu is selected, run this function
Run_OnOpen() {
return;//Does Nothing
}
//When menu is deselected, run this function
Run_OnClose() {
return;//Does Nothing
}
}
return new def();
}
GenerateSelector() of imported define.js (line 27 specifically) is where the error for MenuManager is not defined happens
I did not see #Chris G's comment/answer, because I did not get an email or such. But anyways, I had figured out through I think blind luck that:
I could create a base class and place that in window, where the imported class could easily access the class without trying to remember or know any file locations (And yes, I am working on keeping global/window clean, not shoving it with tons of variables, just the few important ones like this case)
Note: I cannot explain why this exactly works, but possibly due to the scope.
MenuManager.js
function LoadPlugins() {
import("../../Workspaces/Writing/define.js").then((module) => {
let newModule = module.default();
MenuManager.menus.push(newModule);
console.log("loaded: " + MenuManager.menus[0].Name);
}).catch((err) => {
console.log(err);
});
}
class PluginBaseDef {
Name;
MenuBase;
workspaceBase;
menuSelector;
menu;
workspace;
constructor() {
}
Generate() {
this.GenerateSelector();
this.GenerateMenu();
this.GenerateWorkspace();
this.Run_OnGenerated();
if (!MenuManager.currentMenu.Name) {
this.ShowMenu();
}
}
}
window.PluginBaseDef = PluginBaseDef;
imported define.js
export default function PluginDef() {
class def extends window.PluginBaseDef {
constructor() {
super();
this.Name = "Writing";
this.MenuBase = "<h5>Menus HTML here</5>";//import from file later
this.workspaceBase = "<h5>Workspace HTML here</h5>";//import from file later
this.Generate();
}
}
return new def();
}

this.router.routeReuseStrategy.shouldReuseRoute = () => false;

this.router.routeReuseStrategy.shouldReuseRoute = () => false;
I have applied this sort of line in order to make the component UI updated everytime. But in some other cases it start to refreshing the page event if it should reuse the route.
How can we overcome this issue?
Actually in my application there are three tabs in left panel. In each tab there are some listings clicking on list items opens the content on right panel. But in one of the listing there is a common UI that is getting open on some list item, but the problem is that when we don't apply above sort of code then the UI is not getting updated. But if we apply the code then the UI is updated everytime we click on other list item. But the problem is that when we apply this code it start to refresh the page everytime we click on other list in different tabs also, that should not be the case.
If we apply this code this.router.routeReuseStrategy.shouldReuseRoute = () => false; then how can we revert this functionality under this.router?
To take less risks I'm just reverting it back to what it was once the reload is done:
refresh() {
const prev = this.router.routeReuseStrategy.shouldReuseRoute;
const prevOSN = this.router.onSameUrlNavigation;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([this.router.url]);
setTimeout(() => {
this.router.routeReuseStrategy.shouldReuseRoute = prev;
this.router.onSameUrlNavigation = prevOSN;
}, 0);
}
I have the same issue, I changed that line for this:
// override the route reuse strategy
this.router.routeReuseStrategy.shouldReuseRoute = function () {
return false;
};
this.router.events.subscribe((evt) => {
if (evt instanceof NavigationEnd) {
// trick the Router into believing it's last link wasn't previously loaded
this.router.navigated = false;
// if you need to scroll back to top, here is the right place
window.scrollTo(0, 0);
}
});
I don't even know if this works well or do the same thing.
private saveRouterStrategyReuseLogic: any;
ngOnInit() {
// Save logic
this.saveRouterStrategyReuseLogic = this.router.routeReuseStrategy.shouldReuseRoute;
this.router.routeReuseStrategy.shouldReuseRoute = (future, curr) => { return false; };
}
ngOnDestroy() {
this.router.routeReuseStrategy.shouldReuseRoute =
this.saveRouterStrategyReuseLogic;
}

Function sortAlpha is undefined

I am currently trying to sort all my game objects alphabetically by title. I have gotten my onclick to register but it does not execute my JS function below is the HTML and JS snippets. The sortAlpha is a method within the Games class.
*edit 1: Adjusted function to attach a event listener
*edit 2: I have opted to create a variable to store and call the function. My next question is am I not correctly displaying the newly alphabetized contents with my function ? I get no errors and my log shows that clicks are being registered.
<div id="filter">
<div id="buttons">
<button onclick="let the_game = new Games(); the_game.sortAlpha()">Sort Games Alphabetically</button>
</div>
</div>
class Games {
constructor() {
this.games = []
this.adapter = new GamesAdapter()
this.initBindingsAndEventListeners()
this.fetchAndLoadGames()
}
initBindingsAndEventListeners() {
this.newGameForm = document.getElementById('new-game-form')
this.newGameTitle = document.getElementById('new-game-title')
this.newGameDeveloper = document.getElementById('new-game-developer')
this.newGameCover = document.getElementById('new-game-cover')
this.newGameForm.addEventListener('submit', this.createGame.bind(this))
}
createGame(g) {
g.preventDefault();
const titleValue = this.newGameTitle.value;
const developerValue = this.newGameDeveloper.value;
const coverValue = this.newGameCover.value;
this.adapter.createGame(titleValue, developerValue, coverValue)
.then(game => {
const newGame = new Game(game)
this.games.push(newGame)
this.newGameTitle.value = ' '
this.newGameDeveloper.value = ' '
this.newGameCover.value = ' '
newGame.renderGameBlock()
})
}
fetchAndLoadGames() {
this.adapter
.getGames()
.then(games => {
games.forEach(game => this.games.push(new Game(game)))
})
.then(() => {
this.renderGames()
})
}
renderGames() {
this.games.map(game => game.renderGameBlock())
}
sortAlpha() {
console.log('test');
this.games.sort(function(gameA, gameB){
if (gameA.title < gameB.title) {
return -1;
}
if (gameA.title > gameB.title){
return 1;
}
return 0;
});
window.onload = function() {
document.getElementById("filter").onclick = sortAlpha;
}
}
}
The sortAlpha is a method within the Games class.
That's your answer right there. sortAlpha is not a free-function, but your onclick="" attribute references sortAlpha() as though it were a free-function.
(A "free function" is a function that is not a member of a class/type)
You need to move your sortAlpha function to the global scope, or attach it to the button using addEventListener. You cannot use class method functions in onclick="" without fully qualifying them.

How to run function after another object reaches certain state

I have a game. I have 3 files app.js, game.js, modal.js.
The Modal sets an event listener on the 'next' button of the modal, and keeps track of 'currentStep'. That is, the modal has multiple steps. Now, I want in the app.js file to start the game, only after the user has completed the modal. In my modal I have :
constructor(){
this.currentStep = 0;
}
complete() {
return (this.currentStep >= 4 ? true : false);
}
activate() {
....
nextButton.addEventListener( 'click' , (e) => {
if (this.currentStep === 0) {
firstInstructions.classList.add('hideMe');
firstGif.classList.remove("hideMe");
....
}else if (this.currentStep === 4) {
modal.classList.add("hideMe");
}
this.currentStep++;
});
}
Now, the basic point is that I somehow want to let my app.js know when the user has completed the Modal so that I can run game.play();
So something like :
const game = new Game();
const modal = new Modal();
modal.activate();
// game.play() WHEN modal.complete
Can someone suggest the proper way for the Modal to let the App know that it is complete, whenever that happens. I know I can use a while loop and keep checking if modal.complete() and when it is run game.play() but that seems wrong.
Thanks.
I can think of two ways, one is you can just add in the activate the game itself like this.
const game = new Game();
const modal = new Modal();
modal.activate(game);
Then in the activate you can do this:
activate (game) {
....
nextButton.addEventListener( 'click' , (e) => {
if (this.currentStep === 0) {
firstInstructions.classList.add('hideMe');
firstGif.classList.remove("hideMe");
....
} else if (this.currentStep === 4) {
modal.classList.add("hideMe");
// Put it here
game.play();
}
this.currentStep++;
};
That is if the last modal step will start the game. But if you really want to put it in complete method. You can also do this
const game = new Game();
// put the game inside the Modal constructor
const modal = new Modal(game);
modal.activate();
Then you put the game in the constructor method
constructor (game) {
this.currentStep = 0;
// put it here
this._game = game;
}
Then in the complete method you can just invoke play from the game
complete () {
this._game.play();
return (this.currentStep >= 4 ? true : false);
}
Hope this helps

Categories

Resources