Array isn't getting overridden in local storage - javascript

I'm creating a to-do list project. where I'm trying to delete a todo when the user clicks on the delete button. I'm using the array's filter method to remove that clicked todo. But when I refresh, that deleted todo comes back. the reason is that It's not getting removed from the local storage. There's something wrong with the event listener at the very bottom of the javascript file. I'm trying to overrides the array with whatever filter method returns and saving it to the local storage but still it doesn't work.
Javascript file
import Todo from './todo.js';
import './style.css';
const TODO_LIST_KEY = 'TODO_LIST_KEY';
const template = document.querySelector('#list-item-template');
const todoListContainer = document.querySelector('#list');
const form = document.querySelector('.form');
const inputField = document.querySelector('#todo-input');
const loadList = () => {
const dataInStringFormat = localStorage.getItem(TODO_LIST_KEY);
return JSON.parse(dataInStringFormat) || [];
};
const renderTodo = (todo) => {
console.log("I'm inside of renderTodo Method");
const templateClone = template.content.cloneNode(true);
const taskContent = templateClone.querySelector('[data-list-item-text]');
taskContent.innerText = todo.description;
const checkBox = templateClone.querySelector('[data-list-item-checkbox]');
checkBox.checked = todo.completed;
checkBox.addEventListener('change', () => {
todo.completed = checkBox.checked;
saveList();
});
const listItem = templateClone.querySelector('.list-item');
listItem.dataset.todoIndex = todo.index;
todoListContainer.appendChild(templateClone);
};
let todoList = loadList();
todoList.forEach((todo) => renderTodo(todo));
const saveList = () => {
localStorage.setItem(TODO_LIST_KEY, JSON.stringify(todoList));
};
const clearField = () => {
inputField.value = '';
};
form.addEventListener('submit', () => {
if (inputField.value === '') return;
// Create a new Todo
const todoTemplate = new Todo(todoList.length, inputField.value, false);
todoList.push(todoTemplate); // new todo gets added to the list
renderTodo(todoTemplate); //Here it adds that new todo to the list
saveList();
clearField();
});
todoListContainer.addEventListener('click', (e) => {
if (!e.target.matches('[data-button-delete]')) return;
// Get the todo that is clicked on
const parent = e.target.closest('.list-item');
const todoIndex = parent.dataset.todoIndex;
// const todoItem = todoList.find((t) => t.index === todoIndex);
parent.remove(); // removes from the screen
todoList = todoList.filter((todo) => todo.index !== todoIndex);
saveList();
});
HTML File
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>To Do List</title>
</head>
<body>
<section class="todo-container">
<ul class="todo-list" id="list">
<li class="heading">
<h3>Today's To Do</h3>
<img
src="https://img.icons8.com/ios-glyphs/30/000000/refresh--v1.png"
alt="refresh-icon"
class="refresh-icon"
/>
</li>
<li>
<form class="form">
<label for="todo-input">
<input
type="text"
id="todo-input"
placeholder="Add to your list..."
/>
</label>
</form>
</li>
</ul>
<article class="footer">
Clear all completed
</article>
</section>
<template id="list-item-template">
<li class="list-item">
<label class="list-item-label">
<input type="checkbox" data-list-item-checkbox />
<span data-list-item-text></span>
</label>
<img
data-button-delete
src="https://img.icons8.com/ios-glyphs/30/000000/trash--v1.png"
alt="delete-icon"
class="delete-icon"
/>
</li>
</template>
</body>
</html>

I'm guessing that todo.index is a number. dataset values are always strings, so todo.index !== todoIndex will always be true when todo.index and todoIndex are different types.
Set todoIndex to an integer:
const todoIndex = parseInt(parent.dataset.todoIndex);

Related

how to render appendChild Js without duplicate

