Dynamic Model Binding Partial View to Model MVC? - asp.net-mvc

I'm trying to bind the model from the partial view to my main form. My Model workoutPlan only stores a list of WorkoutSets
public List<WorkoutSet> WorkoutSet;
In my main page. My form looks something like this:
#model WorkoutPlanObjects.WorkoutPlan
#using (Html.BeginForm("AddNewPlan", "Workout", FormMethod.Post))
{
if (Model != null)
{
<table id="workoutTable">
<tr>
<td>
#{Html.RenderPartial("~/Views/Partial/_AddNewPlan.cshtml", Model);}
</td>
</tr>
</table>
}
<input type="submit" id="submit" value="submit" />
}
and here's my partial view _AddNewPlan
#model WorkoutPlanObjects.WorkoutPlan
#if(Model!=null)
{
foreach (var item in Model.WorkoutSet)
{
#Html.TextBoxFor(a => item.Repeats)
}
}
I was able to update my partial view using ajax calls but when I try to submit the form, no values get passed. Here's the snippet of the code in chrome which shows the rendered partial view (excuse the formatting). Any solution for this?
I could see that the name of the rendered views are all the same item.Repeats. How should I change this partial view name to bind with the main page model?
<form action="/Workout/AddNewPlan" method="post">
<table id="workoutTable">
<tbody><tr>
<td>
<input id="item_Repeats" name="item.Repeats" type="text" value="1">
<input id="item_Repeats" name="item.Repeats" type="text" value="2">
<input id="item_Repeats" name="item.Repeats" type="text" value="3">
<input id="item_Repeats" name="item.Repeats" type="text" value="4">
</td>
</tr>
</tbody>
</table>
<input type="submit" id="submit" value="submit">
</form>

Change your foreach loop to a for loop so that the inputs are correctly named
foreach (iny i = 0; i < Model.WorkoutSet.Count; i++)
{
#Html.TextBoxFor(a => a.WorkoutSet[i].Repeats)
}
which will render
<input .... name="WorkoutSet[0].Repeats" type="text" value="1">
<input .... name="WorkoutSet[1].Repeats" type="text" value="2">
and allow the DefaultModelBinder to bind the collection.
Note property WorkoutSet will need to be IList. Alternatively you can create a custom EditorTemplate for WorkoutSet and use #Html.EditorFor(m => m.WorkoutSet)

Related

How to take data from get and put it to post? Handle input radio type

