HTML Multiple select not returning array - html

I'm trying to make a web application using express and I'm trying to make a form to create a new product. In there I have multiple select to select the categories for the product.
Here is my code for the multi-select:
<div class="form-group row mb-3">
<label for="categories" class="col-form-label sol-sm-2">Categories:</label>
<div class="col-sm-10">
<select multiple name="product[categories[]]" id="categories" class="form-control">
<% for (let category of categories) { %>
<option value="<%= category.id %>">
<%= category.name %>
</option>
<% } %>
</select>
</div>
</div>
The target is for it to fill in the products.categories field with an array of categories.
But this is what I receive on the server:
{
product: {
name: 'Nvidia GeForce RTX 4090',
price: '1499.99',
description: 'State-of-the-art GPU. Recently released with unrivalled performance. Take your gaming to the next level!',
discountedPrice: '',
stock: '5'
},
'product[categories': [ '63a18514fade00e8bc7f4d1c' ]
}
It doesn't seem to want to format the array properly and I'm unsure as to why this is.
I've already tried removing the second brackets in the name field to get
product[categories]
But then it doesn't return an array when just a single item is selected.
Is there an alternative way of me doing this or should I just use the above and convert it to an array on the server?

It works without extra server-side code if you change the name to
<select multiple name="product[categories]">
(without the nested brackets). Then the request
GET path?product[categories]=a&product[categories]=b
will be parsed into
req.query = {"product":{"categories":["a","b"]}}
and the request
POST path
Content-Type: application/x-www-form-urlencoded
product[categories]=a&product[categories]=b
will be parsed into
req.body = {"product":{"categories":["a","b"]}}
if you use the body-parsing middleware express.urlencoded({extended: true}) with the extended: true option.

Related