I am a new learning JS. Who can help me complete this code. I have 2 problem:
render child Node user Chat when click without duplicate
how to remove child Node user when close chat window
full code is here: Jsfiddle
// event handling when click
handleEvents: function () {
let _this = this;
userChatList.onclick = function (e) {
const userNode = e.target.closest(".user-chat__item");
if (userNode) {
userIndex = Number(userNode.getAttribute("user-num"));
_this.renderUserChat(userIndex);
const getChatWithItems = document.querySelectorAll(".chat-with__item");
getChatWithItems.forEach(item => {
item.onclick = function(e){
const itemNode = e.target.closest(".chat-with__top i");
if(itemNode){
chatWithList.removeChild(chatWithItem);
}
}
})
}
}
},
//render user chat with someone
renderUserChat: function (num) {
// console.log(userIndex);
chatWithItem = document.createElement("li");
chatWithItem.classList.add("chat-with__item");
chatWithItem.setAttribute('user-num', num);
chatWithItem.innerHTML = `
<div class="chat-with__top">
<div class="chat-with__img">
<img src="${this.users[num].img}" alt="${this.users[num].name}">
<span class="user__status ${this.users[num].status}"></span>
</div>
<p class="chat-with__name">${this.users[num].name}</p>
<i class="fa-solid fa-xmark"></i>
</div>
<div class="chat-with__body">
<ul class="chat__text">
<li class="chat-text__user">Hey. 👋</li>
<li class="chat-text__user user__chatting">I am here</li>
<li class="chat-text__user user__chatting">What's going on?</li>
<li class="chat-text__user">Have you finished the "project 2" yet?</li>
<li class="chat-text__user user__chatting">I have been fixed bugs</li>
<li class="chat-text__user">OK.</li>
</ul>
</div>
<div class="chat-width__footer">
<i class="fa-solid fa-image"></i>
<i class="fa-solid fa-folder"></i>
<div class="chat-width__input">
<input type="text" id="send-sms" name="send SMS" placeholder="...">
</div>
<i class="fa-solid fa-paper-plane-top"></i>
</div>
`
chatWithList.appendChild(chatWithItem);
},
<ul class="chat-with__list">
</ul>
I have not still known how to solve it, up to now
Just keep track which chat windows are opened in an object.
To give you basic idea of the concept:
// storage for opened chat windows
// this variable must be accessible by event handlers
const openedChats = {};
In chat opened event handler:
if (openedChats[userId]) //check if chat already opened
return;
const chatWithItem = document.createElement("li");
...
openedChats[userId] = chatWithItem; //store window
chatWithList.appendChild(chatWithItem); //show window
In chat close event handler:
const chatWithItem = openedChats[userId]; // get opened chat
if (chatWithItem)
{
chatWithItem.parentNode.removeChild(chatWithItem); // destroy window
delete openedChats[userId]; // remove window
}
If you need to get list of all userIds that have opened chat windows, use:
const openedChatsIds = Object.keys(openedChats);
Finnaly I find the way to code. This is my way
handleEvents: function () {
let _this = this;
let currentChat = [];
userChatList.onclick = function (e) {
const userNode = e.target.closest(".user-chat__item");
if (userNode) {
userIndex = Number(userNode.getAttribute("user-num"));
// get value 'userIndex' for currentChat array
function getCurrentChat(arr, index) {
arr.push(index);
}
// check value userIndex in a currentChat array
function checkCurrentChat(arr, index) {
if (arr.indexOf(index) < 0) {
getCurrentChat(currentChat, userIndex);
return true;
} else {
return false;
}
}
let isExisted = checkCurrentChat(currentChat, userIndex);
// console.log(isExisted);
if (isExisted) {
_this.renderUserChat(userIndex);
}
const getChatWithItems = chatWithList.querySelectorAll(".chat-with__item");
getChatWithItems.forEach( function(item) {
item.onclick = function (e) {
const closeChat = e.target.closest(".chat-with__top i");
if(closeChat){
const getNum = Number(closeChat.parentElement.getAttribute("user-num"));
chatWithList.removeChild(item);
const findNum = currentChat.indexOf(getNum);
currentChat.splice(findNum, 1);
}
}
})
}
}
}
inside, i add an attribute to get number (userIndex):
<div class="chat-with__top" user-num ="${num}">
if you use second .parentElement, it will ok.
closeChat.parentElement.parentElement.getAttribute("user-num")

Can't find why the if block not getting executed

