View model parameter is null in HttpPost action method - c#

I am redirecting my viewModel from the HttpPost of View1 to the HttpGet of View2.
This works without problems.
There the user has to accept the terms and agreemens.
This should get changed in the viewModel to true (before it is false).
And then redirect to the HttpPost of view2.
There something goes wrong.
The HttpPost ActionResult of View2 receives the viewModel with all Parameters as NULL (before they were filled)
How can I fix this?
Here is my HttpGet ActionResult for View2:
public ActionResult Verify(QuestionViewModel viewModel)
{
//Anrede in Viewbag
if (viewModelVeri.Per_Salutation == 2)
{
ViewBag.Per_Salutation = "Frau";
}
else
{
ViewBag.Per_Salutation = "Herr";
}
int? per_region_id = viewModelVeri.Per_Region;
int per_region_id_nullable = Convert.ToInt32(per_region_id);
Region region = _repository.GetRegionById(per_region_id_nullable);
QuestionViewModel viewModel2 = new QuestionViewModel()
{
Reg_Name = region.Reg_Name
};
//Regionsname in Viewbag
ViewBag.Reg_Name = viewModel2.Reg_Name;
return View(viewModel);
}
And here's my HttpPost ActionResult for View2:
[HttpPost]
public ActionResult Verify(QuestionViewModel viewModel, string tbButton)
{
//here the ViewModel-Parameters are already NULL
My View:
<div class="panel-body">
#using (Html.BeginForm("Verify", "QuestionForm", FormMethod.Post, new { id = "verifyform" }))
{
#Html.AntiForgeryToken()
<div class="ctrl-row">
<div class="form-group">
<div class="container-fluid">
#Html.LabelFor(model => model.Per_Salutation, new { #class = "control-label col-sm-1" })
<div class="col-sm-3">
#ViewBag.Per_Salutation
</div>
</div>
</div>
</div>
<div class="ctrl-row">
<div class="form-group">
<div class="container-fluid">
#Html.LabelFor(model => model.Per_Name_Last, new { #class = "control-label col-sm-1" })
<div class="col-sm-3">
#Html.DisplayFor(model => model.Per_Name_Last, new { #class = "control-label col-sm-1 non-zero-num" })
</div>
#Html.LabelFor(model => model.Per_Name_First, new { #class = "control-label col-sm-1" })
<div class="col-sm-3">
#Html.DisplayFor(model => model.Per_Name_First, new { #class = "control-label col-sm-1 non-zero-num" })
</div>
</div>
</div>
</div
<div class="ctrl-row">
<div class="form-group">
<div class="container-fluid">
#Html.LabelFor(model => model.Per_EMail, new { #class = "control-label col-sm-1" })
<div class="col-sm-8">
#Html.DisplayFor(model => model.Per_EMail, new { #class = "control-label col-sm-1 non-zero-num" })
</div>
</div>
</div>
</div>
<div class="checkbox">
<input type="checkbox" id="NutzungsbedingungenAngenommen " />
<label for="NutzungsbedingungenAngenommen ">
Ich erkläre mich mit den Nutzungsbedingungen einverstanden.
</label>
</div>
<button class="btn btn-default" type="submit" name="tbButton" value="questsend">Senden</button>
}
<script>
$(document).ready(function () {
$(".non-zero-num").val($(this).val() == 0 ? ' ' : $(this).val());
})
$('#verifyform').on('click', '[value="questsend"]', function () {
if ($('#agree').is(':checked')) {
return true;
}
else {
return false;
}
});
</script>
EDIT
Here my QuestionViewModel
public class QuestionViewModel
{
//Other Properties
[Required(ErrorMessage = "Bitte die Nutzungsbedingungen annehmen!")]
public bool NutzungsbedingungenAngenommen { get; set; }
}
My HttpPost Controller for View1:
[HttpPost]
public ActionResult DefaultForm(QuestionViewModel viewModel, string tbButton)
{
if (ModelState.IsValid)
{
try
{
if (tbButton.Equals("questsend"))
{
return RedirectToAction("Verify", viewModel);
}
else if (tbButton.Equals("questupload"))
{
//write to DB
return View(viewModel);
}
else
{
dropdownPopulate(viewModel);
return View("DefaultForm", viewModel);
}
}
catch
{
dropdownPopulate(viewModel);
return View(viewModel);
}
}
else
{
dropdownPopulate(viewModel);
return View(viewModel);
}
}

The problem is you use Html.DisplayFor to display the property values of viewModel in View2, so the values won't be submitted to the HttpPost method, hence viewModel is null when HttpPost for View2 is executed. Only values in <input>, <textarea> and <select> tags will be submitted.
You can submit the values of viewModel to the HttpPost for View2 by adding Html.HiddenFor inside Html.BeginForm for all properties of viewModel. You should also use Html.CheckBoxFor(m => m.NutzungsbedingungenAngenommen) for the checkbox. Something like below should work
#using (Html.BeginForm("Verify", "QuestionForm", FormMethod.Post, new { id = "verifyform" }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Per_Salutation)
#Html.HiddenFor(m => m.Per_Name_First)
#Html.HiddenFor(m => m.Per_Name_Last)
.... // Html.HiddenFor for the rest of QuestionViewModel properties
....
.... // the rest of your code inside the form tag
.... // remove <input type="checkbox" id="NutzungsbedingungenAngenommen " />
#Html.CheckBoxFor(m => m.NutzungsbedingungenAngenommen)
<button class="btn btn-default" type="submit" name="tbButton" value="questsend">Senden</button>
}

