Standardize Dropdown's Selected Handlebars Helper - node.js

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 :)

Related

Stuck in dependent drop down menu

I got stuck while creating a dependent drop down menu in react. I am trying to make a form in which user can select three options through the drop down menu respectively and based on the options selected the 4th field which is a disabled text field will be populated with the appropriate result.
This is my code.
<div className="col-md-4">
<label htmlFor="inputEmail4" className="form-label">District</label>
<select className="form-select" value={selectedDistrict} onChange={handleDistrict} aria-label="Select District" >
<option value="Select District">Select District</option>
<option value={Data.data[0].DISTRICT}>{Data.data[0].DISTRICT}</option>
</select>
</div>
<div className="col-md-4">
<label htmlFor="inputPassword4" className="form-label">Assembly Constituency Number & Name.</label>
<select className="form-select" value={selectedConstituencyNameandNumber} onChange={handleConstituencyNameandNumber} aria-label="Select Assembly Constituency Number & Name." >
<option style={{"display": "block"}} value="Select Assembly Constituency Number & Name.">Select Assembly Constituency Number & Name.</option>
{selectedDistrict && acBooth.map((acBooth, index) => (
<option key={index} value={acBooth['AC No & Name']}>{acBooth['AC No & Name']}</option>
))}
</select>
</div>
<div className="col-md-4">
<label htmlFor="inputEmail4" className="form-label">Booth No.</label>
<select className="form-select" value={selectedBoothNumber} onChange={handleBoothNumber} aria-label="Select Booth No." >
<option value="Select Booth No.">Select Booth No.</option>
{selectedDistrict && partNumber.map((item, index) => (
<option key={index} value={item["Part No"]}>{item["Part No"]}</option>
))}
</select>
</div>
This is the code for 3 drop down menu. I want to execute a function which takes two parameter from the selected options in drop down menu and then display the output in the blank text field whose code is given below.
<div className="col-md-4">
<label htmlFor="inputEmail4" className="form-label">Booth Name</label>
<input type="text" disabled className="form-control" value={boothName} onChange={()=>{
const newName = getBoothName(selectedConstituencyNameandNumber, selectedBoothNumber);
setBoothName(newName);
console.log(newName);
}} id="inputUID" />
</div>
These are the function which I have written. It is working properly in the backend but I am unable to get the output in frontend.
const getBoothName = (acNo, partNo) => {
const boothName = Data.data.filter((item) => {
return item["AC No & Name"] === acNo && item["Part No"] === partNo
})
return boothName[0]["Part Name English"]
}
const [selectedDistrict, setSelectedDistrict] = useState('');
const [selectedConstituencyNameandNumber, setSelectedConstituencyNameandNumber] = useState('');
const [selectedBoothNumber, setSelectedBoothNumber] = useState('');
const handleDistrict = (e) => {
setSelectedDistrict(e.target.value);
}
const handleConstituencyNameandNumber = (e) => {
setSelectedConstituencyNameandNumber(e.target.value);
}
const handleBoothNumber = (e) => {
setSelectedBoothNumber(e.target.value);
}
A help is really appreciated.
The onChange function of your Input Textfield should be on the 3rd Select field. After that on change of your third dropdown it will setBoothName(newName) and your value will be set in the input textfield.
<div className="col-md-4">
<label htmlFor="inputEmail4" className="form-label">
Booth No.
</label>
<select
className="form-select"
value={selectedBoothNumber}
onChange={handleBoothNumber}
aria-label="Select Booth No."
>
<option value="Select Booth No.">Select Booth No.</option>
{selectedDistrict &&
partNumber.map((item, index) => (
<option key={index} value={item["Part No"]}>
{item["Part No"]}
</option>
))}
</select>
</div>
Change handleBoothNumber function in your code like this
const handleBoothNumber = (e) => {
setSelectedBoothNumber(e.target.value);
setBoothName(getBoothName(selectedConstituencyNameandNumber, selectedBoothNumber));
}

How can I add selected attribute to option that I select?

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>

How to load products based on category selection that the user selected using mongoose and nodejs

Here is the scenario: the client choosed category type then in the products selection i want to load only the products that belongs to this category
what i tried is to use one function to get all categories which will return all categories
and the other one is to get the productCategory based on the category selection that the user select it. but that gives me error in the ejs file that productCategory the variable that i passed in the second function is not defined.
the callback function
exports.makeABill = (req, res, next) => {
console.log('get Bill page');
categoryDB.find({}, function(err, data) {
res.render('bill/makeABill.ejs', { categories: data });
});
// categoryDB.findOne(req.body.categoryName, function(err, result) {
// res.render('bill/makeABill.ejs', { productCategory: result });
// });
}
the two input
<label for="categoryName"> Category : </label>
<select class="form-control form-control-sm" id="categoryName" autofocus>
<option value="">Select Category</option>
<% categories.forEach(function (category) {%>
<option> <%= category.categoryName %> </option>
<%})%>
</select>
<br>
<!--product-->
<label for="productsCategory"> Product Name : </label>
<select class="form-control form-control-sm" id="productsCategory" name="productsCategory" autofocus>
<option value="">Select Product</option>
<% productCategory.forEach(function (category) {%>
<option> <%= category.productsCategory %> </option>
<%})%>
</select>
<br>
Try this for filter {columName : values}
categoryDB.findOne({categoryName : req.body.categoryName}, function(err, result) {
res.render('bill/makeABill.ejs', { productCategory: result });
});

HTML DOM Select not working as expected on lit-html

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.

Cannot POST error using Express, Mongoose, EJS

(For the solution check the last answer)
I'm creating object with rating system with default value of 0. What I'm trying to do is to update the rating value by choosing one of the options in the <select> dropdown menu. The problem is when I'm submiting the selected value it sends the 'Cannot POST / ...' error.
Here's the app.js code with post and get requests:
// Updating Single Object
app.post('project/:projectId', (req, res) => {
const project = {};
project.rating = req.body.rating;
Project.update({_id: request.params.projectId}, project, (err) =>{
if(err){
console.log(err);
return;
} else {
res.redirect('/project/:projectId');
}
});
});
// Route To Single Project
app.get('/project/:projectId', (req, res) => {
const requestedProjectId = req.params.projectId;
Project.findOne({_id: requestedProjectId}).populate('image_file').exec((err, project) => {
res.render('project', {
project: project
});
});
});
And the form:
<form action="/project/<%= project.id %>" method="post">
<select class="" name="rating">
<option value="0">Rate us</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
<input type="submit" value="Vote">
</form>
I think I'm missing something but can't figure it out.
Thank you in advance.
Just change your url in routes, use /project/:projectId instead of project/:projectId in app.post() method
Solved it after 3 hours, realizing I was missing the / before project in the url route.
app.post('project/:projectId', (req, res) => {
Maybe instead of:
<form action="/project/<%= project.id %>" method="post">
you should use:
<form action="/project/<%= project._id %>" method="post">

Resources