I'm a newbie in javascript, i've been following a youtube video of creating a simple project like booklist app using javascript the tutorial is very well explained but when i tried to do it myself i got stuck at one point i can't figure out what's happening
The project is basically about when i submit the details of the book it will be added to the table in the webpage, also it will stored in the local storage too. same like that i need to remove the details of the book from local storage when it is removed from the table.
Here is the code for setting up the class Store with methods getBooks for getting the books from the local storage, addBook for adding new book to local storage, removeBook for removing the book from local storage
class Store{
static getBooks(){
let books;
if (localStorage.getItem('books') == null) {
books = [];
} else{
books = JSON.parse(localStorage.getItem('books'));
}
return books;
}
static addBook(Book) {
const books = Store.getBooks();
books.push(Book);
localStorage.setItem('books', JSON.stringify(books));
}
static removeBook(isbn){
const books = Store.getBooks();
books.forEach((book, index) => {
if (book.isbn === isbn) {
books.splice(index, 1);
}
});
localStorage.setItem('books', JSON.stringify(books));
}
}
The methods getBooks and addBooks are working perfectly fine, but the removeBook method is not working in a way that i wanted.
Here is how i invoked the method,
document.querySelector('#book-list').addEventListener('click', (e) => {
// Delete book from the table in interface
UI.deleteBook(e.target);
Store.removeBook(e.target.parentElement.previousElementSibling.textContent);
});
e.target.parentElement.previousElementSibling.textContent is getting the correct value i needed, so i did made the call to removeBook successfully but i can't pass through the if block inside the method
Here is my complete HTML script
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Book List</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.3/css/all.css" integrity="sha384-SZXxX4whJ79/gErwcOYf+zWLeJdY/qpuqC4cAa9rOGUstPomtqpuNWT9wdPEn2fk" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch#4.5.2/dist/yeti/bootstrap.min.css" integrity="undefined" crossorigin="anonymous">
</head>
<body>
<div class="container mt-4">
<h1 class="display-4 text-center">
<i class="fas fa-book-open text-primary"></i> My<span class="text-primary">Book
</Myspan>List</h1>
<form class="book-form">
<div class="form-group">
<label for="title">Title</label>
<input type="text" id="title" class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label for="author">Author</label>
<input type="text" id="author" class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label for="isbn">ISBN#</label>
<input type="text" id="isbn" class="form-control" autocomplete="off">
</div>
<input type="submit" value="Add Book" class="btn btn-primary btn-sm">
</form>
<table class="table table-striped mt-5">
<thead>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
<th></th>
</thead>
<tbody id="book-list"></tbody>
</table>
</div>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
Here is my complete javascript code,
class Book{
constructor(title, author, isbn){
this.title = title;
this.author = author;
this.isbn = isbn;
}
}
class UI{
static displayBooks(){
const books = Store.getBooks();
books.forEach((book) => UI.addBookToList(book))
}
static addBookToList(book){
const list = document.querySelector("#book-list");
const row = document.createElement('tr');
row.innerHTML = `
<td> ${book.title} </td>
<td> ${book.author} </td>
<td> ${book.isbn} </td>
<td>X</td>
`;
list.appendChild(row);
}
static deleteBook(el){
if(el.classList.contains('delete')){
el.parentElement.parentElement.remove();
}
}
static showAlert(message, className) {
const div = document.createElement('div');
div.className = `alert alert-${className}`;
div.appendChild(document.createTextNode(message));
const container = document.querySelector('.container');
const form = document.querySelector('.book-form');
container.insertBefore(div, form);
setTimeout(()=>
document.querySelector('.alert').remove(),
3000
);
}
static clearFields() {
document.querySelector('#title').value = '';
document.querySelector('#author').value = '';
document.querySelector('#isbn').value = '';
}
}
class Store{
static getBooks(){
let books;
if (localStorage.getItem('books') == null) {
books = [];
} else{
books = JSON.parse(localStorage.getItem('books'));
}
return books;
}
static addBook(Book) {
const books = Store.getBooks();
books.push(Book);
localStorage.setItem('books', JSON.stringify(books));
}
static removeBook(isbn){
const books = Store.getBooks();
books.forEach((book, index) => {
if (book.isbn.toString() === isbn.toString()) {
books.splice(index, 1);
}
});
localStorage.setItem('books', JSON.stringify(books));
}
}
document.addEventListener("DOMContentLoaded", UI.displayBooks());
document.querySelector('.book-form').addEventListener('submit', (e) => {
e.preventDefault();
const title = document.querySelector('#title').value;
const author = document.querySelector('#author').value;
const isbn = document.querySelector('#isbn').value;
if (title === '' || author === '' || isbn === '') {
UI.showAlert("Please fill in all fileds", "danger");
} else {
const book = new Book(title, author, isbn);
UI.addBookToList(book);
Store.addBook(book);
UI.clearFields();
UI.showAlert("Succefully added", 'success');
}
});
document.querySelector('#book-list').addEventListener('click', (e) => {
Store.removeBook(e.target.parentElement.previousElementSibling.textContent);
UI.deleteBook(e.target);
UI.showAlert("Succefully removed", 'success');
});
I spent one and half hour to figure out what's wrong in the code but i still can't, I'm completely new to javascript.
The problem is that in your HTML you pad the book ISBN (and other fields) with spaces:
row.innerHTML = `
<td> ${book.title} </td>
<td> ${book.author} </td>
<td> ${book.isbn} </td>
<td>X</td>
`;
This means that the textContent of those td elements will not match with the properties of your book object. Either trim what you get from textContent, or just remove those spaces from your HTML:
row.innerHTML = `
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td>X</td>
`;
If your goal was to give these texts a bit of margin, then do that with CSS styling instead.
There are also 2 other issues I bumped into:
Your HTML has </Myspan>, which should be </span>.
You don't correctly set the handler for the DOMContentLoaded event. The argument should be a function, but you actually execute a function instead (immediately). So remove the parentheses at the end:
document.addEventListener("DOMContentLoaded", UI.displayBooks);

