Let me clarify the title a bit.
I am a model which has a collection of trailers, and in turn each collection has a list of trailers. The html looks something like this
for(int i = 0; i <Model.TrailerCollection.Count(); i++)
{
... // some html
#for(int j = 0; j < Model.TrailerCollection[i].Trailers.Count(); j++)
{
<div>
#Html.CheckBoxFor(m => m.TrailerCollection[i].Trailers[j].SelectedTrailer, new {data_id = Model.TrailerCollection[i].Trailers[j].TrailerTypeId}
</div>
}
}
Now this outputs the html as I require with a list of items with a checkbox next to each one, but the problem arises when I post the model back to the controller.
If I have a list of say 10 checkboxes and check them all, when the model is passed to the controller the first one is always false, even when checked, but the others are fine.
Below is the JS for clarity.
var data = $("#form1").seralize
$.ajax({
url: 'xxxx',
data:data,
type:'post'
success: function(result)
{...}
});
My controller snippet
public async Task<HttpResponseMessage> Update(VM model)
I am just a little confused as to why the first one is always false.
Any help would be very much appreciated.
Rendered HTML
<div class="row">
<div class="col-lg-8"> Trl_86_dd_1 </div>
<div class="col-lg-4">
<input data-id="11" id="TrailerCollection_0__Trailers_0__SelectedTrailer" name="TrailerCollection[0].Trailers[0].SelectedTrailer" value="true" type="checkbox">
<input name="TrailerCollection[0].Trailers[0].SelectedTrailer" value="false" type="hidden">
</div>
<input data-val="true" data-val-number="The field TrailerTypeId must be a number." id="TrailerCollection_0__Trailers_0__TrailerTypeId" name="TrailerCollection[0].Trailers[0].TrailerTypeId" value="11" type="hidden">
<input id="TrailerCollection_0__Trailers_0__Name" name="TrailerCollection[0].Trailers[0].Name" value="Trl_86_dd_1" type="hidden">
</div>
<div class="row">
<div class="col-lg-8">Trl_86_dd_2</div>
<div class="col-lg-4">
<input checked="checked" data-id="26" id="TrailerCollection_0__Trailers_1__SelectedTrailer" name="TrailerCollection[0].Trailers[1].SelectedTrailer" value="true" type="checkbox">
<input name="TrailerCollection[0].Trailers[1].SelectedTrailer" value="false" type="hidden">
</div>
<input data-val="true" data-val-number="The field TrailerTypeId must be a number." id="TrailerCollection_0__Trailers_1__TrailerTypeId" name="TrailerCollection[0].Trailers[1].TrailerTypeId" value="26" type="hidden">
<input id="TrailerCollection_0__Trailers_1__Name" name="TrailerCollection[0].Trailers[1].Name" value="Trl_86_dd_2" type="hidden">
</div>
Related
Hi I'm trying to make a Razor page with a simple form that add the new element into a list, but the OnPost() method is never calling. I have a breakpoint on the first line of the method and it never hits, but in the OnGet() it works.
This is my OnPost() method:
public void OnPost()
{
newDocument = Int32.Parse(Request.Form[nameof(newDocument)]);
newAge = Int32.Parse(Request.Form[nameof(newAge)]);
newTriage = (Triage)Int32.Parse(Request.Form[nameof(newTriage)]);
newName = Request.Form[nameof(newName)];
newGender = Request.Form[nameof(newGender)];
newSymptoms = Request.Form[nameof(newSymptoms)];
var newPatient = new Patient
{
Document = newDocument,
Name = newName,
Age = newAge,
Gender = newGender,
Triage = newTriage,
Symptoms = newSymptoms
};
patients.Add(newPatient);
OrderPatients();
}
And the razor page have this form:
<form>
<div class="form-group">
<label for="patientDoc">Documento</label>
<input asp-for="newDocument" type="number" class="form-control" id="patientDoc" placeholder="ingrese documento">
</div>
<div class="form-group">
<label for="patientName">Nombre</label>
<input asp-for="newName" type="text" class="form-control" id="patientName" placeholder="Nombre">
</div>
<div class="form-group">
<label for="patientAge">Edad</label>
<input asp-for="newAge" type="number" class="form-control" id="patientAge" placeholder="Edad">
</div>
<div class="form-group">
<label for="patientGender">Género</label>
<input asp-for="newGender" type="text" class="form-control" id="patientGender" placeholder="Género">
</div>
<div class="form-group">
<label for="patientTri">Prioridad</label>
<select asp-for="newTriage" class="form-control" id="patientTri">
#foreach (Triage tri in Enum.GetValues(typeof(Triage)))
{
<option>#tri</option>
}
</select>
</div>
<div class="form-group">
<label for="patientSymp">Sintomas</label>
<input asp-for="newSymptoms" type="text" class="form-control" id="patientSymp" placeholder="Sintomas">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
I have read that you have to put this line #addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers at the beginning of the page, but I did it and still the OnPost doesn't hit.
Anyone knows what is wrong?
The default method type for form is GET, so it hits your OnGet handler because you didn't specify the method. Change <form> to <form method="post"> and it should work.
Forget it. I just had to add the method="form" at the beginning of the form like this:
<form method="post">
. . .
</form>
Gonna leave this around here for another newbie like me who might need it.
This question already has answers here:
How to make Check Box List in ASP.Net MVC
(2 answers)
Closed 2 years ago.
I have a list of checkboxes and I need to tell which are checked using C#. How would I tell which individual checkboxes are checked and then add their values together? I am using Asp.Net Core, if that could be helpful
<div class="row">
<form method="post" class="offset-3 col-5 border">
<div id="Colors" class="form-group" type="checkbox">
<label class="form-check-label">Colors</label>
<div class="form-check">
<input id="Red" class="form-check-input" type="checkbox" value="1">
<label class="form-check-label">Red</label>
</div>
<div class="form-check">
<input id="Yellow" class="form-check-input" type="checkbox" value="2">
<label class="form-check-label">Yellow</label>
</div>
<div class="form-check">
<input id="Green" class="form-check-input" type="checkbox" value="4">
<label class="form-check-label">Green</label>
</div>
<div class="form-check">
<input id="Blue" class="form-check-input" type="checkbox" value="8">
<label class="form-check-label">Blue</label>
</div>
<div class="form-group">
<input type="submit" value="Post" class="btn btn-primary" />
</div>
</div>
</form>
</div>
If you are using Jquery and you want to get all checkboxses are checked then you can do what you need like below :
var selected = [];
$('.form-check-input input:checked').each(function() {
selected.push($(this).attr('name'));
});
if you are using another Javascript librarry, you should do same.
if you want to get them in your Controller, so you should follow these steps:
1- Add a name to your Checkbox like (here chose color) :
<input id="Red" class="form-check-input" name="color" type="checkbox" value="1">
2- Add a property to your Model
public class AddPostModel
{
public List<int> Color { get; set; }
}
In this way, when you post your view, you have all values from checkboxes are checked.
I have a dynamic in line form in my ASP.NET MVC application, which when a user clicks a button, in this case 'add', it will add a new row to the form with the required fields. I am trying to get this to work with ASP.Net Identity, but not having any luck.
I believe it would be similar to having a list, and then doing a for a loop through the list to register the users, but putting it into practice is proving to be confusing.
<input name="__RequestVerificationToken" type="hidden" value=""> <div class="row">
<div class="form-group mx-1">
<label class="control-label" for="Email">Email</label>
<input name="Email" class="form-control" data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="The Email field is required." id="Email" placeholder="Email" type="text" value="">
</div>
<div class="form-group mx-1">
<label class="control-label" for="User_Role">User Role</label>
<select name="UserRoles" class="form-control" data-val="true" data-val-required="The UserRoles field is required." id="UserRoles" ><option value="">Role</option>
<option value="Employee">Employee</option>
<option value="Manager">Manager</option>
</select>
</div>
<div class="form-group mx-1">
<label class="control-label" for="Password">Password</label>
<input name="Password" class="form-control valid validate-equalTo-blur" data-val="true" data-val-length="The Password must be at least 6 characters long." data-val-length-max="100" data-val-length-min="6" data-val-required="The Password field is required." id="Password" placeholder="Password" type="password" value=""aria-describedby="Password-error" aria-invalid="false">
</div>
<div class="form-group mx-1">
<label class="control-label" for="ConfirmPassword">Confirm password</label>
<input class="form-control valid" name="ConfirmPassword" data-val="true" data-val-equalto="The password and confirmation password do not match." data-val-equalto-other="*.Password" id="ConfirmPassword" placeholder="Confirm Password" type="password" value="" aria-describedby="ConfirmPassword-error" aria-invalid="false">
</div>
<button type="button" class="btn btn-sm btn-info add_button form-control col-md-1" style="margin-top: 37px"><i class="fas fa-plus"></i></button>
<div class="field_wrapper">
<div class="form-group row">
<fieldset class="form-group mx-1">
<label class="control-label" for="Email">Email</label>
<input name="Email" class="form-control" data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="The Email field is required." id="Email" placeholder="Email" type="text" value="">
</fieldset>
<fieldset class="form-group mx-1">
<label class="control-label" for="User_Role">User Role</label>
<select name="UserRoles" class="form-control" data-val="true" data-val-required="The UserRoles field is required." id="UserRoles" ><option value="">Role</option>
<option value="Employee">Employee</option>
<option value="Manager">Manager</option>
</select>
</fieldset>
<fieldset class="form-group mx-1">
<label class="control-label" for="Password">Password</label>
<input name="Password" class="form-control" data-val="true" data-val-length="The Password must be at least 6 characters long." data-val-length-max="100" data-val-length-min="6" data-val-required="The Password field is required." id="Password" placeholder="Password" type="text" value="">
</fieldset>
<fieldset class="form-group mx-1">
<label class="control-label" for="ConfirmPassword">Confirm password</label>
<input name="ConfirmPassword" class="form-control" data-val="true" data-val-equalto="The password and confirmation password do not match." data-val-equalto-other="*.Password" id="ConfirmPassword" placeholder="Confirm Password" type="text" value="">
</fieldset>
<i class="fas fa-times center"></i>
</div>
</div>
<div class="row">
<button type="submit" class="btn btn-info my-2">Submit</button>
<a type="button" class="btn btn-warning my-2 ml-1" href="/Home">Back</a>
</div>
Jquery Form:
$(document).ready(function () {
var max_fields = 10; //maximum input boxes allowed - change as needed
var wrapper = $(".field_wrapper"); //Fields wrapper
var add_button = $(".add_button"); // class add button
var remove_button = $('.remove_button'); // class remove button
var html = `
<div class="form-group row">
<fieldset class="form-group mx-1">
#Html.LabelFor(m => m.Email, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Email, new { #class = "form-control", placeholder="Email" })
</fieldset>
<fieldset class="form-group mx-1">
#Html.Label("User Role", new { #class = "control-label" })
#Html.DropDownList("UserRoles", (SelectList)ViewBag.Name, "Role", new { #class = "form-control"})
</fieldset>
<fieldset class="form-group mx-1">
#Html.LabelFor(m => m.Password, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Password, new { #class = "form-control", placeholder="Password" })
</fieldset>
<fieldset class="form-group mx-1">
#Html.LabelFor(m => m.ConfirmPassword, new { #class = "control-label" })
#Html.TextBoxFor(m => m.ConfirmPassword, new { #class = "form-control", placeholder="Confirm Password" })
</fieldset>
<i class="fas fa-times center"></i>
</div>`;
var x = 1; //initlal count
$(add_button).click(function (e) { //on add button click
e.preventDefault();
if (x < max_fields) { //max input box allowed
x++; // increment value of x
$('.counter').text(x);
$(wrapper).append(html); //add html
}
});
$(wrapper).on("click", remove_button, function (e) { // runs when a user clicks on anything with the class 'remove_button'
e.preventDefault(); // prevent default, duh
$(this).parent('div').remove(); // get parent of each element and remove it
x--; // decrement the value of x
$('.counter').text(x); // update text with the count only after value of x has been changed
})
});
My AccountController method - I have an employee class which inherits the identity model and also uses its own view model:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RegisterEmployees(EmployeeViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
Create a View model that will house the list of type UserViewModel. I used EmployeeListViewModel;
public class EmployeeListViewModel{
public List<EmployeeViewModel> UsersToRegister {get;set;}
}
Change the model declaration on the top of your view to;
#model EmployeeListViewModel
Use this for your form. We need to define the input fields statically because we need to use the 0 index on the name like Model.UsersToRegister[0].Email;
// please edit with your parameters
#Html.BeginForm(){
// add validate forgery token html helper
<div class="form-group mx-1">
<label class="control-label" for="Email">Email</label>
<input name="Model.UsersToRegister[0].Email" class="form-control" data-val="true" data-val-email="The Email field is not a valid e-mail address." data-val-required="The Email field is required." id="Email" placeholder="Email" type="text" value="">
</div>
<div class="form-group mx-1">
<label class="control-label" for="User_Role">User Role</label>
<select name="Model.UsersToRegister[0].UserRoles" class="form-control" data-val="true" data-val-required="The UserRoles field is required." id="UserRoles" >
<option value="">Role</option>
<option value="Employee">Employee</option>
<option value="Manager">Manager</option>
</select>
</div>
<div class="form-group mx-1">
<label class="control-label" for="Password">Password</label>
<input name="Model.UsersToRegister[0].Password" class="form-control valid validate-equalTo-blur" data-val="true" data-val-length="The Password must be at least 6 characters long." data-val-length-max="100" data-val-length-min="6" data-val-required="The Password field is required." id="Password" placeholder="Password" type="password" value=""aria-describedby="Password-error" aria-invalid="false">
</div>
<div class="form-group mx-1">
<label class="control-label" for="ConfirmPassword">Confirm password</label>
<input class="form-control valid" name="Model.UsersToRegister[0].ConfirmPassword" data-val="true" data-val-equalto="The password and confirmation password do not match." data-val-equalto-other="*.Password" id="ConfirmPassword" placeholder="Confirm Password" type="password" value="" aria-describedby="ConfirmPassword-error" aria-invalid="false">
</div>
<button type="button" class="btn btn-sm btn-info add_button form-control col-md-1" style="margin-top: 37px"><i class="fas fa-plus"></i></button>
}
Update your script to use this, what we did here is we're using x as a counter. The counter will indicate which index (in a list of objects) does a certain input field belong to;
<script>
var x = 1; //initlal count
$(add_button).click(function (e) { //on add button click
e.preventDefault();
if (x < max_fields) { //max input box allowed
var html = "
<div class='field_wrapper'>
<div class='form-group row'>
<fieldset class='form-group mx-1'>
<label class='control-label' for='Email'>Email</label>
<input name='Model.UsersToRegister["+x+"].Email' class='form-control' data-val='true' data-val-email='The Email field is not a valid e-mail address.' data-val-required='The Email field is required.' id='Email' placeholder='Email' type='text' value=''>
</fieldset>
<fieldset class='form-group mx-1'>
<label class='control-label' for='User_Role'>User Role</label>
<select name='Model.UsersToRegister["+x+"].UserRoles' class='form-control' data-val='true' data-val-required='The UserRoles field is required.' id='UserRoles' ><option value=''>Role</option>
<option value='Employee'>Employee</option>
<option value='Manager'>Manager</option>
</select>
</fieldset>
<fieldset class='form-group mx-1'>
<label class='control-label' for='Password'>Password</label>
<input name='Model.UsersToRegister["+x+"].Password' class='form-control' data-val='true' data-val-length='The Password must be at least 6 characters long.' data-val-length-max='100' data-val-length-min='6' data-val-required='The Password field is required.' id='Password' placeholder='Password' type='text' value=''>
</fieldset>
<fieldset class='form-group mx-1'>
<label class='control-label' for='ConfirmPassword'>Confirm password</label>
<input name='Model.UsersToRegister["+x+"].ConfirmPassword' class='form-control' data-val='true' data-val-equalto='The password and confirmation password do not match.' data-val-equalto-other='*.Password' id='ConfirmPassword' placeholder='Confirm Password' type='text' value=''>
</fieldset>
<a href='#' class='btn btn-sm btn-danger remove_button form-control col-md-1' style='margin-top: 37px'><i class='fas fa-times center'></i></a>
</div>
</div>
";
x++; // increment value of x
$('.counter').text(x);
$(wrapper).append(html); //add html
}
});
</script>
Then use this for your controller action, we replaced the parameter with the new class;
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RegisterEmployees(EmployeeListViewModel model)
{
if (ModelState.IsValid)
{
foreach(var u in model.UsersToRegister){
var user = new ApplicationUser { UserName = u.Email, Email = u.Email };
var result = await UserManager.CreateAsync(user, u.Password);
if(!result.Succeeded){
break; // depends on you if you want to stop creating users if one failed
// continue; // depends on you if you want to continue creating users if one failed
}
}
if (result.Succeeded)
{
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
I'm pretty sure the previous answer to this would work, but you commented on one of my other answers asking to look at this question, so here you go:
I've tried to keep it as simple as possible, so you're gonna have to add in your own extra markup, styling, data annotations etc.
Models
Pretty self explanatory, we just have a model for the view that has a list of user models.
public class RegisterLotsModel
{
public List<UserToRegister> UsersToRegister { get; set; }
}
public class UserToRegister
{
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
View
Just a simple form with some inputs for the fields to register the users. The surrounding divs are referenced in the JS and are used when we append the HTML for the extra users.
#model BulkRegister.Models.RegisterLotsModel
#using (Html.BeginForm("BulkRegister"))
{
<div class="users-to-register">
<div class="user-container">
<input type="text" name="UsersToRegister[0].UserName" />
<input type="email" name="UsersToRegister[0].Email" />
<input type="password" name="UsersToRegister[0].Password" />
</div>
</div>
<input type="button" id="add-user-button" value="Add another user" />
<input type="submit" value="Register the users" />
}
#section scripts {
<script src="~/Scripts/bulk-reg.js"></script>
}
JQuery
I put this in a separate file, but you can just pop it at the bottom of the view if you like. We count how many user-container classes are already on the page to work out what the index for the HTML we're adding should be. This means we don't have to keep track with a separate variable.
$(function () {
$('#add-user-button').click(function () {
// how many users are there already
var userContainerCount = $('.user-container').length;
// this count is used for the index for the next user container
var nextIndex = userContainerCount;
// build up some html. it's far simpler to just use regular html here, rather than razor.
// if you really want to use razor then look into partial views and fetching via ajax.
// you could use es6 backticks here to create a multi line string, but there seemed to
// be a little confusion in the comments, so this is just a plain concatenated string.
var html = '<div class="user-container">' +
'<input type="text" name="UsersToRegister[' + nextIndex + '].UserName" />' +
'<input type="email" name="UsersToRegister[' + nextIndex + '].Email" />' +
'<input type="password" name="UsersToRegister[' + nextIndex + '].Password" />' +
'</div>';
// append the html
$('.users-to-register').append(html);
});
});
Controller Actions
GET: Nothing fancy.
public ActionResult BulkRegister()
{
return View();
}
POST: Loop over the users in the model and just register them with the UserManager. You'll need to add validation and whatever you want to do to report successes and failures.
[HttpPost]
public async Task<ActionResult> BulkRegister(RegisterLotsModel model)
{
// do whatever validation you want
if (!ModelState.IsValid)
return View(model);
// loop over your users and register them in the regular way
var successful = new List<string>();
var failed = new List<string>();
foreach (var toRegister in model.UsersToRegister)
{
var user = new ApplicationUser { UserName = toRegister.UserName, Email = toRegister.Email };
var result = await UserManager.CreateAsync(user, toRegister.Password);
if (result.Succeeded)
successful.Add(toRegister.UserName);
else
failed.Add(toRegister.UserName);
}
// do whatever you want to do with failures
if (failed.Any())
return RedirectToAction("FailedRegistration", new { failed });
// maybe register some more users?
return RedirectToAction("BulkRegister");
}
And that's it. If you've got a lot of users to register you might want to just build up the user models and insert them straight into the database in a single transaction.
I have a number of radio button groups, called Properties. A property can be things like "colour", "size", "height", and so on. The properties have options. For "colour", the options could be "Red", "Blue", and so on. When I'm posting my form, I'm able to read the Id for each individual radio button, but I haven't been able to access the group-Ids.
I'm rendering the properties like this:
for (int i = 0; i < Model.Properties.Count(); i++)
{
<div class="col-lg-3 col-md-4 col-sm-6">
<strong>#Model.Properties[i].Label</strong><br />
#foreach (var option in Model.Properties[i].Options)
{
<label style="font-weight:unset;">
#Html.RadioButtonFor(m => m.Properties[i].SelectedRadioOption, option.Id, new { id = Model.Properties[i].Id })
#option.Value
#Model.Properties[i].Unit
</label>
<br />
}
#Html.ValidationMessageFor(m => m.Properties[i].SelectedRadioOption)
</div>
}
This produces HTML like this:
<div class="col-lg-3 col-md-4 col-sm-6">
<strong>Size</strong><br />
<label style="font-weight:unset;">
<input checked="checked" data-val="true" data-val-required="The Selected property field is required." id="7012" name="Properties[0].SelectedRadioOption" type="radio" value="7025" />
34 - 36
</label>
<br />
<label style="font-weight:unset;">
<input id="7012" name="Properties[0].SelectedRadioOption" type="radio" value="7026" />
37 - 39
</label>
<br />
<span class="field-validation-valid" data-valmsg-for="Properties[0].SelectedRadioOption" data-valmsg-replace="true"></span>
</div>
<div class="col-lg-3 col-md-4 col-sm-6">
<strong>Colour</strong><br />
<label style="font-weight:unset;">
<input data-val="true" data-val-required="The Selected property field is required." id="7013" name="Properties[1].SelectedRadioOption" type="radio" value="7029" />
Black
</label>
<br />
<label style="font-weight:unset;">
<input id="7013" name="Properties[1].SelectedRadioOption" type="radio" value="7036" />
Pink
</label>
<br />
<span class="field-validation-valid" data-valmsg-for="Properties[1].SelectedRadioOption" data-valmsg-replace="true"></span>
</div>
This is the section in my controller's POST-method where I iterate through the Properties:
List<PropertyOptionForProduct> propertyOptions = new List<PropertyOptionForProduct>();
if (VMProduct.Properties != null)
{
for (var i = 0; i < VMProduct.Properties.Count(); i++)
{
propertyOptions.Add(new PropertyOptionForProduct
{
ProductId = VMProduct.Id,
ProductPropertyId = VMProduct.Properties[i].Id, // <-- This is always 0!
ProductPropertyOptionId = (int)VMProduct.Properties[i].SelectedRadioOption
});
}
}
If I select "Black" on the "Colour"-property, the object looks like this:
PropertyOptionForProduct
{
ProductId = 17008,
ProductPropertyId = 0, // <-- Why not 7013?
ProductPropertyOptionId = 7025
}
The correct value for ProductPropertyId in the above example should be 7013, which is the id-value on the radio button tag.
What am I doing wrong?
The format of what is posted back from a html <form> will be (for example) name1=value1&name2=value2 this maps to the name and the value field of your checked radiobuttons. Your code might for example example generate this post Properties[0].SelectedRadioOption=7026&Properties[1].SelectedRadioOption=7036.
The server has no idea about the id attribute in your radiobutton html. The id field is not included in the post unless you alter the post (using javascript) before it is sent. So either you need to include all identifiers in the value field perhaps separated like value="1243;34534" or you need to use javascript to include the id in the post data before it is posted.
Turns out, I'm a noob.
All I needed to add to make it work was this line of razor-code in the view:
#Html.HiddenFor(m => m.Properties[i].Id)
Now I can access the value of Properties[i].Id in the controller.
The form fields do not return the value of the form even thought the asp-controller and asp-action is stated.
The form does go to the right controller function and returns the right view, however, it does the form object is NULL.
#using ActionAugerMVC.Models
#model Tuple<IEnumerable<Cities>,Content,IEnumerable<Content>,Quote>
#addTagHelper "*,Microsoft.AspNetCore.Mvc.TagHelpers"
<div class="sidebar__item">
<p class="subheading">Instant Quote Request</p>
<form class="register__form" role="form" asp-controller="Plumbing" asp-action="QuoteForm" method="post">
<div class="text-danger" asp-validation-summary="All"></div>
<div class="form-group">
<label class="sr-only">Full Name </label>
<input asp-for="#Model.Item4.FullName" type="text" class="form-control" placeholder="Full name">
</div>
<div class="form-group">
<label class="sr-only">Your phone</label>
<input asp-for="#Model.Item4.Phone" type="tel" class="form-control" placeholder="Your phone">
<span asp-validation-for="#Model.Item4.Phone" class="text-danger"></span>
</div>
<div class="form-group">
<label class="sr-only">E-mail</label>
<input asp-for="#Model.Item4.Email" type="email" class="form-control" placeholder="E-mail">
<span asp-validation-for="#Model.Item4.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label class="sr-only">Your Message</label>
<input asp-for="#Model.Item4.Message" type="text" class="form-control" placeholder="Your Message">
</div>
<input type="submit" value="Get a Quote Now" class="btn btn-accent btn-block">
</form>
</div> <!-- .sidebar__item -->
And the Controller looks like this, with the Quote object being null.
The hard coded, values appear correctly in the view, but the Quote object returned by the form is null.
[HttpPost]
public IActionResult QuoteForm(Quote quote)
{
if (ModelState.IsValid)
{
/* quote.FullName = "Umar Aftab";
quote.Email = "test#email.com";
quote.City = "Calgary";
quote.Message = "Test Message";
quote.Phone = "6474543769";
*/
}
return View(quote);
}
The issue is your use of a Tuple as your view's model combined with asp-for. For example, with something like:
<input asp-for="#Model.Item4.FullName" type="text" class="form-control" placeholder="Full name">
The name of the input is going to end up as Item4.FullName. However, your action accepts only Quote, which means the modelbinder needs the input to be named just FullName in order to bind it properly. You either need to accept the same model the view uses (though I've never tried posting a Tuple so not sure if that will even work), or you can use a partial view to work around the issue.
Essentially, you just would need to move all the fields related to just Quote to a partial view. Then, in this view, you can include them via:
#Html.Partial("_QuoteFields", Model.Item4)
That should be enough psych Razor out enough to just name the fields like FullName instead of Item4.FullName. If it's not, then you may need to reset the HtmlFieldPrefix, via:
#Html.Partial("_QuoteFields, Model.Item4, new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "" } })
The model on your view is different than the model you're trying to bind it to in the controller. You're not going to get the key/value pairs to match up
Put your razor in a partial view with Quote as the model and try that.
_QuoteForm.cshtml
#model Quote
<div class="form-group">
<label class="sr-only">Full Name </label>
<input asp-for="FullName" type="text" class="form-control" placeholder="Full name">
</div>
<div class="form-group">
<label class="sr-only">Your phone</label>
<input asp-for="Phone" type="tel" class="form-control" placeholder="Your phone">
<span asp-validation-for="Phone" class="text-danger"></span>
</div>
<div class="form-group">
<label class="sr-only">E-mail</label>
<input asp-for="Email" type="email" class="form-control" placeholder="E-mail">
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label class="sr-only">Your Message</label>
<input asp-for="Message" type="text" class="form-control" placeholder="Your Message">
</div>
OriginalView.cshtml
using ActionAugerMVC.Models
#model Tuple<IEnumerable<Cities>,Content,IEnumerable<Content>,Quote>
#addTagHelper "*,Microsoft.AspNetCore.Mvc.TagHelpers"
<div class="sidebar__item">
<p class="subheading">Instant Quote Request</p>
<form class="register__form" role="form" asp-controller="Plumbing" asp-action="QuoteForm" method="post">
<div class="text-danger" asp-validation-summary="All"></div>
#Html.Partial("_QuoteForm", Model.Item4)
<input type="submit" value="Get a Quote Now" class="btn btn-accent btn-block">
</form>
</div> <!-- .sidebar__item -->