Make sure you declare what model should be used by Razor.
#model QuestionViewModel
Make sure the name and id of your HTML inputs are in the format expected by the MVC modelbinder. I recommend using the provided HtmlHelpers instead of writing the input tags by hand.
#Html.CheckBoxFor(m => m.Agree)
Remove the string tbButton parameter from your POST action

Related

Models inside a ViewModel have null property values when submitted using Post

ViewModel:
public class EventViewModel
{
public Organization.Event Evt { get; set; }
public GuestContributor Gst { get; set; }
}
Through this ViewModel, I am displaying event data using Evt property which is working fine. And Gst property used to get data from a form on the same page:
#using (Html.BeginForm("GuestApplyToEvent", "Event", FormMethod.Post))
{ <div class="row">
<div class="col-12 col-md-6">
#Html.TextBoxFor(c => c.Gst.Name, new { #class = "form-control", placeholder = "Name" })
#Html.ValidationMessageFor(c => c.Gst.Name)
</div>
<div class="col-12 col-md-6">
#Html.TextBoxFor(c => c.Gst.Email, new { #class = "form-control", placeholder = "Email Address" })
#Html.ValidationMessageFor(c => c.Gst.Email)
</div>
</div>
<div class="row">
<div class="col-12 ">
#Html.TextBoxFor(c => c.Gst.Mobile, new { #class = "form-control", placeholder = "Mobile (WhatsApp Number)" })
#Html.ValidationMessageFor(c => c.Gst.Mobile)
</div>
</div>
<input type="submit" class="btn dorne-btn" value="Apply" />
}}
When sending this ViewModel to my View I am not assigning anything to Gst.
And this is the action:
[HttpPost]
public ActionResult GuestApplyToEvent(Models.GuestContributor eventViewModel)
{
if (!ModelState.IsValid)
return View("~/Views/Event/Index.cshtml", eventViewModel);
return View("~/Views/Event/Index.cshtml", eventViewModel);
}
The problem is that all the properties in eventViewModel are null even after the data is entered.
Your Post method is expecting a model of type GuestContributor but in your View it appears you are using model EventViewModel, try changing your Post Method to receive model type EventViewModel

Can I accept a HttpPostedFileBase as part of a ViewModel