How to remove list item from Typescript todo list app

I am attempting to build a basic todo list app using ONLY Typescript, HTML, and CSS. app.ts is set up to listen for a submit event. The ListTemplate class defines a render method used to create and append the list item elements to the DOM. The ListItem class defines listItem as a string type and executes the format() function defined in the HasFormatter interface. List items are dynamically appended to <ul></ul> container in the HTML. The functionality to input text and append list items to the DOM currently works fine. I am now trying to append functioning remove item buttons to each list item, which I set up in the render method: const btn = document.createElement('button'). I am not sure how to set up the functionality to remove individual list items when clicking the remove buttons. I tried adding another event listener to app.ts that listens for the remove button with the id listItemBtn, but am not sure how to set up the listener (or potentially a remove method in ListTemplate) to target specific list items when clicking remove button. Any thoughts on how to do this? Thanks!
Here is the CodeSandBox link for the project: https://codesandbox.io/s/vanilla-typescript-forked-xnnf4q
app.ts
import { ListItem } from './classes/ListItem.js';
import { ListTemplate } from './classes/ListTemplate.js';
import { HasFormatter } from './interfaces/HasFormatter.js'
const form = document.querySelector('#newForm') as HTMLFormElement; // typecasting, casting the element to be a certain type
const listItemBtn = document.querySelector('#listItemBtn') as HTMLButtonElement;
const listItem = document.querySelector('#listItem') as HTMLInputElement;
//list template instance
const ul = document.querySelector('ul')!;
const list = new ListTemplate(ul)
form.addEventListener('submit', (e: Event) => {
e.preventDefault();
let values: [string] // tuple
values = [listItem.value]
let doc: HasFormatter;
doc = new ListItem(...values)
list.render(doc, listItem.value, 'start')
})
listItemBtn.addEventListener('onclick', (e: Event) => {
e.preventDefault();
???
}
ListTemplate.ts
import { HasFormatter } from "../interfaces/HasFormatter.js";
export class ListTemplate {
constructor(private container: HTMLUListElement){}
render(item: HasFormatter, heading: string, position: 'start' | 'end'){
const li = document.createElement('li');
const p = document.createElement('p');
const btn = document.createElement('button');
btn.appendChild(document.createTextNode('x'));
btn.setAttribute('id', 'listItemBtn')
p.innerText = item.format();
li.append(p);
li.append(btn)
if (position === 'start'){
this.container.prepend(li);
} else {
this.container.append(li);
}
}
remove() {
???
}
}
ListItem.ts
import { HasFormatter } from '../interfaces/HasFormatter.js'
export class ListItem implements HasFormatter { //ensuring that all structures follow HasFormatter structure
constructor(
public listItem: string
) {}
format() {
return `${this.listItem}`
}
}
HasFormatter.ts
export interface HasFormatter {
format(): string
}
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TypeScript Tutorial</title>
<!-- <link rel="stylesheet" href="styles.css"> -->
</head>
<body>
<header>
<form id="newForm">
<div class="field">
<label>List item</label>
<input type="text" id="listItem">
</div>
<button>Add</button>
</form>
<div class="wrapper">
<!-- output list -->
<ul class="item-list">
</ul>
</div>
</header>
<script type="module" src='app.js'></script>
</body>
</html>
I'm not prety sure but I think that shoul be some like
listItemBtn.addEventListener('onclick', (e: Event) => {
//get the "<li>" where the button is
const element=e.target.parent;
//remove it
element.parent.removeChild(element);
}

Nested component does not render properly in Svelte/Sapper

I have three files inside a slug. I use slug parameters as directory name.
The problem I am having is everything except the each loop in taglist. For some reason it does not receive the prop tagList. Any help would be appreciated.
index.svelte
<script context="module">
export function preload({ params }, { user }) {
let [id, slug] = [params.id, params.slug];
return { id, slug };
}
</script>
<script>
import Editor from "../../../_components/Editor.svelte";
import Topics from "./Topics.svelte";
import { stores } from "#sapper/app";
export let id;
export let slug;
const { session } = stores();
</script>
<svelte:head>
<title />
</svelte:head>
<div class="editor-page">
<div class="container page">
<div class="row">
<div class="col-md-8 offset-md-2 col-xs-12">
<Topics {id} {slug} />
{#if $session.user}
<Editor />
{/if}
</div>
</div>
</div>
</div>
Topics.svelte
<script>
import { onMount } from "svelte";
import * as api from "api.js";
import "bytemd/dist/index.min.css";
import TagList from "../../../_components/_TagList.svelte";
export let id;
export let slug;
let topics = [];
let title = "";
let tagList = [];
let value = "";
let Viewer = null;
onMount(async () => {
const bytemd = await import("bytemd");
Viewer = bytemd.Viewer;
const response = await api.get(
`t/${id}/${slug}`,
localStorage.getItem("jwt")
);
console.log(response);
if (response.topic) {
topics = response.topic;
title = response.title;
value = topics[0].description;
for(let i= 0; i < response.tags.length; i++) {
tagList.push(response.tags[i]);
}
}
});
</script>
<div>
<h3>{title}</h3>
<hr/>
<svelte:component this={Viewer} {value} />
<TagList {tagList} />
</div>
_TagList.svelte
<script>
export let tagList;
console.log(tagList);
</script>
<ul>
{#each tagList as tag}
<p>hello</p>
<li>{tag.name}</li>
{/each}
</ul>
In Svelte, updates are only triggered with an assignment.
In your case that means that when the component is rendered it will render an empty taglist (tagList = []).
Now in onMount you do taglist.push, but as said earlier, this doesn't trigger an update (remember that this function is called after the component has mounted) because it is not an assignment.
There are four ways to fix it in your case:
after the for loop you do tagList = tagList, this is an assignment and will trigger the update.
instead of doing the for loop use a mapping tagList = response.tags.map(tag => tag)
instead of doing the for loop you spread the tags into the taglist tagList = [...response.tags]
considering you don't do anything with the tags anyway, and tagList is empty and you don't seem to have any other way to update, just assign the tags to it directly tagList = response.tags
Of course your code might be simplified, if you actually do something with each tag before adding it to the list case 3 and 4 are not good options, for that scenario I would use the map option

Uncaught ReferenceError: weight is not defined at HTMLButtonElement.letsCalculateBMI

I have this task where I am to build a BMI calculator on specific instructions. I seem to have followed all the instructions except one in letsCalculateBMI. The instruction says:
letsCalculateBMI and get it to obtain the selected value from the SELECT element, pass that value to a getSelectedUser function call, which should return the user object for the selected value. This user object should be assigned to a user variable.
My confusion stems from how to getSelectedUser function call inside letsCalculateBMI to return the user object and the user object assigned to a user.
For a quicker view in computeBMI arrow function, the user parameter is an immediately destruct to weight, height, and country properties.
Currently the error I have is Uncaught ReferenceError: weight is not defined at HTMLButtonElement.letsCalculateBMI
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Mini App</title>
</head>
<body>
<div class="select">
<select class="select-text">
<option disabled selected>Select User</option>
</select>
</div>
<div class="details mdc-elevation--z3">
<p>
<span class="prop" data-age>Age :</span>
<span class="value" data-age-value>23 years</span>
</p>
<p>
<span class="prop" data-height>Height :</span>
<span class="value" data-height-value>169cm</span>
</p>
<p>
<span class="prop" data-weight>Weight :</span>
<span class="value" data-weight-value>68kg</span>
</p>
<p>
<span class="prop" data-gender>Gender :</span>
<span class="value" data-gender-value>Female</span>
</p>
<p>
<span class="prop" data-country>Country :</span>
<span class="value" data-country-value>Nigerian</span>
</p>
</div>
<button id="oracle" class="mdc-button" onclick="letsCalculateBMI()">
Calculate BMI
</button>
<div id="outcome">
<h5 class="mdc-typography--headline5">
BMI
</h5>
<p class ="bmi-text"></p>
</div>
<script>
const users = [];
const countriesWithLowerBmi = ["Chad", "Sierra Leone", "Mali", "Gambia", "Uganda", "Ghana", "Senegal", "Somalia", "Ivory Coast", "Isreal"];
const featToMeter = 0.3048;
const bmiCountryRatio = 0.82;
const computeBMI = ({weight, height, country}) => {
const heightInMeters = height * featToMeter;
let BMI = weight / (heightInMeters^2);
if (countriesWithLowerBmi.includes(country))
BMI *= bmiCountryRatio;
return Math.round(BMI, 2);
};
const getSelectedUser = (userId) => {
return users.find(({id}) => id === userId);
};
const displaySelectedUser = ({target}) => {
const user = getSelectedUser(target.value);
const properties = Object.keys(user);
properties.forEach(prop => {
const span = document.querySelector(`span[data-${prop}-value]`);
if(span) {
span.textContent= user[prop];
}
})
}
const letsCalculateBMI = () => {
const value = document.querySelector('.select-text').value;
getSelectedUser(value);
const user = {weight, height, country}
const bmi = computeBMI(user);
document.querySelector('.bmi-text').innerHTML = bmi
};
const powerupTheUI = () => {
const button = document.querySelector('#oracle');
const select = document.querySelector('.select-text');
select.addEventListener('change', displaySelectedUser);
button.addEventListener('click',letsCalculateBMI);
};
const displayUsers = (users) => {
users.forEach(user => {
const select = document.querySelector('.select-text');
const option = document.createElement('option');
option.text = user.name;
option.value = user.id;
select.appendChild(option);
});
};
const fetchAndDisplayUsers = () => {
users.push(
{
age: 40,
weight: 75,
height: 6,
country: 'Nigeria',
name: 'Charles Odili',
id: 'dfhb454768DghtF'
},
{
age: 23,
weight: 68,
height: 6,
country: 'Nigeria',
name: 'Simpcy',
id: 'gibb12erish'
}
);
displayUsers(users);
};
const startApp = () => {
powerupTheUI();
fetchAndDisplayUsers();
};
startApp();
</script>
</body>
</html>
The error tells you all you need to know: weight isn't defined because, well, you haven't defined it (same goes for height and country). These are properties of the user, so you need to get them from the user object returned by getSelectedUser.
For example:
user = getSelectedUser(value);
computeBMI(user.weight, user.height, user.country);
This should fix your problem, but....
For a quicker view in computeBMI arrow function, the user parameter is an immediately destruct to weight, height, and country properties.
In my opinion, this isn't good OOP design - you've already got an object with all that info; why the need to write more code to split it up?
What I'd rather do would be something like this:
...
computeBMI(getSelectedUser(value));
...
const computeBMI = (user) => {
const heightInMeters = user.height * featToMeter;
let BMI = user.weight / (heightInMeters^2);
if (countriesWithLowerBmi.includes(user.country))
BMI *= bmiCountryRatio;
return Math.round(BMI, 2);
};
letsCalculateBMI should be:
const letsCalculateBMI = () => {
const value = document.querySelector('.select-text').value;
const user = getSelectedUser(value);
const bmi = computeBMI(user);
document.getElementById("bmi-text").innerHTML = bmi;
};

Categories