The form sends a get request and gets the following view
#using System.Linq;
#model List<Searchphone>
<h2 class="search1"></h2>
<form class="priceeconom1" method="post" asp-action="Index" asp-controller="Home">
#foreach (var p in Model)
<table>
{
<tr class="searchdata">
<td class="flight">#p.ColourPhone</td>
<td class="fromtable">#p.TypePhone</td>
<td class="fromtable">#p.SizePhone</td>
<td class="fromtable">#p.PriceLight</td>
<td class="fromtabletime">#p.PriceOk</td>
<td class="totable">#p.PriceHigh</td>
</tr>
}
</table>
All data is generated from multiple linked tables.It is assumed that the user selects one of the "Price" and the form must pass the selected data and price in the Post request.
2)I did it like this
#using System.Linq;
#model List<Searchphone>
<h2 class="search1"></h2>
<form class="priceeconom1" method="post" asp-action="Index" asp-controller="Home">
#foreach (var p in Model)
<table>
{
<tr class="searchdata">
<td class="flight"><input type="text" name="#p.ColourPhone</td>
<td class="fromtable"><input type="text" name="#p.TypePhone</td>
<td class="fromtable"><input type="text" name="#p.SizePhone</td>
<td class="pricelight"><input type="radio" name="price"value="#p.PriceLight.ToString("")"/>#p.PriceLight.ToString("")</td>
<td class="fromtabletime"><input type="radio" name="price"value="#p.PriceOk.ToString("")/>#p.PriceOk.ToString("")</td>
<td class="totable"><input type="radio" name="price"value="#p.PriceHigh.ToString("")/>#p.PriceHigh.ToString("")</td>
<td class="button13"><button type="submit" asp-action="Buy" asp-route-id="#p.PhoneId" asp-controller="Home">Next</button></td>
</tr>
}
</table>
</form>
When I need to go to the next post-form. And I did the next view
#using System.Linq;
#using System;
#model List<Searchphone>
#{
ViewBag.Title = "PhoneId";
}
<h2 class="orderinformation">Order Data</h2>
<form method="post" class="formpass">
<input type="hidden" id="PhoneId"value="#ViewBag.PhoneId" name="PhoneId">
<label for="OrderSurName">Surname</label><br>
<input type="text" placeholder="Enter Surname" name="OrderSurName" required><br>
<label for="OrderName">Имя</label><br>
<input type="text" placeholder="Enter Name" name="OrderName" required><br>
<button type="submit" class="button7">To pay</button>
</form>
Controller
[HttpGet]
public IActionResult Buy(int? id)
{
if (id == null) return RedirectToAction("Index");
ViewBag.PhoneId = id;
return View();
}
[HttpPost]
public string Buy(Order order)
{
context.Orders.Add(order);
context.SaveChanges();
return "Thanks, " + order.OrderName;
}
But I have problem. I got URL:~/Home/Buy/0 and the view with return "Thanks, ". I don`t get the view for input Order Data. Table Order in Database gets default and null values. What is my mistake?
Part 2.Adding to the next question
Controller
[HttpGet]
public IActionResult Buy(int? id, string price)
{
if (id == null) return RedirectToAction("Index");
if(price=="PriceLight.ToString("")")
ViewBag.price=PriceLight.ToString("");
if(price=="PriceOk.ToString("")")
ViewBag.price=PriceOk.ToString("");
if(price=="PriceHigh.ToString("")")
ViewBag.price=PriceHigh.ToString("");
ViewBag.PhoneId = id;
return View();
}
View
#using System.Linq;
#using System;
#model List<Searchphone>
#{
ViewBag.Title = "PhoneId";
}
<h2 class="orderinformation">Order Data</h2>
<form method="post" class="formpass">
<h3> <input type="hidden" name="#ViewBag.price" value="#ViewBag.price" />#ViewBag.price</h3>
<br>
<input type="hidden" id="PhoneId"value="#ViewBag.PhoneId" name="PhoneId">
<label for="OrderSurName">Surname</label><br>
<input type="text" placeholder="Enter Surname" name="OrderSurName" required><br>
<label for="OrderName">Имя</label><br>
<input type="text" placeholder="Enter Name" name="OrderName" required><br>
<button type="submit" class="button7">To pay</button>
</form>
The form is submitted in post method while the return view action only accept a HttpGet request. So, it always hit the HttpPost action since they have the same name.
I tested your codes and find there are some problems in your first view.
[HttpGet]
public IActionResult Buy(int? id)
{
if (id == null) return RedirectToAction("Index");
ViewBag.PhoneId = id;
return View();
}
This action just receive a id parameter. There is no need to submit the whole form. In my opinion, You don’t even need to use forms, just use the < a > tag like below:
#using System.Linq;
#model List<Searchphone>
<h2 class="search1"></h2>
#foreach (var p in Model)
{
<table>
<tr class="searchdata">
<td class="flight"><input type="text" name="#p.ColourPhone" /></td>
<td class="fromtable"><input type="text" name="#p.TypePhone" /></td>
<td class="fromtable"><input type="text" name="#p.SizePhone" /></td>
<td class="pricelight"><input type="checkbox" name="#p.PriceLight.ToString()" /></td>
<td class="fromtabletime"><input type="checkbox" name="#p.PriceOk.ToString()" /></td>
<td class="totable"><input type="checkbox" name="#p.PriceHigh.ToString()" /></td>
<td class="totable"> <a asp-action="Buy" asp-route-id="#p.PhoneId" asp-controller="Home">Next</a></td>
</tr>
</table>
}

How to pass value from submit button to mvc dynamically?

I started working with MVC from few days and got a question out of learning lot of ways to communicate between Controllers and Views in MVC
I have page that shows list of employees in tabular form.
Model is of type IEnumerable of Employee Model
It has three buttons they are Edit, Create, Delete, Details.
Requirement:
I used buttons so that all should be of HTTP Post request type because I do not want users to directly access them using URL requests.
Here is my view code:
#using (Html.BeginForm())
{
<p>
<input type="submit" name="CreateView" value="Create New(Post)" formaction="CreateView" formmethod="post" />
</p>
<table class="table">
<tr>
-------Headings of table-------
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.EmployeeName)</td>
<td>#Html.DisplayFor(modelItem => item.EmployeeGender)</td>
<td>#Html.DisplayFor(modelItem => item.EmployeeCity)</td>
<td>#Html.DisplayFor(modelItem => item.DepartmentId)</td>
<td>#Html.DisplayFor(modelItem => item.EmployeeDateOfBirth)</td>
<td>
<input type="submit" name="EditView" value="Edit(Post)" formaction="Edit" formmethod="post" /> |
<input type="submit" name="DetailsView" value="Details(Post)" formaction="Details" formmethod="post" /> |
<input type="submit" value="Delete(Post)" onclick="return confirm('Are you sure you want to delete record with EmployeeId = #item.EmployeeId')" />
</td>
</tr>
}
</table>
}
Here delete button works because I do not need the id of the employee.
But for other actions like editing, deleting and details viewing I need to pass Employees Id to controller. But how do I pass the Id to the controller using submit button.
In get requests types I used to pass like this:
#Html.ActionLink("Details", "Details", new { id = item.EmployeeId })
For single submit button I used to pass data like this
#using (Html.BeginForm("Details", "BusinessLayer", FormMethod.Post, new { id = item.EmployeeId }))
Can any one tell me the approach that I can fallow to achieve this?
You can have 3 separate form tags, one for each button. Just make sure you have an input field inside the form for the data you want to pass. For example if your action method is accepting the EmployeeId with a a parameter called EmployeeId, you should have in input hidden field with the same inside the form.
#model IEnumerable<Employee>
<table>
#foreach(var item in Model)
{
<tr>
<td>#item.EmployeeName</td>
<td>#item.EmployeeGender</td>
<td>#item.EmployeeCity</td>
<td>#item.EmployeeDateOfBirth</td>
<td>
#using(Html.BeginForm("Details","YourControllerName"))
{
<input type="hidden" name="EmployeeId" value="#item.EmployeeId" />
<input type="submit" value="Details" />
}
#using(Html.BeginForm("Edit","YourControllerName"))
{
<input type="hidden" name="EmployeeId" value="#item.EmployeeId" />
<input type="submit" value="Edit" />
}
#using(Html.BeginForm("Delete","YourControllerName"))
{
<input type="hidden" name="EmployeeId" value="#item.EmployeeId" />
<input type="submit" value="Delete" />
}
</td>
</tr>
}
Also remember, nested forms are invalid HTML. So make sure you do not have those.

POST action returns the model in a valid state but returns Model.Count as 0 when using foreach or for loop

As shown in my post here the GET action method Test(..) works fine when using foreach loop in the corresponding Test.chtml view but the POST action method Test(...) returns null. But, as mentioned by many users, the foreach is not reliable for POST method. So, I decided to use the for loop as shown below. But that returned unexpected results in the view since, according to this post, in a foreachloop type casting is done automatically but in for loop you have to type cast the objects Model[i].BlogID etc to a proper class object type.
So, I decided to type cast the objects Model[i].BlogID etc to a BlogsWithRelatedPostsViewModel class object type as shown in the second version of Test.cshml view below; and this time the Test.cshtml view is displayng the correct records. But although the submit button in the view is sending a a valid model (ModelState.IsValid is true) the Model.Count is 0 that results in no update to database. Why Model.Count is 0 and how to correct it? As you can see below the html page source of the view is showing the name attributes of the tags matching the property values in the View Model.
Note: For complete code, please see this OP. I'm using ASP.NET Core with EF Core and Tag Helpers.
Test.cshtml view with for loop - without type casting the loop objects:
#model IList<ASP_Core_Blogs.Models.BlogPostViewModels.BlogsWithRelatedPostsViewModel>
#using ASP_Core_Blogs.Models.BlogPostViewModels
#{ ViewData["Title"] = "Index"; }
<div class="row">
<div class="col-md-12">
<form asp-controller="Blogs" asp-action="Test" asp-route-returnurl="#ViewData["ReturnUrl"]" method="post">
#{
IEnumerable<SelectListItem> yearsList = (IEnumerable<SelectListItem>)ViewBag.YearsList;
var currentlySelectedIndex = 0; // Currently selected index (usually will come from model)
}
<strong>Select a Post Year</strong>
<h6>Choose a year and a URL to begin:</h6>
<label>Year:</label><select asp-for="#currentlySelectedIndex" asp-items="yearsList"></select><input type="submit" class="btn btn-default" name="GO" value="GO" />
<table class="table">
<thead>
<tr>
<th></th>
<th></th>
<th>Url</th>
<th>Title</th>
<th>Content</th>
</tr>
</thead>
<tbody>
#for (int i=0; i< Model.Count(); i++)
{
<tr>
<td><input type="hidden" asp-for="#Model[i].BlogID" /></td>
<td><input type="hidden" asp-for="#Model[i]).PostID" /></td>
<td>
<input type="text" asp-for="#Model[i].Url" style="border:0;" readonly />
</td>
<td>
<input asp-for="#Model[i].Title" />
</td>
<td>
<input asp-for="#Model[i].Content" />
</td>
</tr>
}
</tbody>
</table>
<button type="submit" class="btn btn-default">Save</button>
</form>
</div>
</div>
Test.cshtml view with for loop objects being casted as BlogsWithRelatedPostsViewModel class objects:
#model IList<ASP_Core_Blogs.Models.BlogPostViewModels.BlogsWithRelatedPostsViewModel>
#using ASP_Core_Blogs.Models.BlogPostViewModels
#{ ViewData["Title"] = "Index"; }
<div class="row">
<div class="col-md-12">
<form asp-controller="Blogs" asp-action="Test" asp-route-returnurl="#ViewData["ReturnUrl"]" method="post">
#{
IEnumerable<SelectListItem> yearsList = (IEnumerable<SelectListItem>)ViewBag.YearsList;
var currentlySelectedIndex = 0; // Currently selected index (usually will come from model)
}
<strong>Select a Post Year</strong>
<h6>Choose a year and a URL to begin:</h6>
<label>Year:</label><select asp-for="#currentlySelectedIndex" asp-items="yearsList"></select><input type="submit" class="btn btn-default" name="GO" value="GO" />
<table class="table">
<thead>
<tr>
<th></th>
<th></th>
<th>Url</th>
<th>Title</th>
<th>Content</th>
</tr>
</thead>
<tbody>
#for (int i=0; i< Model.Count(); i++)
{
<tr>
<td><input type="hidden" asp-for="((BlogsWithRelatedPostsViewModel)#Model[i]).BlogID" /></td>
<td><input type="hidden" asp-for="((BlogsWithRelatedPostsViewModel)#Model[i]).PostID" /></td>
<td>
<input type="text" asp-for="((BlogsWithRelatedPostsViewModel)#Model[i]).Url" style="border:0;" readonly />
</td>
<td>
<input asp-for="((BlogsWithRelatedPostsViewModel)#Model[i]).Title" />
</td>
<td>
<input asp-for="((BlogsWithRelatedPostsViewModel)#Model[i]).Content" />
</td>
</tr>
}
</tbody>
</table>
<button type="submit" class="btn btn-default">Save</button>
</form>
</div>
</div>
A Portion of html generated by View after Submit:
<tr>
<td><input type="hidden" data-val="true" data-val-required="The BlogID field is required." id="BlogID" name="BlogID" value="1" /></td>
<td><input type="hidden" data-val="true" data-val-required="The PostID field is required." id="PostID" name="PostID" value="1" /></td>
<td>
<input type="text" style="border:0;" readonly id="Url" name="Url" value="blog1#test.com" />
</td>
<td>
<input type="text" id="Title" name="Title" value="Title1" />
</td>
<td>
<input type="text" id="Content" name="Content" value="Content1" />
</td>
</tr>

How to send Id to Controller from Ajax Input Button

I have this grid which has an edit button. How do I add code to the input button so that the value of the Id is sent to the Controller?
#using (Ajax.BeginForm("EditLineItem", "OrderSummary", new AjaxOptions() { InsertionMode = InsertionMode.Replace, UpdateTargetId = "content" })) {
<div id="summaryGrid">
<table >
<tr>
<th>Report Type</th>
<th>Borrower Name</th>
<th>Property Address</th>
<th>Est Comp Date</th>
<th>Report Price</th>
<th>Exp Fee</th>
<th>Disc.</th>
<th>Total Price</th>
</tr>
#{
foreach (var item in Model) {
<tr>
<td >#item.ReportName</td>
<td >#item.BorrowerName</td>
<td >#item.Address</td>
<td >#item.EstimatedCompletionDate</td>
<td >#item.ReportPrice</td>
<td >#item.ExpediteFee</td>
<td >#item.Discount</td>
<td >#item.TotalPrice</td>
<td >#item.Id</td>
<td ><input type="submit" value="Edit" /></td>
</tr>
}
}
</table>
</div>
}
just put a name on your input button.
<input type="submit" name="id" value="edit" />
Then on your action, you should be able to get the value for id.
If you want more complexity then you are going to have to rethink the way you are doing it. Most likely by writing your own JQuery methods.
$('input.edit').on('click', function (evt) {
evt.preventDefault();
var values = $(this).data();
$.post($(this).attr('href'), values, function (result) { /*do something*/ });
});
Html :
<a href="/edit/1" class="edit" type="submit" data-id="1" data-method="edit" />
That's a start, but you could probably tweak it to fit your needs. At that point, you don't need to wrap the whole table with the Ajax.BeginForm.
To add to Khalid's answer: I tested with this form:
<form method="get">
<input type="submit" name="Id1" value="Edit" id="id1" />
<input type="submit" name="Id2" value="Edit" id="id2" />
<input type="submit" name="Id3" value="Edit" id="id3" />
</form>
The post looks like this when clicking on the third button:
http://localhost:34605/HtmlPage.html?Id3=Edit
In other words, the browser passes the name of whichever button is clicked.
This is an example of getting the Id in the controller:
if (Request.QueryString.HasKeys()) {
string key = Request.QueryString.GetKey(0);
int id;
int.TryParse(key.Substring(2, 1), out id);
Response.Write("You selected id: " + id);
}
I have since found an even easier way of doing this:
Use the <button> element instead of <input>
With <button> you can do this:
<button type="submit" value="#item.Id" name="id">Edit</button>
and then in the controller, all you need is this:
public ActionResult EditLineItem(int id)
{ //Do something with id}
Note that this does not work with IE6.

Save all data of MVC4 html grid

Can anyone give me an example of saving all html grid data in one time. I have a view like this.
#model IList<SURVEY.Models.Question>
#using (Html.BeginForm("Index", "Survey", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-3" }))
{
#foreach(var item in Model)
{
<tr>
<td>#item.Ans1</td>
<td align="center">
<label>
<input type="radio" name="optionAS_#item.QuestionId" value="1" id="optionAS_1" onclick="disableAs(this,#item.QuestionId,1)"/>
</label>
</td>
<td align="center">
<label>
<input type="radio" name="optionAS_#item.QuestionId" value="2" id="optionAS_1" onclick="disableAs(this,#item.QuestionId,2)"/>
</label>
</td>
</tr>
}
}
I am getting null value for these controls in controller post.
[HttpPost]
public ActionResult Index(IList<Question> ques)
{
return View();
}
I am getting ques is null here. Can anyone tell me how can I resolve this?
You should use html helpers to bind properties of your model, your code might be as follows:
#for(var i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.HiddenFor(_ => Model[i].Id)
Model[i].Ans1
</td>
<td align="center">
<label>
#Html.RadioButtonFor(_ => Model[i].Name)
</label>
</td>
...
</tr>
}
and so for. HiddenFor helper is needed to create hidden input to send Id value to server to give you ability to identify you object. Take a look into Html Helpers in MVC and you will have your model back to server when form is submitted.

Resources