Retrieve object from select drop down list - c#

I am trying to return a meetingSpace object from the last form group in this razor page:
<form method="post">
<input type="hidden" asp-for="Booking.Id" />
<div class="form-group">
<label asp-for="Booking.Name"></label>
<input asp-for="Booking.Name" class="form-control" />
<span class="text-danger" asp-validation-for="Booking.Name"></span>
</div>
<div class="form-group">
<label asp-for="Booking.Start">Start</label>
<input asp-for="Booking.Start" class="form-control" />
<span class="text-danger" asp-validation-for="Booking.Start"></span>
</div>
<div class="form-group">
<label asp-for="Booking.Duration">Duration</label>
<input asp-for="Booking.Duration" class="form-control" />
<span class="text-danger" asp-validation-for="Booking.Duration"></span>
</div>
<div class="form-group">
<label asp-for="Booking.MeetingSpace">Room</label>
<select data-value="true" data-va-required="The meeting space field is required" id="MeetingSpace.Id" name="MeetingSpace.Name">
#foreach (var meetingSpace in Model.meetingSpaces)
{
<option value="meetingSpace">#meetingSpace.Name</option>
}
</select>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
I am unsure how set this select dropdown to return the value to the Booking.MeetingSpace property. I can see the list of meetingSpace objects in the drop down but after selecting an option the meetingSpace field in the created/edited object is not returned as with the other properties in the form. I have also tried the same using the Booking.MeetingSpaceId int as well but it is still not populated after submission of the form.

it could be a binding problem try to change the name attribute value like this:
<select data-value="true" data-va-required="The meeting space field is required" id="Booking.MeetingSpace.Id" name="Booking.MeetingSpace.Name">
#foreach (var meetingSpace in Model.meetingSpaces)
{
<option value="#meetingSpace.Id">#meetingSpace.Name</option>
}
</select>
also option value should start with # to be recognized.

Related

How do I pass an entire object using an HTML form?

I'll only include the relevant properties of the model and the related lines for simplicity:
Model: Department.cs
Property: public virtual Staff HOD { get; set; }
ControllerMethod:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Add([Bind] Department newDepartment)
{
await _departmentRepository.AddDepartmentAsync(newDepartment);
await _departmentRepository.SaveTransactionAsync(true);
return Redirect("Details" + "/" + newDepartment.Id);
}
View:
#model Department
#inject IStaffRepository staffRepository
#{
// select list
var listOfEmployees = new List<Staff>();
foreach (var staff in await staffRepository.GetAllStaffAsync())
{
listOfEmployees.Add(staff);
}
var selectListHOD = new SelectList(listOfEmployees, "EmployeeId", "Name"); //populates the option element with the correct Employee Id
}
<form class="form-group" method="post">
<div class="d-flex flex-column">
<div class="input-group" style="margin-bottom: 1%">
#Html.DropDownListFor(department => department.HOD, selectListHOD, htmlAttributes: new { #class="form-control" })
</div>
<div class="input-group">
<input class="btn btn-primary" type="submit" value="Save" />
</div>
</div>
</form>
Result:
(I have not included the other properties seen here in the question, but trust me, the properties of the parameter to the controller get correctly populated with them, except for the HOD property)
Source:
<form class="form-group" method="post">
<div class="d-flex flex-column">
<div class="input-group" style="margin-bottom: 1%">
<input class="form-control" id="Id" name="Id" placeholder="Id" type="text" value="" /> </div>
<div class="input-group" style="margin-bottom: 1%">
<input class="form-control" id="Name" name="Name" placeholder="Department" type="text" value="" /> </div>
<div class="input-group" style="margin-bottom: 1%">
<input class="form-control" id="Description" name="Description" placeholder="Description" type="text" value="" /> </div>
<div class="input-group" style="margin-bottom: 1%">
<select class="form-control" id="HOD" name="HOD">
<option value="EMP01">Employee 1</option>
<option value="EMP02">Employee 2</option>
</select>
</div>
<div class="input-group">
<input class="btn btn-primary" type="submit" value="Save" /> </div>
</div>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8AIwSWkSO0hJqMxy-oQKoeG35LTDmI2N1XHDZL9qeaxRxc17TyZbT2z9Iq0GMPkRyE7HnaX1r4ZSIs0bQATYB7w_A_HZDBXGETMmpdSqlMXZCmf7cH9ECzrNGz0Wuu9zHkE50yI92vPY-GxNPG-pRhs" />
</form>
My guess is that the value being supplied to the controller seems to be wrong. How would I pass the actual object instead of the ID?
I tried using this instead of EmployeeId, but it throws Object Reference not found exception.
You cannot bind an object with <select></select>.You can try to bind Hod.EmployeeId with <select></select>.And add a hidden input to bind Hod.Name.When selected value changes,change the value of hidden input.Here is a demo:
<form class="form-group" method="post">
<div class="d-flex flex-column">
<div class="input-group" style="margin-bottom: 1%">
#Html.DropDownListFor(department => department.HOD.EmployeeId, selectListHOD, htmlAttributes: new { #class = "form-control",#onChange= "AddName()" })
<input hidden asp-for="HOD.Name" />
</div>
<div class="input-group">
<input class="btn btn-primary" type="submit" value="Save" />
</div>
</div>
</form>
#section scripts
{
<script>
$(function () {
AddName();
})
function AddName() {
$("#HOD_Name").val($("#HOD_EmployeeId option:selected").text());
}
</script>
}
result:

Validation not working on cloned elements

If I create multiple elements like this...
#for (int i = 0; i < 10; i++)
{
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Railcars[i].RailcarNumber" class="control-label"></label>
<input asp-for="Railcars[i].RailcarNumber" class="form-control" />
<span asp-validation-for="Railcars[i].RailcarNumber" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label asp-for="Railcars[i].Weight" class="control-label"></label>
<input asp-for="Railcars[i].Weight" class="form-control" />
<span asp-validation-for="Railcars[i].Weight" class="text-danger"></span>
</div>
</div>
</div>
}
Validation appears to work correctly for all rows.
However, if I create a single row like this...
<div class="railcars">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Railcars[0].RailcarNumber" class="control-label"></label>
<input asp-for="Railcars[0].RailcarNumber" class="form-control" />
<span asp-validation-for="Railcars[0].RailcarNumber" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label asp-for="Railcars[0].Weight" class="control-label"></label>
<input asp-for="Railcars[0].Weight" class="form-control" />
<span asp-validation-for="Railcars[0].Weight" class="text-danger"></span>
</div>
</div>
</div>
</div>
And then clone rows using jQuery's clone(), validation only appears to work for the first (non-cloned) row.
Note: I am taking care to update all the ID, name, and for attributes of the cloned elements, and updating the subscript number. I checked that it's correct and posts the correct information to the server. ModelState even correctly detects validation problems with the cloned elements. It's just that client-side validation doesn't work on them.
Here is the HTML produced:
Uncloned Row (validation works):
<div class="row first-railcar">
<div class="col-md-4">
<div class="form-group railcar-number">
<label class="control-label" for="Railcars_0__Railcar">Railcar Number</label>
<input class="form-control" type="text" data-val="true" data-val-length="The field Railcar Number must be a string with a maximum length of 18." data-val-length-max="18" data-val-required="The Railcar Number field is required." id="Railcars_0__Railcar" maxlength="18" name="Railcars[0].Railcar" value="">
<span class="text-danger field-validation-valid" data-valmsg-for="Railcars[0].Railcar" data-valmsg-replace="true"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group railcar-volume">
<label class="control-label" for="Railcars_0__Volume">Volume (pounds)</label>
<input class="form-control" type="text" data-val="true" data-val-number="The field Volume (pounds) must be a number." data-val-required="The Volume (pounds) field is required." id="Railcars_0__Volume" name="Railcars[0].Volume" value="">
<span class="text-danger field-validation-valid" data-valmsg-for="Railcars[0].Volume" data-valmsg-replace="true"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="control-label"> </label><br>
<img class="add-railcar" src="/images/add.png" title="Add Additional Railcar" style="display: none;">
<img class="remove-railcar" src="/images/delete_2.png" title="Remove Railcar" style="display: none">
</div>
</div>
</div>
Cloned Row (validation doesn't work):
<div class="row">
<div class="col-md-4">
<div class="form-group railcar-number">
<label class="control-label" for="Railcars_1__Railcar">Railcar Number</label>
<input class="form-control" type="text" data-val="true" data-val-length="The field Railcar Number must be a string with a maximum length of 18." data-val-length-max="18" data-val-required="The Railcar Number field is required." id="Railcars_1__Railcar" maxlength="18" name="Railcars[1].Railcar" value="">
<span class="text-danger field-validation-valid" data-valmsg-for="Railcars[1].Railcar" data-valmsg-replace="true"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group railcar-volume">
<label class="control-label" for="Railcars_1__Volume">Volume (pounds)</label>
<input class="form-control" type="text" data-val="true" data-val-number="The field Volume (pounds) must be a number." data-val-required="The Volume (pounds) field is required." id="Railcars_1__Volume" name="Railcars[1].Volume" value="">
<span class="text-danger field-validation-valid" data-valmsg-for="Railcars[1].Volume" data-valmsg-replace="true"></span>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label class="control-label"> </label><br>
<img class="add-railcar" src="/images/add.png" title="Add Additional Railcar">
<img class="remove-railcar" src="/images/delete_2.png" title="Remove Railcar" style="">
</div>
</div>
</div>
I also carefully compared the cloned HTML above to the HTML created by my first example (with the for loop), and they are identical. Apparently, there is something different about it being added after the page has loaded.
Does anyone know how to clone elements this way and have validation work on all the cloned elements?
Update:
My use of jQuery's clone() includes two true values ($row.clone(true, true)). If I don't do this, the click handlers for my images don't work.
As recommended, I tried several variations of the following code after cloning the elements. But I couldn't get it to make any difference.
var form = document.getElementById('input-form');
$.validator.unobtrusive.parse(form);
You are on the right track with $.validator.unobtrusive.parse(form);. However for jQuery Validation to work on copied/appended with ajax/cloned elements you need three things.
Make sure every input element has a unique name.
Remove any previous validation bindings in the form of existing elements.
Re-create the validation bindings.
So here is a working example below.
<form>
<div class="container" id="ElementContainer">
<div id="ElementToClone">
<div class="row">
<div class="col col-md-4">
<div class="form-group">
<input class="form-control" type="text" name="elem[0]" id="elem_0" data-val="true" data-val-required="Required.">
</div>
</div>
</div>
</div>
</div>
<div class="pt-3">
<button type="button" class="btn btn-primary" onclick="Clone()">Clone</button>
<button type="submit" class="btn btn-primary ml-3">Submit</button>
</div>
</form>
<script>
function Clone() {
var $container = $('#ElementContainer');
var $elem = $('#ElementToClone');
var $form = $container.closest('form');
//duplicate x times
for (var i = 1; i <= 5; i++) {
var clonedHtml = $elem.html();
//create a unique name
clonedHtml = clonedHtml.replace('elem[0]', 'elem[' + i + ']');
//create an unique id (not required for validate to function)
clonedHtml = clonedHtml.replace('elem_0', 'elem_' + i);
//append the cloned element
$container.append(clonedHtml);
}
//remove validation from the inital input elements or it won't work
$form.removeData('validator').removeData('unobtrusiveValidation');
//bind the validation again
$.validator.unobtrusive.parse($form);
}
</script>
P.S. tested on MVC, not Core. But it should work the same since it is front-end anyways.

values are reset to zero

I'm doing insert via post, but my class is getting zero values.
The values of the inputs are passed via variable and are displayed correctly, but at the moment of action via post are coming with zero values.
The most interesting thing is if you type inside the input, then the values come correctly.
<form method="post">
<div class="col-sm-3">
<label>SALDO</label>
<div style="border:1px solid #bbb9b9; border-radius:3px;"></div>
<br />
<div class="form-group">
<label asp-for="Caixas.ValorFinalDinheiro" class="control-label"></label>
<input asp-for="Caixas.ValorFinalDinheiro" name="Caixas.ValorFinalDinheiro" id="Caixas.ValorFinalDinheiro" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalCheque" class="control-label"></label>
<input asp-for="Caixas.ValorFinalCheque" class="form-control finalFundo" disabled="disabled"/>
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalBoleto" class="control-label"></label>
<input asp-for="Caixas.ValorFinalBoleto" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalCartao" class="control-label"></label>
<input asp-for="Caixas.ValorFinalCartao" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinalDeposito" class="control-label"></label>
<input asp-for="Caixas.ValorFinalDeposito" class="form-control finalFundo" disabled="disabled" />
</div>
<div class="form-group">
<label asp-for="Caixas.ValorFinal" class="control-label"></label>
<input asp-for="Caixas.ValorFinal" class="form-control" style="background-color:#9FF781" />
</div>
</div>
<div class="col-sm-12">
<div class="form-group">
<input type="submit" value="Confirmar o Fechamento do Caixa" class="btn btn-primary btn-sm" />
<a asp-page="Index" class="btn btn-success btn-sm">Retorna ao Caixa</a>
</div>
</div>
</form>
And the controller
[BindProperty]
public Caixas Caixas { get; set; }
public async Task<IActionResult> OnPostAsync()
{
var C = _context.Caixas.Find(Caixas.Id);
C.fechado = true;
C.DataFinal = DateTime.Now;
C.HoraFinal = DateTime.Now;
C.FuncionarioFechamentoId = _userManager.GetUserId(HttpContext.User);
C.ValorFinalDinheiro = Caixas.ValorFinalDinheiro;
C.ValorFinalCheque = Caixas.ValorFinalCheque;
C.ValorFinalBoleto = Caixas.ValorFinalBoleto;
C.ValorFinalCartao = Caixas.ValorFinalCartao;
C.ValorFinalDeposito = Caixas.ValorFinalDeposito;
C.ValorFinal = Caixas.ValorFinal;
C.ValorSaida = Caixas.ValorSaida;
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
In fact, all browsers should not submit the disabled inputs.
Following this
In this example, the INPUT element is disabled. Therefore, it cannot
receive user input nor will its value be submitted with the form.
<INPUT disabled name="fred" value="stone">
I don't know why you have a form like that. But to overcome this situation, you can add an <input type="hidden"> element with the same name and same value as the disabled input. And send the value in POST request.
Try to use, the Data Annotation "HttpPost" before your method. Like this:
[HttpPost]
public async Task<IActionResult> OnPostAsync()
{
...
}

pass textbox value as parameter to controller route

I'm trying to use a textbox value in controller route and am having some trouble with the syntax. Essentially, I have my end users selecting a file and then submitting the data to the api to process and send the data off. I have working static code below, but can't get the xml path to be dynamically populated via the textbox value.
note that (as far as I can tell) the forward slash at the end of the path is required since there is a dot in the file path.
#using (Html.BeginForm("", "api/food/dummy food file.xml/"))
{
<div class="row">
<div class="col-lg-6">
<label class="control-label">Select File</label>
<div class="input-group">
<label class="input-group-btn">
<span class="btn btn-default">
Browse… <input type="file" style="display: none;" single>
</span>
</label>
<input id="food.filepath" name="food.filepath" type="text" class="form-control" readonly>
</div>
</div>
</div>
<br />
<div>
<button id = "btnSubmit" type="submit" class="btn btn-primary">Submit</button>
</div>
}
I don't what the syntax would be, but I can't get something like the below to work.
#using (Html.BeginForm("", "api/food/" + food.filepath + "/"))
{
<div class="row">
<div class="col-lg-6">
<label class="control-label">Select File</label>
<div class="input-group">
<label class="input-group-btn">
<span class="btn btn-default">
Browse… <input type="file" style="display: none;" single>
</span>
</label>
<input id="food.filepath" name="food.filepath" type="text" class="form-control" readonly>
</div>
</div>
</div>
<br />
<div>
<button id = "btnSubmit" type="submit" class="btn btn-primary">Submit</button>
</div>
}
Because the form is rendered on server and the value food.filepath is on client, you cannot mix them. Changing form action according to client values need to be done on client with javascript.
You can remove the file from action a add it on submit javascript action, for example by changing BeginForm to this:
#using (Html.BeginForm("", "api/food/", FormMethod.Get, new { onSubmit = "this.dataset.act = this.dataset.act||this.action; this.action = this.dataset.act + this['food.filepath'].value" } ))

Disabling Submit-button(s) when HTML Select is empty

I am a programmer-to-be, currently working on a project at work.
My problem is this: I have 3 submit buttons (on the same form), that each call a different action. These buttons HAVE to be disabled, if the HTML Selector (dropdown) does not have any options to choose from.
My form currently looks like this:
<form asp-action="Index" method="post" onkeypress="return event.keyCode != 13;">
<div class="col-md-9">
<div class="form-group form-inline">
<label asp-for="OrderNumber">Order number: </label>
<input asp-for="OrderNumber" class="form-control" type="text" placeholder="Insert order number" autofocus />
</div>
<div>
<label>Delete</label>
<input asp-for="Deletion" type="checkbox" />
</div>
<div>
<div class="col-md-4">
<input class="btn btn-block" type="submit" name="action" value="Receive" />
</div>
<div class="col-md-4">
<input class="btn btn-block" type="submit" name="action" value="Start" />
</div>
<div class="col-md-4">
<input class="btn btn-block" type="submit" name="action" value="Finish" />
</div>
</div>
</div>
<div class="col-md-3">
<div class="form-group form-inline">
<label asp-for="ProfileId">Profile: </label>
<select asp-for="ProfileId" class="form-control">
#foreach (var p in Model.Profiles)
{
<option value="#p.ProfileId">#p.Name</option>
}
</select>
</div>
</div>
</form>
Thanks for any help, and if you need more information, or if this question is poorly described, do not hesitate to ask! :)
Give your buttons an id. Set them to be disabled (enabled="false").
Call a javascript function when the dropdownlist is changed.
Check the selectedIndex of the dropdownlist in the function that is called onchange and set the button accordingly.
I could write a function that would do it, but you wouldn't learn anything. If you do a bit of research, have a stab at writing the function, I'll fix it if it doesn't work.
Simply disable the form fields that need be depending on if the others have the same conditional requirements. You can give them a custom class and use some javascript code and toggle the class with some JS like so.
toggleAttribute = function(className,index){
var eLs = [].slice.call(document.getElementsByClassName(className),0);
for(var i=0; i<eLs.length; i++){
eLs[i].disabled=(index==0 ? false : true); //index 0 is for 'Yes'
}
}
I came up with a solution of my own using Razor.
My form now looks like this:
<form asp-action="Index" method="post" onkeypress="return event.keyCode != 13;">
<div class="col-md-9">
<div class="form-group form-inline">
<label asp-for="OrderNumber">Order number: </label>
<input asp-for="OrderNumber" class="form-control" type="text" placeholder="Insert order number" autofocus />
</div>
<div>
<label>Delete</label>
<input asp-for="Deletion" type="checkbox" />
</div>
<div>
<div class="col-md-4">
<input class="btn btn-block" type="submit" name="action" value="Receive"
#{ if (Model.Profiles.Count() == 0) { #: disabled="disabled"
} } />
</div>
<div class="col-md-4">
<input class="btn btn-block" type="submit" name="action" value="Start"
#{ if (Model.Profiles.Count() == 0) { #: disabled="disabled"
} } />
</div>
<div class="col-md-4">
<input class="btn btn-block" type="submit" name="action" value="Finish"
#{ if (Model.Profiles.Count() == 0) { #: disabled="disabled"
} } />
</div>
</div>
</div>
<div class="col-md-3">
<div class="form-group form-inline">
<label asp-for="ProfileId">Profile: </label>
<select asp-for="ProfileId" class="form-control">
#foreach (var p in Model.Profiles)
{
<option value="#p.ProfileId">#p.Name</option>
}
</select>
</div>
</div>
</form>
I added an if-statement, that checks if there is any profiles in my list of profiles. If there is none (0), it adds the disabled="disabled" attribute to my inputs.
Thanks to anyone that tried to assist me in solving this, and providing me with suggestions for a solution :)

Categories