In addition to this question
I am trying to map individually a state to another state to store the amountToPay object to get the sum of it. The problem is every time it renders the onChange function. It stores every state as object as you can see here: .
What I want to happen is to only get [434] instead of ['','4','43','434']
So I can .reduce the array to get the sum.
My method on storing the array object to another state is this
const [amountToPay, setAmountToPay] = useState("")
console.log("AMOUNT TO PAY", amountToPay)
useEffect(() => {
serviceItem.map((item) => (
setAmountToPay([...amountToPay, item.amountToPay])
))
}, [serviceItem])
useEffect(() => {
serviceItem.map((item) => (
setAmountToPay([...amountToPay, item.amountToPay])
))
}, [serviceItem])
You can check the whole code here CodeSandbox code.Any help is appreciated :)
There are several things I suggest you to do:
Add some id property to your serviceItem. You can use UUID, nanoid, or even Date.now()
Remove const [amountToPay, setAmountToPay] = useState([]);
Use values directly from serviceItem collection. In order to do this you need to create onChange handler, it will be something like this
const handleChange = (id) => (nextAmount) => {
setServiceList(prevValue => {
return prevValue.map(item => item.id === id ? { ...item, amount: nextAmount } : item)
})
}
And amount to pay can be easily got from serviceItem collection, without effects or other states
const procedurePriceTotal = serviceItem.reduce(
(acc, item) => (acc = acc + item.amount),
0
);
this is happening because you are setting serviceItem on onChange method
and use passed serviceItem as deps array to useeffect in which you are setting amountToPay.
so on every change it's appending in array
Rather then setting amount in useEffect, make a method and call on remove/add button so it will only call after user is finished typing. you can also place a button 'Save' or 'calculate Amount' and call handleSetAmountToPAY method which will update amount.
import React, { useState, useMemo, useEffect } from "react";
export default function App() {
//Values
const [serviceItem, setServiceList] = useState([
{ serviceValue: "", quantityValue: "", amountToPay: "" }
]);
console.log("SERVICE ITEM", serviceItem);
//Add item function
const handleItemAdd = () => {
setServiceList([
...serviceItem,
{ serviceValue: "", quantityValue: "", amountToPay: "" }
]);
handleSetAmountToPAY(serviceItem)
};
//Remove item function
const handleItemRemove = (index) => {
const list = [...serviceItem];
list.splice(index, 1);
setServiceList(list);
handleSetAmountToPAY(list)
};
//Get Values
const handleGetValues = (e, index) => {
const { name, value } = e.target;
const list = [...serviceItem];
list[index][name] = value;
setServiceList(list);
};
//Saving state to another state
const [amountToPay, setAmountToPay] = useState([]);
console.log("AMOUNT TO PAY", amountToPay);
const handleSetAmountToPAY = (list) => {
list && list.map((item) =>
setAmountToPay([...amountToPay, item.amountToPay])
);
}
//Add total amount
const procedurePriceTotal = amountToPay.reduce(
(index, value) => (index = index + value),
0
);
console.log("TOTAL PRICE", procedurePriceTotal);
return (
<div className="App">
{serviceItem.map((singleItem, index) => (
<div class="row form-row">
<div class="col-12 col-md-6 col-lg-4">
<div class="form-group">
<label>
Service <span class="text-danger">*</span>
</label>
<input
name="serviceValue"
type="text"
class="form-control"
value={singleItem.serviceValue}
placeholder="Tooth Extraction"
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="form-group">
<label>
Quantity <span class="text-danger">*</span>
</label>
<input
name="quantityValue"
type="text"
class="form-control"
placeholder="1"
value={singleItem.quantityValue}
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-3">
<div class="form-group">
<label>
Amount (₱)<span class="text-danger">*</span>
</label>
<input
name="amountToPay"
type="number"
class="form-control"
placeholder="500"
value={singleItem.amountToPay}
onChange={(e) => {
handleGetValues(e, index);
}}
/>
</div>
</div>
<div class="col-12 col-md-6 col-lg-2">
<div class="add-more">
<br />
{serviceItem.length !== 1 && (
<button
type="submit"
onClick={() => handleItemRemove(index)}
className="btn btn-primary rx-pr"
>
<i className="fas fa-plus" /> Remove Item
</button>
)}
</div>
</div>
</div>
))}
{/* Add Item */}
<div className="add-more-item rx-pr">
<button
type="submit"
onClick={handleItemAdd}
className="btn btn-primary rx-pr"
>
<i className="fas fa-plus" /> Add Item
</button>
</div>
</div>
);
}
I was doing it the wrong way.
I solved it by mapping the serviceItem then using reduce to get the sum instead of putting it again into a separate array of object then mapping it again to get the sum.
const newNumberArray = serviceItem.map(function(item) {
return parseInt(item.amountToPay)
})
const totalAmountPaid = newNumberArray.reduce((index,value) => index = index + value, 0 )
Thanks for all the help and suggestion!
When I select an option, I want to remove selected attribute from old one and add it to option I selected.
HTML Code:
<label>Gender</label>
<select id="gender" name="gender" class="form-control" onchange="redirect()">
<option value="male">Male</option>
<option value="female">Female</option>
<option value="any">Any</option>
</select>
JS Code:
<script>
$("#gender").on("change", function redirect() {
// const queryStr = "gender=any&platform=any"
// const usp = new URLSearchParams(queryStr)
// console.log(usp)
url = window.location.href.split("?")[0] + "?gender=" + this.value + "&platform=<%=platform%>"
location.replace(url)
})
</script>
I'm facing the following challenge building some dropdowns with Handlebars.
Instead of modeling the dropdown's select this way:
<div class="form-floating col-3">
<select name="sport" class="form-select" id="floatingSelect">
<option {{selectedSport sport 'all'}} value="all">All</option>
<option {{selectedSport sport 'Hiking'}} value="Hiking">Hiking</option>
<option {{selectedSport sport 'Mountaineering'}} value="Mountaineering">Mountaineering</option>
<option {{selectedSport sport 'Climbing'}} value="Climbing">Climbing</option>
<option {{selectedSport sport 'Mountain biking'}} value="Mountain biking">Mountain biking</option>
</select>
<label for="floatingSelect">Sport</label>
</div>
<div class="form-floating col-2">
<select name="difficulty" class="form-select" id="floatingSelect">
<option {{selectedDifficulty difficulty 'all'}} value="all">All</option>
<option {{selectedDifficulty difficulty 'Easy'}} value="Easy">Easy</option>
<option {{selectedDifficulty difficulty 'Medium'}} value="Medium">Medium</option>
<option {{selectedDifficulty difficulty 'Hard'}} value="Hard">Hard</option>
<option {{selectedDifficulty difficulty 'Expert'}} value="Expert">Expert</option>
</select>
<label for="floatingSelect">Difficulty</label>
</div>
I would like to use the same helper for both of them, but I still don't know how to do it. (btw, ignore the fact that I should be using an {{#each}} for displaying the options :P)
The controller:
module.exports.list = (req, res, next) => {
const filters = req.query;
const { location, sport, difficulty } = req.query;
const criterial = Object.keys(filters)
.filter((key => filters[key] !== 'all'))
.reduce((criterial, filter) => {
if (filters[filter]) criterial[filter] = filters[filter];
return criterial;
}, {});
Route.find(criterial)
.then(routes => {
res.render('routes/list', { routes, location, sport, difficulty })
})
.catch(next)
}
The helpers:
hbs.registerHelper('selectedSport', (option, sportValue) => {
return option === sportValue ? ' selected' : '';
})
hbs.registerHelper('selectedDifficulty', (option, difficultyValue) => {
return option === difficultyValue ? ' selected' : '';
})
Many thanks for your help! :)
Finally I solved it this way:
Instead of typing all the options directly on the HBS, I create these with a helper that renders each option.
<div class="form-floating col-3">
<select name="sport" class="form-select" id="floatingSelect">
{{#each sportOptions as |sportOption|}}
{{option ../sport sportOption}}
{{/each}}
</select>
<label for="floatingSelect">Sport</label>
</div>
<div class="form-floating col-2">
<select name="difficulty" class="form-select" id="floatingSelect">
{{#each difficultyOptions as |difficultyOption|}}
{{option ../difficulty difficultyOption}}
{{/each}}
</select>
<label for="floatingSelect">Difficulty</label>
</div>
The helper receives 2 parameters: sport and sportOption and according to their value, returns a string cointaining the option with the selected attribute or not.
hbs.registerHelper('option', function (selectedValue, value) {
const selectedProperty = value == selectedValue ? 'selected' : '';
return new hbs.SafeString(`<option value=${value} ${selectedProperty}>${value}</option>`);
});
sportOption and difficultyOption are arrays with all the possibilities inside them.
I hope that, if someone else has the same problem, this can be of any help :)
I have a form which has a input field called admissionNumber and the button. In input field when user enter number and click the button then function getAllStudent filter the an array . If admission number match with entered number then other fields (fullname and faculty) automatically filled . How can I do this ? Please someone help me to do this . Thank you
getAllStudents function which return students details (admissionNumber,fullname,faculty)
getAllStudents(user._id, token).then((data) => {
if (data.error) {
setValues({ ...values, error: data.error });
} else {
setValues(data);
}
});
form fields
<input
type="text"
onChange={(event) => {
setSearchTerm(event.target.value);
}}
className="form-control offset-md-2 col-md-6"
placeholder="Admission Number"
required
maxLength="5"
/>
<button
// onClick={}
className="btn rounded ml-4"
>
Verify
</button>
</div>
<div className="bg-dark rounded">Personal Details</div>
<div className="row form-group ">
<input
type="text"
name="studentFullName"
className="form-control mt-2 offset-md-2 col-md-8"
placeholder="Student Name"
/>
<input
type="text"
name="faculty"
className="form-control mt-2 offset-md-2 col-md-8"
/>
</div>
You should pass a function to button onClick prop.
Assuming you using a functional component and a state with students, currentUser and searchTerm you can do something like that:
const [students] = useState([...])
const [currentUser, setCurrentUser] = useState(undefined)
const [searchTerm, setSearchTerm] = useState(undefined)
const checkStudent = () => {
const match = students.find(student => student.admissionNumber === searchTerm)
if(match) {
setCurrentUser(match)
}
}
return (
<>
<button
onClick={() => checkStudent()}
/>
<input
type="text"
name="studentFullName"
className="form-control mt-2 offset-md-2 col-md-8"
placeholder="Student Name"
value={currentUser?.fullname}
/>
</>
)
I'm not sure if there's something wrong with my code or it's an existing bug with lit-html?
I'm trying to get value of the selected item when button is clicked.
number.js
<select class="form-control" name="number">
<option ?selected=${this.number === 1} value="1">One</option>
<option ?selected=${this.number === 2} value="2">Two</option>
<option ?selected=${this.number === 3} value="3">Three</option>
</select>
<button type="button" #click=${getSelectedValue}>Get Selected Value</button>
getSelectedValue() {
const selectOptions = this.querySelector('select[name="number"]');
const selectedValue = selectOptions.options[selectOptions.selectedIndex].value;
}
I've tried selectOptions.value but I still can't get the selected value.
Note that, I've added createRenderRoot() {return this;} to disable the shadowDOM.