I am setting up a view and ViewModel to accept some data and a File. But when I look at the returned model, I do not see a file.
Here's my ViewModel:
public class ResourceReviewViewModel
{
public Guid ResourceReviewId { get; set; }
//....
[Display(Name="Review Document File")]
public HttpPostedFileBase ReviewFile { get; set; }
}
My view:
My controller to handle the submit:
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult ResourceReview(ResourceReviewViewModel model)
{
//...
return View(model); // model.ReviewFile is null
}
#model PublicationSystem.ViewModels.ResourceReviewViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Submit Your Review</h4>
<hr/>
#Html.HiddenFor(model => model.RequestReviewId, new { htmlAttributes = new { #class = "form-control" } })
<div class="form-group">
#Html.LabelFor(model => model.ReviewFile, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" id="ReviewFile" name="ReviewFile" value="ActionHandlerForForm" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
</div>
}
The model comes back with values, but the file property is NULL. The HTML for the Input I copied from another page, so I'm not sure I need value="ActionHandlerForForm".
Yes, it's possible. I think in your example you're just missing multipart/form-data
Try this (replace "Index" and "Home" to the View/Controller you have):
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}

ASP .Net MVC Form not calling controller on submit

I am having trouble getting my view to call the post method in my MVC Controller. When I click on the submit button, it does not call the Create method. I add a breakpoint, but it never gets to the code. I am assuming some error, but not sure how to see the error message.
Here is the View:
#model PersonViewModel
#{
ViewBag.Title = "Register";
}
#using (Html.BeginForm(PeopleControllerAction.Create, ControllerName.People, FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal row">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
</div>
<div class="row panel radius">
<div class="medium-2 columns">
<h3>Contact Information</h3>
</div>
<div class="medium-10 columns">
<div class="row">
<div class="medium-6 columns">
<div class="form-group">
#Html.LabelFor(model => model.FirstName, htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
</div>
</div>
// more fields removed for brevity
<div class="row">
<div class="form-group">
<div>
<input type="submit" value="Submit" class="button" />
</div>
</div>
</div>
}
Here is the controller:
public class PeopleController : Controller
{
private IPersonService context { get; set; }
public PeopleController(IPersonService context)
{
this.context = context;
}
// POST: People/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "FirstName,LastName,Age,Email,Phone,City,State,HopeToReach,Story,Goal,Image")] PersonViewModel person)
{
if (ModelState.IsValid)
{
try
{
person.ImagePath = ImageUploader.UploadImage(person.Image);
}
catch (ArgumentException e)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Message);
}
var id = await context.AddAsync(person);
return RedirectToAction(PeopleControllerAction.Confirmation);
}
return View(person);
}
}
This was resolved. The action in question did not have a route. I am using attribute routing.

Display template for editing a list attribute in model returns null on form submit