How to iterate two separate arrays in handlebars Nodejs with {{#each}}?

I am creating a Risk Management System using Nodejs and Express with Handlebar views. Using SQL, I am extracting two queries in my GET router and rendering them to the handlebar view. This is how it looks:
const parametro = await pool.query('select * from parametro where parid = ?', [parid]);
const tipoParametros = await pool.query('select distinct tipoparid from tipoparametro order by tipoparid');
res.render('parametros/modificar', {parametro, tipoParametros});
I consoled logged both arrays and they look like these:
- parametro: [
RowDataPacket {
parid: 22,
parcodigo: 'TEST',
tipoparid: 11,
pardescripcion: 'Just a Test',
parexplicacion: 'Test',
parclasificacion: 3,
parvalor: 'Test 1',
parusrcreaid: null,
parfechacrea: 2022-06-25T21:58:19.000Z,
parusrmodid: null,
parfechamod: null
}
]
- tipoParametros: [ RowDataPacket { tipoparcodigo: 'SYS', tipoparid: 11 } ]
However, since my POST for this view needs :id, I am iterating with each parametro because that's how I keep track of each row in my database, with parid. The .hbs view looks like this:
{{#each parametro}}
<form class = "row g-3 needs-validation" method = "POST" action = "/parametros/modificar/{{this.parid}}" novalidate>
{{> formparametro}}
</form>
{{/each}}
and inside "formparametro" there is a form-floating section where I try to iterate with each tipoParametro but nothing is coming out. Looks like this:
<div class="col-6">
<div class="form-floating">
<select class="form-select" id="floatingSelect" name="tipoparid" aria-label="Floating label select example" required>
{{#each tipoParametros}}
<option value={{this.tipoparid}}>{{this.tipoparcodigo}}</option>
{{/each}}
</select>
<label for="floatingSelect">Tipo Parametro</label>
</div>
</div>
How can I have access to the query and columns of tipoParametro?
As your formparametro partial is rendered within your {{#each parametro}} block, the context for each time the partial is rendered is the currently iterated item from parametro. Therefore, the partial has no access to tipoParametros.
What we want to do is provide a context to our partial that does contain tipoParametros. This is very simple. The Handlebars documentation covers Partial Contexts and it states that a context can be supplied to the partial as the second parameter within the mustache brackets, as in:
{{> myPartial myOtherContext }}
As your tipoParametros object belongs to your root context, I would recommend using the #root data variable to pass the root context to your partial. The line in your template invoking your partial thus becomes:
{{> formparametro #root}}
I have created a fiddle for your reference.

rails select_tag, how to generate dropdown list with json value and custom text from an array

I'ved got a json array and i want to create a dropdown selection list. And because its json, i dont want to display json but based on a readable name.
I tried 2 methods that came the closest, but cant get what i want.
controller:
#books = [{"code"=>"PA1","name"=>"James","type"=>"Novel"},{"code"=>"PA2","name"=>"John","type"=>"Science"}]
method 1
form.html.erb:
<%= select "book", "book", #books.each_with_index.map {|name, index| [name,name["name"]]} %>
the generated html:
<select id="book_book" name="book[book]"><option code="PA1" name="James" type="Novel" value="James">James</option>
<option code="PA2" name="John" type="Science" value="John">John</option></select></div>
method 2
form.html.erb:
<%= select_tag "book", options_for_select(#books) %>
the generated html:
<select id="book" name="book"><option value="{"code"=>"PA1","name"=>"James", "type"=>"Novel"}">{"code"=>"PA1", "name"=>"James", "type"=>"Novel"}</option><option value="{"code"=>"PA2", "name"=>"John", "type"=>"Science"}">{"code"=>"PA2", "name"=>"John", "type"=>"Science"}</option></select> </div>
method 3 (update new method not working as well)
Even this does not work, there is 2 different "value"! Getting more frustrated.
#books = [{"value" => {"code"=>"PA1","name"=>"James","type"=>"Novel"}},{"value" => {"code"=>"PA2","name"=>"John","type"=>"Science"}}]
<%= select "book", "book", #books.each_with_index.map {|value, index| [value,value["value"]["name"]]} %></div>
<select id="book_book" name="book[book]"><option value="James" value="{"code"=>"PA1", "name"=>"James", "type"=>"Novel"}">James</option>
<option value="John" value="{"code"=>"PA2", "name"=>"John", "type"=>"Science"}">John</option></select></div>
This is the desired result I want, Please HELP!:
<select id="book_book" name="book[book]"><option value="{"code"=>"PA1","name"=>"James","type"=>"Novel"}">James</option>
<option value="{"code"=>"PA2","name"=>"John","type"=>"Science"}">John</option></select></div>
I don't know why you wanna pass whole hash as string in value of your <option>
But if you want to generate this
<select id="book_book" name="book[book]"><option value="{"code"=>"PA1","name"=>"James","type"=>"Novel"}">James</option>
<option value="{"code"=>"PA2","name"=>"John","type"=>"Science"}">John</option></select></div>
Then you should write following
<%= select("book", "book", #books.each.map {|hash| [hash['name'], hash.to_s] }) %></div>

How can I save each item in a "select multiple" field in its own MySQL record (using Rails)?

Thanks for any help you can provide! I have a Ruby on Rails application where I am trying to save maps with driving directions and waypoints. The data needs to come straight out of the entry form instead of the Google Maps javascript. I've solved the starting and ending points, but the waypoints are giving me a problem.
My questions:
How can I save each waypoint in its own record in the Waypoint table? I'm able to get the first waypoint into the table, but the rest of the "select multiple" options are ignored.
How can I make each waypoint.newsavedmap_id field the same as its corresponding newsavedmaps id so that I can call these up later?
HTML
<p>Enter a street address, city, and state:
<input id="startinput" type="text" name="starthere" size="56"></p>
<p>Or, select a location from the list:
<select id="startdrop" name="startthere">
<option value="">
<% for masterlocation in #masterlocation %>
<option value="<%= masterlocation.street_address %> <%= masterlocation.city %>, <%= masterlocation.state %>, <%= masterlocation.zip %>"><%= masterlocation.place_name %></option>
<% end %>
</select></p>
<div><b>Stops</b></div>
<div id="multiselectdiv1">
<select multiple id="waypoints" name="waypointsselected">
<% for masterlocation in #masterlocation %>
<option value="<%= masterlocation.street_address %> <%= masterlocation.city %>, <%= masterlocation.state %>, <%= masterlocation.zip %>"><%= masterlocation.place_name %></option>
<% end %>
</select>
</div>
<b>End</b>
<p>Enter a street address, city, and state:
<input id="endinput" type="text" name="endhere" size="56"></p>
<p>Or, select a location from the list:
<select id="enddrop" name="endthere">
<option value="">
<% for masterlocation in #masterlocation %>
<option value="<%= masterlocation.street_address %> <%= masterlocation.city %>, <%= masterlocation.state %>, <%= masterlocation.zip %>"><%= masterlocation.place_name %></option>
<% end %>
</select></p>
</div>
<div>
<input type="submit" onclick="calcRoute();" id="showmapview" value="Show Map">
</div>
I have two MySQL tables. The first is newsavedmaps:
id
itinerary_id
start
start_lat
start_long
start_masterlocation_id
end
end_lat
end_long
end_masterlocation_id
name
The second is waypoints:
id
newsavedmap_id
waypoint
waypoint_lat
waypoint_long
waypoint_masterlocation_id
The two are meant to be connected by newsavedmaps.id and waypoint.newsavedmap_id .
My newsavedmap_controller.rb includes:
def create
#newsavedmap = Newsavedmap.new(params[:newsavedmap])
#newsavedmap.name = params[:newsavedmapname]
if !params[:starthere].blank?
#newsavedmap.start = params[:starthere]
else
#newsavedmap.start = params[:startthere]
end
if !params[:endhere].blank?
#newsavedmap.end = params[:endhere]
else
#newsavedmap.end = params[:endthere]
end
if !params[:waypointsselected].blank?
#waypoint = Waypoint.new(params[:waypoint])
#waypoint.waypoint = params[:waypointsselected]
end
Edit 1
In response to Colinm's suggestion to wrap the controller in an iterator to get separate records for each address, I tried this, but I'm pretty sure I'm doing the wrong thing:
if !params[:waypointsselected].blank?
for waypoint in #waypoint
#waypoint = Waypoint.new(params[:waypoint])
#waypoint.waypoint = params[:waypointsselected]
#waypoint.newsavedmap = #newsavedmap
end
end
How can I save each waypoint in its own record in the Waypoint table? I'm able to get the first waypoint into the table, but the rest of the "select multiple" options are ignored.
Unless you have a really good reason not to, use Rails' form helpers instead of rolling your own form fields. They will take care of this automatically, as well as handle edge cases you may not even know about. (Like the gotcha that sending a multiple select back with no selections will leave the record unchanged. That doesn't sound like it's an issue in your use case, but Rails still has your back if you use the form helpers.)
In your case, you'd simply pass multiple: true in the html_options hash, like so:
<%= f.select :waypointsselected,
MasterLocation.waypoints_for_select,
{},
{ multiple: true }
%>
Note that this does assume you implement a waypoints_for_select method. Without seeing all the code, it looks like you have way too much logic in your view right now. It's brittle and verbose to construct the options array in the view; offloading that to the model or a helper keeps your view code cleaner and helps eliminate potential future bugs.
If you absolutely can't/absolutely don't want to use form helpers in this application, the key you're looking for is []. Rails sensibly assumes a form field represents a single value unless you explicitly denote it as an array. Just tack an empty array on the end of the field name:
<select multiple id="waypoints" name="waypointsselected[]">
...and that field's key in the params hash will instead contain an array of values representing the selected items:
{ waypointsselected: ["3", "6", "9"] }
As for associating the waypoints with the newsavedmaps, just set it explicitly during creation. You're almost there:
if !params[:waypointsselected].blank?
#waypoint = Waypoint.new(params[:waypoint])
#waypoint.waypoint = params[:waypointsselected]
# You can do it like this...
#waypoint.newsavedmap = #newsavedmap
# Or using the ID...
#waypoint.newsavedmap_id = #newsavedmap.id
end
(I haven't adapted this code to deal with the fact you now have an array of values in waypointsselected; I just plugged the relationship definitions into your existing code. Remember to adjust your code to expect an array and iterate over it.)

How can I ensure that my Backbone forms return data that's formatted the same way as it came in?

In short:
When using Backbone and Underscore templates, what's the best way to ensure that the data in a form is formatted in the exact same way when POSTed to the server as it was when it was initially fetched?
Longer question:
I'm currently using Backbone’s fetch() to retrieve some data from the server as JSON. On success I'm taking that JSON and using the data in an Underscore template like so:
<div class="module-content">
<form>
<div class="customer-primary">
<% if (ParentCompany) { %>
<div class="row">
<div class="label">Parent Company</div>
<div class="value">
<div class="current-value"><%= ParentCompany %></div>
<div class="editable-value"><input name="ParentCompany" value="<%= ParentCompany %>"></div>
</div>
</div>
<% } %>
<% if (Title) { %>
<div class="row">
<div class="label">Title</div>
<div class="value">
<div class="current-value"><%= Title %></div>
<div class="editable-value"><input name="Title" value="<%= Title %>"></div>
</div>
</div>
<% } %>
…
</div>
</form>
</div>
The JSON has a number of children with multiple entries, like this:
{
"UserID":"12345",
"FirstName":"Brandon",
"Ship": {
"Address1":"33 One Two Ave",
"Address2":"#23D",
"Address3":"",
"City":"New York",
"State":"NY",
"Country":"United States",
"Zip":"10023"
},
"Phones": [
{
"Kind":"Tel",
"Number":"512-123-4567"
},
{
"Kind":"Fax",
"Number":"512-123-4567"
}
]
}
How can I ensure that I build the form out in such a way that it returns an object that's formatted in the same way for easy DB updates?
Please let me know if you need more info!
If I understand your question, you're trying to make sure the JSON data structure matches what the server expects when you're sending data. (And coincidentally, that it's in the same structure that you received from the server.)
What you want to do is override the model's toJSON function so your data is serialized as expected. Then, when it gets persisted by Backbone.sync, the proper data structure will be sent to the remote API.
Take a look at these:
Saving Backbone model and collection to JSON string
backbone.js: overwritting toJSON
I think the best you can do is to validate your model. and make the fields that you need to be sent required, if you need all of them ,then validate your entire model.
This plugin is a good option for this task.
https://github.com/fantactuka/backbone-validator

How to Get Model Data from Partial View?

I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..