I have a view model which holds the available products and after i show them in a view i have to get the particular item which he clicks on Add to Cart
My Action
public ActionResult Test1()
{
DUPVM obj = new DUPVM();
MembersipEntities Entity = new MembersipEntities();
obj.test = Entity.Dups.ToList();
return View(obj);
}
[HttpPost]
public ActionResult Test1(Dup model)
{
return View(model);
}
and the viewmodel
public class DUPVM
{
public IEnumerable<Dup> test { get; set; }
}
And in my view i will display the all products available by looping through them
#model MVC4.Models.DUPVM
#using (Html.BeginForm("Test1", "PDF", FormMethod.Post))
{
foreach (var item in Model.test)
{
#Html.DisplayFor(modelitem => item.ID)
<br>
#Html.DisplayFor(modelitem => item.Rate)
#Html.DisplayFor(modelitem => item.AgentID)
#Html.TextBoxFor(modelitem => item.ID)
<br>
<input type="submit" value="Add to cart" />
<br>
}
}
Then If a user clicks on Add to cart then i want to pass all details of that products to controller using model. But i am not able to do this.
First change your view to create the form inside the loop. This way you are getting a discrete form with only the values you require.
#model MVC4.Models.DUPVM
foreach (var item in Model.test)
{
#using (Html.BeginForm("Test1", "PDF", FormMethod.Post))
{
#Html.DisplayFor(modelitem => item.ID)
<br>
#Html.DisplayFor(modelitem => item.Rate)
#Html.DisplayFor(modelitem => item.AgentID)
#Html.TextBoxFor(modelitem => item.ID)
<br>
<input type="submit" value="Add to cart" />
<br>
}
}
Your view is strongly-typed to the DUPVM class but your controller action is expecting a single Dup. The default behaviour of the form Razor helpers is to post the model back in the same format it was accepted.
To get this to work you will need to create a partial view for each item, strongly type that partial view to a Dup (not a DUPVM) and either write some javascript that posts your selected item to the controller, or have a seperate form for each item.
You partial view might look something like this
#model MVC4.Models.Dup
#using (Html.BeginForm("Test1", "PDF", FormMethod.Post))
{
#Html.DisplayFor(x => x.ID)
<br>
#Html.DisplayFor(x => x.Rate)
#Html.DisplayFor(x => x.AgentID)
#Html.TextBoxFor(x => x.ID)
<br>
<input type="submit" value="Add to cart" />
<br>
}
And your main view like this:
#model MVC4.Models.DUPVM
#foreach (var item in Model.test)
{
Html.RenderPartial("_YourPartialView", item);
}
Related
I want to pass id and quantity to action, but I get this error: parameter dictionary contains a null entry for parameter id.
I've tried to do map routing, but I can't find how to do it properly.
My Action:
[HttpPost]
public ActionResult Index(int id, int quantity)
{
var user = unitOfWork.Users.FindByEmail(HttpContext.User.Identity.Name);
unitOfWork.Carts.AddProductToCartByEmail(user.Email, id, quantity);
unitOfWork.Complete();
return View();
}
From this page I'm trying to pass the parameters:
#using PagedList;
#using PagedList.Mvc;
#model IPagedList<MVC_Task.Models.AllProductsModel>
#{
ViewBag.Title = "Index";
}
<h2>Products</h2>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.First().Name)
</th>
<th>
#Html.DisplayNameFor(model => model.First().Price)
</th>
#if (User.Identity.IsAuthenticated)
{
<th>
Quantity
</th>
}
</tr>
#foreach (var item in Model)
{
using (Html.BeginForm("Index", "Product", FormMethod.Post))
{
#Html.AntiForgeryToken()
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Price)
</td>
#if (User.Identity.IsAuthenticated)
{
<td class="form-inline">
#Html.TextBoxFor(modelItem => item.Quantity, new { #type = "number", #class = "form-control" })
<input type="submit" value="Add" class="btn btn-default"
onclick="window.location.href = '#Url.Action("Index", new { item.Id, item.Quantity})';" /> //on this line I send the parameters
</td>
}
</tr>
}
}
</table>
<center>#Html.PagedListPager(Model, page => Url.Action("Index", new { page }))</center>
In post actions there is only one parameter can be taken from body, all other parameters you may pass them as route parametes or query string parameters, so your signature
public ActionResult Index(int id, int quantity)
may become:
public ActionResult Index(int id, [FromBody] int quantity)
you can call the action using the url /index?id=...
and try to call the action using ajax post or submit the form not a hyperlink
for more information see https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api#using-frombody
You're not really posting correctly, using the client side window.location.href - it will result in a HttpGet.
You should add a hidden input for the item id, here's the relevant snippet:
<td class="form-inline">
<!-- add the hidden input -->
#Html.HiddenFor(modelItem => item.Id)
<!-- keep this as is -->
#Html.TextBoxFor(modelItem => item.Quantity, new { #type = "number", #class = "form-control" })
<!-- remove the onclick redirect,
this will post to the action defined in BeginForm -->
<input type="submit" value="Add" class="btn btn-default"/>
</td>
Next, a model is often passed as a complex type. It has many benefits over simple types, because you can extend it with, e.g.; validation logic.
So, lets define a model:
public class PostModel
{
public int Id {get;set;}
public int Quantity {get;set;}
}
Now adjust your Index to accept the model:
[HttpPost]
public ActionResult Index(PostModel model)
{
//your logic
}
By default the binders will bind to the appropriate properties in the model. If your TextBox's name is Id, or Quantity, it will be able to do so. You can validate that in the rendered html.
I have a index in controller and view following.
When I enter student ID on textbox search, I want to display information of student and list all activities of them. How to do that.
Thank you so much!
Controller:
public ActionResult Index(string Sid)
{
var grad = db.Gradings.Include(g => g.Activity).Include(g => g.Student).Where(g => g.StudentID == Sid).ToList();
}
View:
<div class="col-xs-4">
<p>
#using (Html.BeginForm())
{
<p>
Student ID: #Html.TextBox("Sid",ViewBag.FilterValue as string)
<input type="submit" value="Search" />
</p>
}
<table class="table">
<tr>
<th>Activity Name</th>
....
</tr>
#foreach (var item in Model)
{
<tr>
<td>#Html.DisplayFor(modelItem => item.Activity.Name)</td>
<td>#Html.DisplayFor(modelItem => item.Responsibility)</td>
....
</tr>
}
</table>
</div>
<h3> Total score: #ViewBag.Total</h3>
Thanks for your help!
You need one action to serve your view initially when the user requests it:
public ActionResult Index()
{
return View();
}
Then you need another action to process the user's request when the request submits the form:
public ActionResult Index(string Sid)
{
var grad = db.Gradings.Include(g => g.Activity).Include(g => g.Student).Where(g => g.StudentID == Sid).ToList();
return View(grad);
}
Now grad is a List<Grading> and above we are passing it to the view as the model so make sure you use it in your view. You may need to include the namespace for List and Grading:
#model List<Grading>
Finally instead of using foreach in your view, use a for loop so your HTML tags have unique IDs. Right now (with foreach), all Grading records will have the same name and id attributes.
was trying to use Html.BeginCollectionItem to work in my application and am struggling with getting the data to post. I want to add items to a list and then post the entire list. I am using ajax and jquery to add and delete items to my list and that seems to be working. But when I post the model received in my controller is always null even though when I look at fiddler the Form Data contains all my information.
Does anyone see something simple in my code that I am doing wrong?
Main View:
#model Test.Models.TestList
#using (Html.BeginForm())
{
<div class="form-group">
<div class="row">
<label for="AssemblyText" class="col-sm-1 col-sm-offset-1 control-label">Assembly:</label>
<div class="col-sm-2">
<input type="text" id="assembly" />
</div>
<label for="QuantityText" class="col-sm-1 control-label">Quantity:</label>
<div class="col-sm-2">
<input type="text" id="Qty" />
</div>
<button type="button" id="AddAssembly">Add Button</button>
</div>
</div>
<table id="Assemblies" class="table table-striped">
<thead>
<tr>
<th>Assembly</th>
<th>Quantity</th>
<th>Action</th>
</tr>
</thead>
<tbody class="text-left">
#if (Model != null)
{
foreach (var assembly in Model.mylist)
{
#Html.Partial("AssemblyRow", assembly)
}
}
</tbody>
</table>
<div class="form-group">
<input type="submit" id="submitbtn" class="btn btn-success" value="Submit" />
</div>
}
Partial View (AssemblyRow)
#model Test.Models.Test
<tr class="editorRow">
#using (Html.BeginCollectionItem("Assembly"))
{
<td>
#Html.HiddenFor(m => m.assembly)
#Html.TextBoxFor(m => m.assembly)
</td>
<td>
#Html.HiddenFor(m => m.Qty)
#Html.TextBoxFor(m => m.Qty)
</td>
<td>
<span class="dltBtn">
Delete
</span>
</td>
}
My Models are simple and look like...
public class TestList
{
public List<Test> mylist { get; set; }
}
public class Test
{
public string assembly { get; set; }
public string Qty { get; set; }
}
My controller
[HttpPost]
public ActionResult PostMain(TestList model)
{
return View();
}
I can provide whatever other code you guys think is helpful but I tried to keep it simple with what I thought were the relevant pieces.
Thanks for any help!
Edit: Pic of fiddler
Your collection property is named mylist therefore you must pass that name to the BeginCollectionItem method
#using (Html.BeginCollectionItem("mylist"))
{
....
which will generate elements with name=mylist[xxxx].assembly" (where xxxx is a Guid) that are need to correctly bind to your model.
However, you have other issues with your code. The DefaultModelBinder binds the first name/value pair matching a model property and ignores any subsequent name/value pairs with the same name. Because you have a hidden input for each property before the textbox, only the initial values you sent to the view will be bound when you submit, not the edited values. You need to remove both hidden inputs s that the partial is
#model Test.Models.Test
<tr class="editorRow">
#using (Html.BeginCollectionItem("mylist"))
{
<td>#Html.TextBoxFor(m => m.assembly)</td>
<td>#Html.TextBoxFor(m => m.Qty)</td>
<td>
<span class="dltBtn">Delete</span>
</td>
}
</tr>
Side note: It is also unclear what the html in the initial <div class="form-group"> is for. You including 2 inputs and a button, but that will not bind to your model and will not correctly add items to your collection (your add button needs to use ajax to call a server method that returns another partial view and append it to the DOM)
I don't see a submit button, so can't really tell what you are submitting, but try changing ActionResult PostMain(TestList model) to:
ActionResult PostMain(List<Test> model)
You need to use Editor Template instead of partial views. Main controller context is not available in partial views.
There are so many posts on Stackoverflow discussing this. One of them is
ASP.NET MVC 3 - Partial vs Display Template vs Editor Template
I have asked this question, I didn't get my answer. So I did much more researches about this and still couldn't find a proper good answer for this.
My controller (shortened):
public ActionResult SearchResult(String sortOrder, String carMake, String carModel)
{
var cars = from d in db.Cars
select d;
if (!String.IsNullOrEmpty(carMake))
{
if (!carMake.Equals("All Makes"))
{
cars = cars.Where(x => x.Make == carMake);
}
}
if (!String.IsNullOrEmpty(carModel))
{
if (!carModel.Equals("All Models"))
{
cars = cars.Where(x => x.Model == carModel);
}
}
switch (sortOrder)
{
case "Model":
cars = cars.OrderBy(s => s.Model);
break;
default:
cars = cars.OrderBy(s => s.Make);
break;
}
return View(cars);
}
My Index view (shortened - this is where user filters cars by different inputs):
#model IEnumerable<Cars.Models.Car>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
<label>Model</label>
<select id="ModelID" name="carModel">
<option>All Models</option>
</select>
<button type="submit" name="submit" value="search" id="SubmitID">Search</button>
}
My SearchResult view where shows the search results (shortened):
#model IEnumerable<Cars.Models.Car>
<table>
#foreach (var item in Model)
{
<tr>
<td>
<label>Make:</label>
<p>#Html.DisplayFor(modelItem => item.Make)</p>
</td>
<td>
<label>Model:</label>
<p>#Html.DisplayFor(modelItem => item.Model)</p>
</td>
</tr>
}
</table>
Model
My goal: When user clicks on the sort by Model the page will sort the results by Model.
Problem: When the sortby is clicked, all the parameters of action SearchResult will be null since the search value do not exist in SearchResult View.
Question: How to fix this?
Thanks, Amin
UPDATE: Any example would be deeply appreciated. I'm stuck with back-end the whole process of sending and fetching data between controller and view.
You can create one class like below
public CarFilter CarFilter;
public byte PageSize;
public short PageNumber;
public int TotalRows;
where CarFilter is another class where you can store your filters enter by users.
and use this class as a model for your view.so that when you view is loading you can load car filter data.
You can retain your search filters by doing any of the following:
Use sessions to store the filters on your search result view. This way, when you post on the same view using Sort you would still have the search filters.
(I prefer this one) Wrap your model in a class that has properties for your search filters. This way, you will still be able to retrieve the search filters. Provided that you have a POST for SearchResult that accepts the Wrapper Model as parameter.
Create Classes as follow:
class ManageCarSearch
{
public CarFilter CarFilter;
public byte PageSize;
public short PageNumber;
public int TotalRows;
public String sortOrder;
}
Class CarFilter
{
public String carMake,
public String carModel
}
your searchResult view look like below:
#model IEnumerable<car>
<table>
#foreach (var item in Model)
{
<tr>
<td>
<label>Make:</label>
<p>#Html.DisplayFor(modelItem => item.Make)</p>
</td>
<td>
<label>Model:</label>
<p>#Html.DisplayFor(modelItem => item.Model)</p>
</td>
</tr>
}
</table>
Model
and controller as below:
public ActionResult SearchResult(String sortOrder, String carMake, String carModel)
{
var cars = from d in db.Cars
select d;
ManageCarSearch objsearch = new ManageCarSearch();
////your Logic
return View(objsearch);
}
and you serach view look like below:
#model IEnumerable<ManageCarSearch>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
<label>Model</label>
<select id="ModelID" name="carModel">
<option>All Models</option>
</select>
<button type="submit" name="submit" value="search" id="SubmitID">Search</button>
}
I have created my own custom ValidationAttribute:
public class UrlValidationAttribute : ValidationAttribute
{
public UrlValidationAttribute() {}
public override bool IsValid(object value)
{
if (value == null)
return true;
var text = value as string;
Uri uri;
return (!string.IsNullOrWhiteSpace(text) &&
Uri.TryCreate(text, UriKind.Absolute, out uri));
}
}
I am using that on one of my models and it works perfectly. However, now I am attempting to use it on a view model:
public class DeviceAttribute
{
public DeviceAttribute(int id, attributeDefinition, String url)
{
ID = id;
Url = url;
}
public int ID { get; set; }
[UrlValidation]
public String Url { get; set; }
}
The view model is used in the partial view like this:
#model List<ICMDB.Models.DeviceAttribute>
<table class="editor-table">
#foreach (var attribute in Model)
{
<tr>
#Html.HiddenFor(a => attribute.ID)
<td class="editor-label">
#Html.LabelFor(a => attribute.Url)
</td>
<td class="editor-field">
#Html.TextBoxFor(a => attribute.Url)
#Html.ValidationMessageFor(a => attribute.Url)
</td>
</tr>
}
</table>
For some unknown reason, while the constructor for UrlValidationAttribute fires, the IsValid function doesn't fire. Any ideas?
Edit: On further investigation, it seems this is happening because the DeviceAttribute view model is actually the view model for a partial. The full page is passed a different view model that contains the list of DeviceAttribute view models. So when my controller action is called, the full page view model is constructed and its values filled, but no DeviceAttribute view models are constructed, hence why no validation is run.
I would recommend you using editor templates instead of writing foreach loops. I suppose that your main view model looks something like this:
public class MyViewModel
{
public List<DeviceAttribute> Devices { get; set; }
...
}
Now in your main view:
#model MyViewModel
#using (Html.BeginForm())
{
<table class="editor-table">
#Html.EditorFor(x => x.Devices)
</table>
<input type="submit" value="OK" />
}
and in the corresponding editor template (~/Views/Shared/EditorTemplates/DeviceAttribute.cshtml):
#model DeviceAttribute
<tr>
#Html.HiddenFor(x => x.ID)
<td class="editor-label">
#Html.LabelFor(x => x.Url)
</td>
<td class="editor-field">
#Html.TextBoxFor(x => x.Url)
#Html.ValidationMessageFor(x => x.Url)
</td>
</tr>
And your POST action takes the view model back:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
...
}
Now the default model binder will successfully bind all values in the view model and kick validation.
Here's a nice blog post about templates.