I have a model that contains a class like Days where Days are a collection of Day.
This is what the entity framework autogenerated model looks like:
public MyModel()
{
this.ExceptionDays = new HashSet<ExceptionDay>();
this.RegularDays = new HashSet<RegularDay>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<ExceptionDay> ExceptionDays { get; set; }
public virtual ICollection<RegularDay> RegularDays { get; set; }
}
The RegularDay & ExceptionDay are, both, separate classes in separate files under the autogenerated model.
Now, the create form for this model needs to take a Day and add it to the list Days. I figured I'd use a display template for doing this.
This is what my create display view looks like:
#model MyModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
<div class="form-group">
<div class="col-md-10">
**#Html.EditorFor(model => model.RegularDays, "ICollection_RegularDay_Edit")**
</div>
</div>
<div class="form-group">
<div class="col-md-10">
**#Html.EditorFor(model => model.ExceptionDays, "ICollection_ExceptionDay_Edit")**
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
My Edit template looks like this:
#model RegularDay
<div class="form-group">
#Html.LabelFor(model => model.dayOfWeek)
<div class="col-md-">
<div class="col-md-">
#Html.DropDownListFor(model => model.dayOfWeek, new SelectList(
new List<Object>
{
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
}
))
</div>
#Html.ValidationMessageFor(model => model.dayOfWeek, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.startTime)
<div class="col-md-">
#Html.EditorFor(model => model.startTime, new { htmlAttributes = new { #class = "form-control" } })
#*#Html.ValidationMessageFor(model => model.startTime, "", new { #class = "text-danger" })*#
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NumberOfHours)
<div class="col-md-">
#Html.DropDownListFor(model => model.NumberOfHours, new SelectList(
new List<Object>
{
1,2,3,4,5,6,7,8
}
))
</div>
</div>
The other display template for edit is similar.
Now the problem is, that my controller never gets the regularDay or ExceptionDay in the model that the view returns on post.
My controller method looks like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,Description, RegularDays, ExceptionDays")] MyModel myModel)
{
if (ModelState.IsValid)
{
db.LocationHoursModels.Add(locationHoursModel);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(myModel);
}
How can I go about creating a display template or a create view for the Days type of attribute for this MVC?

ASP.NET MVC get textbox input value

I have a textbox input and some radio buttons. For example my textbox input HTML looks like that:
<input type="text" name="IP" id="IP" />
Once user clicks a button on a web page I want to pass data to my controller:
<input type="button" name="Add" value="#Resource.ButtonTitleAdd" onclick="location.href='#Url.Action("Add", "Configure", new { ipValue =#[ValueOfTextBox], TypeId = 1 })'"/>
Maybe it is trivial but my problem is that I do not know how to get textbox value and pass it through to the controller. How can I read the textbox value and pass it to the controller through ipValue=#[ValueOfTextBox]?
Simple ASP.NET MVC subscription form with email textbox would be implemented like that:
Model
The data from the form is mapped to this model
public class SubscribeModel
{
[Required]
public string Email { get; set; }
}
View
View name should match controller method name.
#model App.Models.SubscribeModel
#using (Html.BeginForm("Subscribe", "Home", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Email)
#Html.ValidationMessageFor(model => model.Email)
<button type="submit">Subscribe</button>
}
Controller
Controller is responsible for request processing and returning proper response view.
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Subscribe(SubscribeModel model)
{
if (ModelState.IsValid)
{
//TODO: SubscribeUser(model.Email);
}
return View("Index", model);
}
}
Here is my project structure. Please notice, "Home" views folder matches HomeController name.
You may use jQuery:
<input type="text" name="IP" id="IP" value=""/>
#Html.ActionLink(#Resource.ButtonTitleAdd, "Add", "Configure", new { ipValue ="xxx", TypeId = "1" }, new {#class = "link"})
<script>
$(function () {
$('.link').click(function () {
var ipvalue = $("#IP").val();
this.href = this.href.replace("xxx", ipvalue);
});
});
</script>
Try This.
View:
#using (Html.BeginForm("Login", "Accounts", FormMethod.Post))
{
<input type="text" name="IP" id="IP" />
<input type="text" name="Name" id="Name" />
<input type="submit" value="Login" />
}
Controller:
[HttpPost]
public ActionResult Login(string IP, string Name)
{
string s1=IP;//
string s2=Name;//
}
If you can use model class
[HttpPost]
public ActionResult Login(ModelClassName obj)
{
string s1=obj.IP;//
string s2=obj.Name;//
}
Another way by using ajax method:
View:
#Html.TextBox("txtValue", null, new { placeholder = "Input value" })
<input type="button" value="Start" id="btnStart" />
<script>
$(function () {
$('#btnStart').unbind('click');
$('#btnStart').on('click', function () {
$.ajax({
url: "/yourControllerName/yourMethod",
type: 'POST',
contentType: "application/json; charset=utf-8",
dataType: 'json',
data: JSON.stringify({
txtValue: $("#txtValue").val()
}),
async: false
});
});
});
</script>
Controller:
[HttpPost]
public EmptyResult YourMethod(string txtValue)
{
// do what you want with txtValue
...
}
you can do it so simple:
First: For Example in Models you have User.cs with this implementation
public class User
{
public string username { get; set; }
public string age { get; set; }
}
We are passing the empty model to user – This model would be filled with user’s data when he submits the form like this
public ActionResult Add()
{
var model = new User();
return View(model);
}
When you return the View by empty User as model, it maps with the structure of the form that you implemented. We have this on HTML side:
#model MyApp.Models.Student
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Student</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.username, htmlAttributes: new {
#class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.username, new {
htmlAttributes = new { #class = "form-
control" } })
#Html.ValidationMessageFor(model => model.userame, "",
new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.age, htmlAttributes: new { #class
= "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.age, new { htmlAttributes =
new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.age, "", new {
#class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default"
/>
</div>
</div>
</div>
}
So on button submit you will use it like this
[HttpPost]
public ActionResult Add(User user)
{
// now user.username has the value that user entered on form
}

Categories