ASP.Net MVC: When form post then one view model property getting null - asp.net-mvc-4

i have simple form where one dropdown and one submit button. i have two index function one for get and one for form post. when i select a product from dropdown and click on submit button then my index action getting invoke but there i notice my products property getting null. see my code please and tell me where i made the mistake.
view code
#model AuthTest.Models.SampleViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DateValTest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Products, htmlAttributes: new { #class = "control-label col-md-2", style = "padding-top:0px;" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedProductId, new SelectList(Model.Products, "ID", "Name"), "-- Select Product--")
#Html.ValidationMessageFor(model => model.SelectedProductId, "", new { #class = "text-danger" })
</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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
model code
public class Product
{
public int ID { set; get; }
public string Name { set; get; }
}
public class SampleViewModel
{
[Display(Name = "Products")]
public List<Product> Products { set; get; }
[Required(ErrorMessage = "Select any one")]
public int SelectedProductId { set; get; }
}
controller code
public class TestValController : Controller
{
// GET: TestVal
public ActionResult Index()
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
return View(SampleVM);
}
[HttpPost]
public ActionResult Index(SampleViewModel vm)
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
if (ModelState.IsValid)
return View(vm);
else
return View(SampleVM);
}
}
when i debug second action then i saw vm products property getting null
please tell me where i made the mistake?
thanks

You are not making any mistake, You are not getting the list of products back because you are not including them in the HTML input form.
If you want to include the list of products you can add the following inside the input form
#for (int i = 0; i < Model.Products.Count(); i++)
{
<div>
#Html.HiddenFor(model => Model.Products[i].Name)
#Html.HiddenFor(model => Model.Products[i].ID)
</div>
}

#Mou,
Please modify your razor view and try this.
In Razor View nowhere you have specified the Http verb(Get,Post).
#using (Html.BeginForm("Index", "TestVal", FormMethod.Post)

Related

mvc model fileupadload does not send model data

I have a mvc 5 asp.net file upload that upload pictures and create path for them.
File uploaded successfully, but model data does comes in null.
This is my model:
[Table("Slider")]
public partial class Slider
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Slider()
{
Slider1 = new HashSet<Slider>();
}
public int ID { get; set; }
public string Path { get; set; }
public int? Slider_ID { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Slider> Slider1 { get; set; }
}
This is Controller part:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Path")] Slider slider, List<HttpPostedFileBase> FileContent)
{
if (ModelState.IsValid)
{
byte[] imageData = null;
if (Request.Files.Count > 0)
{
HttpPostedFileBase poImgFile = Request.Files["Path"];
using (var binary = new BinaryReader(poImgFile.InputStream))
{
imageData = binary.ReadBytes(poImgFile.ContentLength);
}
}
string picturePath = string.Format(Server.MapPath("~/content/slider/{0}.jpg"), slider.ID);
CreateDirectory(picturePath);
using (FileStream writer = new FileStream(picturePath, FileMode.Create))
{
writer.Write(imageData, 0, imageData.Length);
}
db.Sliders.Add(slider);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(slider);
}
And this is the view:
#using (Html.BeginForm("Create", "Sliders", FormMethod.Post, new { #class = "form-horizontal", role = "form", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="col-12 form-group">
<div class="row">
#Html.LabelFor(model => model.Path, "Picture", htmlAttributes: new { #class = "control-label col-12 col-md-2" })
<div class="col-12 col-md-10">
<input type="file" name="Path" id="fileUpload" accept=".png,.jpg,.jpeg,.gif" />
#Html.ValidationMessageFor(model => model.Path, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="form-group">
<div class="col-12 text-left">
<input type="submit" value="create" class="btn btn-success" /> | #Html.ActionLink("back to list", "Index", null, new { #class = "btn btn-primary" })
</div>
</div>
</div>
}
When I check my database I see that Path is :
System.Web.HttpPostedFileWrapper
and Slider_ID is null and Slider_ID1 is null too.
Any suggestions?
After searching my previous codes, I've found that, when saving changes to db in controller class, db.SaveChanges(); slider argument which had been sent to Create method, will get new ID.
I've added these 2 lines after if:
if (ModelState.IsValid)
{
db.Sliders.Add(slider);
db.SaveChanges();
and done my other business logic in lines after that.

How to get My Purchase Class to map to a Specific ApplicationUser

Here is the Idea. When an Admin is logged on they can pull up a list of all of the users.It will give the options for edit, details, delete like normal but I have added a link to Purchases like so:
#model IEnumerable<IdentitySample.Models.ApplicationUser>
#{
ViewBag.Title = "Index";
}
<div class="col-12 backgroundImg">
<div class="navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">List of Users</h2>
<h4 class="formText">
#Html.ActionLink("Create New ", "Create")
</h4>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Email)
</th>
<th>
#Html.DisplayNameFor(model => model.UserName)
</th>
<th>
#Html.DisplayNameFor(model => model.FavStrain)
</th>
<th>
</th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Email)
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
#Html.DisplayFor(modelItem => item.FavStrain)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = item.Id
}) |
#Html.ActionLink("Details", "Details", new { id =
item.Id }) |
#Html.ActionLink("Delete", "Delete", new { id =
item.Id }) |
#Html.ActionLink("Purchases", "PurchaseIndex", new {
id = item.Id})
</td>
</tr>
}
</table>
</div>
</div>
</div>enter code here
When you click the Purchases link it takes you to the PurchaseIndex page which looks like this:
Purchase List
#model IEnumerable<IdentitySample.Models.Purchases>
#{
ViewBag.Title = "Index";
}
<div class="col-12 backgroundImg navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">Index</h2>
<hr />
<div class="formHeaderSmall">
Total Points <br />
#Model.Sum(i => i.Points) </div>
<p class="formText">
#Html.ActionLink("Create New", "CreatePurchase")
</p>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Name)
</th>
<th>
#Html.DisplayNameFor(model => model.Points)
</th>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Points)
</td>
<td></td>
</tr>
}
</table>
<p class="formText">
#Html.ActionLink("Back to List", "Index")
</p>
</div>
</div>
It gives a list of Purchases and gives the total points that is why i didnt include a details page. Everything works right EXCEPT for the fact that the Purchases do not map to a specific user. If I create a new user and click Purchases it brings up a list of all of the purchases, not just the purchases specific for that user. How do I get a Purchase to map to a Specific User?
I have created a Purchases class that looks like this:
public class Purchases
{
[Key]
public int PurchaseId { get; set; }
[Required]
[Display(Name = "Product Name")]
[DataType(DataType.Text)]
public string Name { get; set; }
[Required]
[Range(0,5)]
[Display(Name = "Points")]
[DataType(DataType.Text)]
public int Points { get; set; }
public string ApplicationUserId { get; set; }
public virtual ApplicationUser Users { get; set; }
}
My ApplicationUser Class looks like this:
public class ApplicationUser : IdentityUser
{
[Display(Name ="Favorite Strain")]
[DataType(DataType.Text)]
public string FavStrain { get; set; }
public virtual List<Purchases> Purchase { get; set; }
Now up to this point the Database is registering the Foreign Key of the Purchases Class to the ApplicationUser class like it is supposed to.
I can create a new purchase and display them to a list and all of the Crud Operations work just fine.
The problem is when I create a new Purchase it doesn't include the ApplicationUserId in the Database it returns a Null.
Null Database
I am pretty sure that the problem is in my Controller. I have tried just about everything so I don't want to include the failed try's so here is the Controllers as they are now and working.
There is no need for me to include the edit or details because I am not going to give the users that access.
public ActionResult CreatePurchase()
{
return View();
}
private ApplicationDbContext db = new ApplicationDbContext();
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include = "PurchaseId,Name,Points,Id")] Purchases purchases)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
// GET: Purchases/Edit/5
public ActionResult PurchaseIndex()
{
var userDetails = db.Purchases.Include(u => u.Users);
return View(db.Purchases.ToList());
}
This is my first Question on Stack Overflow so forgive me if something isn't right.
**************************************Update************************************
This is my PurchaseIndexController. Now this returns only the user associated with the purchase. However it is always 0 because there is no UserID. If I try using an int? type or Guid? it gives an error. Cannot implicitly convert type int to string.
public ActionResult PurchaseIndex(string ID)
{
//this gets all purchases for a certain individual
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
ID).ToList();
return View(userDetails);
}
Here is the CreatePurchase View
#model IdentitySample.Models.Purchases
#{
ViewBag.Title = "Create";
}
<div class="col-12 backgroundImg navbarSpace">
<div class="col-12 formBackground">
<h2 class="formHeader">Add a New Purchase</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#*#Html.Hidden("id", (string)ViewBag.UserID)*#
#Html.HiddenFor(model => model.ApplicationUserId)
<div class="form-horizontal">
<div class="col-12">
#Html.LabelFor(model => model.Name, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Name, "", new
{ #class = "text-danger" })
</div>
</div>
<div class="col-12">
#Html.LabelFor(model => model.Points, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-md-10">
#Html.EditorFor(model => model.Points, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Points, "",
new { #class = "text-danger" })
</div>
</div>
<div class="col-12">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-
default" />
</div>
</div>
</div>
}
<div class="formText">
#Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
I also have link in the Manage section for the users to check thier points and purchases but I don't know how to create an ActionLink for this to just get purchases associated with the user.
Here is the controller
public ActionResult WeedPoints(string ID)
{
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
ID).ToList();
return View(userDetails);
}
Here is the Action Link now.
<div class="col-12 formHeaderSmall">#Html.ActionLink("My
Purchases/Points", "WeedPoints", "Manage")</div>
*********************************Update****************************************
Here is the Controllers with the View Bag reference. The Create Purchase View has the ViewBag I just Uncommented it out.
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
public ActionResult CreatePurchase(string Id)
{
ViewBag.UserID = Id;
//ApplicationDbContext db = new ApplicationDbContext();
//var userDetails = db.Purchases.Where(x => x.ApplicationUserId == Id;
return View();
}
private ApplicationDbContext db = new ApplicationDbContext();
//POST: Purchases/Create
[HttpPost]
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include =
"PurchaseId,Name,Points,ApplicationUserId")] Purchases
purchases,string id)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
[Authorize(Roles =
"Admin,DispensaryManager,DispensaryEmployee,DispensaryEastEmployee")]
public ActionResult PurchaseIndex(string Id)
{
//this gets all purchases for a certain individual
ApplicationDbContext db = new ApplicationDbContext();
var userDetails = db.Purchases.Where(x => x.ApplicationUserId ==
Id).ToList();
ViewBag.UserID = Id;
return View(userDetails);
}
***************************Total Refactor*********************************8
Here is the new controller in its entirety.
public class PurchasesController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Purchases
public ActionResult Index()
{
var purchases = db.Purchases.Include(p => p.Users);
return View(purchases.ToList());
}
// GET: Purchases/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
return View(purchases);
}
// GET: Purchases/Create
public ActionResult Create()
{
ViewBag.Users = new SelectList(db.Users, "Id", "UserName");
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (ApplicationUser user in db.Users)
{
SelectListItem selectListItem = new SelectListItem
{
Text = user.UserName,
Value = user.Id.ToString()
};
selectListItems.Add(selectListItem);
}
//ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName");
return View();
}
// POST: Purchases/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 ActionResult Create([Bind(Include =
"PurchaseId,Name,Points,TotalPoints,ApplicationUserId")] Purchases
purchases)
{
if (ModelState.IsValid)
{
var totalPoints = db.Purchases.Sum(x => x.Points);
purchases.TotalPoints = totalPoints;
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// GET: Purchases/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// POST: Purchases/Edit/5
// 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 ActionResult Edit([Bind(Include =
"PurchaseId,Name,Points,TotalPoints,ApplicationUserId")] Purchases
purchases)
{
if (ModelState.IsValid)
{
db.Entry(purchases).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.ApplicationUserId = new SelectList(db.Users, "Id",
"UserName", purchases.ApplicationUserId);
return View(purchases);
}
// GET: Purchases/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Purchases purchases = db.Purchases.Find(id);
if (purchases == null)
{
return HttpNotFound();
}
return View(purchases);
}
// POST: Purchases/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Purchases purchases = db.Purchases.Find(id);
db.Purchases.Remove(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
Now there is a dropdown list of users to choose from when you create a new purchase. Here is the Create View.
<div class="col-12 backgroundImg navbarSpace scrollBar">
<div class="formBackground col-12">
<h2 class="formHeader">Edit Puchase</h2>
<hr />
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger"
})
#Html.HiddenFor(model => model.PurchaseId)
#Html.HiddenFor(model => model.TotalPoints)
<div class="col-12">
#Html.LabelFor(model => model.Name, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.Name, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Name, "", new
{ #class = "text-danger" })
</div>
</div>
<div class="col-12">
#Html.LabelFor(model => model.Points, htmlAttributes: new {
#class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.Points, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model => model.Points, "",
new { #class = "text-danger" })
</div>
</div>
#*<div class="col-12">
#Html.LabelFor(model => model.TotalPoints,
htmlAttributes: new { #class = "formText col-12" })
<div class="col-12">
#Html.EditorFor(model => model.TotalPoints, new {
htmlAttributes = new { #class = "col-12" } })
#Html.ValidationMessageFor(model =>
model.TotalPoints, "", new { #class = "text-danger" })
</div>
</div>*#
<div class="col-12">#Html.LabelFor(model => model.ApplicationUserId,
"Users", htmlAttributes: new { #class = "formText col-12" })
<div class="col-12"> #Html.DropDownList("Users", null, htmlAttributes:
new { #class = "col-12" })
#Html.ValidationMessageFor(model => model.ApplicationUserId, "", new {
#class = "text-danger" })
</div>
</div>
<div class="col-12">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div class="formText"> #Html.ActionLink("Back to List", "Index")
</div>
</div>
</div>
This creates a drop down list of users displaying their User Name. When I select a user and hit save I get an error saying that
There is no ViewData item of type 'IEnumerable' that has the key 'Id'.
Is the 'Id' being passed to this method null?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreatePurchase([Bind(Include = "PurchaseId,Name,Points,Id")] Purchases purchases)
{
if (ModelState.IsValid)
{
db.Purchases.Add(purchases);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(purchases);
}
If it is null, the userID should be included (as a hidden field) in the form you are posting. Then (once the userID is populated in the DB) you should be able to get only the purchase associated with the userID, doing something like this:
var userDetails = db.Purchases.Where(x=>x.ApplicationUserId == ID).ToList();
The problem you are having is that the 'Create new purchase' action is not passing a user id, it is currently:
#Html.ActionLink("Create New", "CreatePurchase")
Whereas it needs to be this to pass an id:
#Html.ActionLink("Create New", "CreatePurchase", new {
id = Model.Id})
However this assumes that an id has been passed to the purchase index view in the model for that page, which is likely not the case but I can't tell as I can't see your purchase index action. The simplest way to pass it for you is through a viewbag, however I do not recommend using this for your site if you intend to use it seriously. The correct way to handle data across your views would be using viewmodels. There are a lot of tutorials available, e.g. https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-3
Using the CRUD implementation you have you can just pass the id to the page using a weakly type viewbag. Your purchase index action should look something like this:
public ActionResult Index(string id)
{
//this checks to see if an id has been passed to the action
if (id != null){
//this gets all purchases for a certain individual
var purchases = db.purchases.Where(i => i.ApplicationUserId == id).ToList()
//this gets the user id passed to the action and sticks it in a viewbag you can retrieve later on the page
ViewBag.UserID == id;
//returns the view with the list above
return View(purchases);
}
else{
//no id was passed to the action so the list is for all purchases
var purchases = db.purchases.ToList();
return View(purchases);
}
}
Now in your view you need to amend the create new purchase action to include the viewbag item:
#Html.ActionLink("Create New", "CreatePurchase", new {
id = ViewBag.UserID})
Change your create purchase action to accept the user id you are passing:
public ActionResult CreatePurchase(string id)
{
//puts the id in a viewbag to again be used by the view
ViewBag.UserID == id;
return View();
}
Then on your create purchase view you need to pass the viewbag item into the model, you do this by having a hidden field somewhere inside the form:
#Html.Hidden("id", (string)ViewBag.UserID)
I'm converting the viewbag into a string because assuming your are using ASP NET identity the user id is a string and ViewBag is a dynamic object, so needs to be turned into a string before you can put it into the model.id space effectively. This will then pass the user ID to the post action and a purchase will be created specific to the id.
Bear in mind, this is a terrible way to be doing this, the default CRUD stuff whilst handy isn't really that great for production because you are accessing models directly and you will need to use weakly typed ViewBags to transfer data. It's error prone and insecure.

MVC partial view wtih different model

I have model as
public class MainDataViewModel
{
[Required]
[Display(Name = "Select Work Orders")]
public string[] SelectedWorkOrdersValues { get; set; }
public MultiSelectList WorkOrderIds { get; set; }
public IEnumerable<ORDERMASTER> ordersDetails;
}
And Main View as
#model InventoryEasy15.Models.MainDataViewModel
<div class="box-body">
<div class="col-md-6">
<div class="form-group">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<label for="fileToUpload">Select the Work Orders</label>
#Html.ValidationMessageFor(m => m.WorkOrderIds, "", new { #class = "text-danger" })
#Html.ListBoxFor(m => m.SelectedWorkOrdersValues, Model.WorkOrderIds as MultiSelectList, new { id = "WorkOrders", #class = "form-control", data_placeholder = "Choose Work Orders..." })
</div>
</div>
</div>
<!-- /.box-body -->
<div class="box-footer">
<input type="submit" value="Get WorkOrder Details" id="btnSubmit" class="btn btn-primary">
</div>
</div>
</div>
</div>
#Html.Partial("MainDataWorkOrderDetails", Model.ordersDetails)
And the Partial view as
#model IEnumerable<InventoryEasy15.ORDERMASTER>
<div id="myDisplayID"><div>
Now I am getting error as
The model item passed into the dictionary is of type 'InventoryEasy15.Models.MainDataViewModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[InventoryEasy15.ORDERMASTER]'.
Any thoughts.
The controller here is
public async Task<ActionResult> MainDataWorkOrderDetails(MainDataViewModel m)
{
var model = new MainDataViewModel();
var result = await db.ORDERMASTERs.Where(x => x.WOID == "WO7446708").ToListAsync();
if (result != null)
{
model.ordersDetails = result;
}
return PartialView(model);
}
You are passing model to the PartialView. Now, the model is of type MainDataViewModel, and your partial view expects the model of type IEnumerable<InventoryEasy15.ORDERMASTER>
return PartialView(model);
I think you should consider passing model.orderDetails to the partial view from your action.
return PartialView(model.orderDetails);
Or else, simply return the View containing the partial view if you want to pass the whole model

ajax beginform mvc callback

I have a single page with multiple partials set up. I want to be able to validate and update each partial seperatly. The validation works BUT when I type in a correct value and press save the page goes to the partial view instead of staying on the single page. What am I doing wrong here?
This is my main page :
#for (var i = 0; i < 10; i++)
{
var idTest = "Test_" + i;
<div id="#idTest">
#Html.Action("Detail", new { id = i })
</div>
}
The partial is created like this:
#{
var idTest = "Test_" + Model.Id;
var ajaxOptions = new AjaxOptions
{
UpdateTargetId = #idTest,
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace
};}
#using (Ajax.BeginForm("Detail", ajaxOptions))
{ #Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Test</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Gemeente, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextBoxFor(model => model.Gemeente, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Gemeente, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
This is the simple model:
public class Test
{
public int Id { get; set; }
[Required(ErrorMessage = "Gelieve een gemeente op te geven")]
public string Gemeente { get; set; }
}
These are the actions:
[HttpGet]
public ActionResult Detail(int id)
{
Models.Test model = new Models.Test();
model.Id = id;
return View(model);
}
[HttpPost]
public ActionResult Detail(Models.Test model)
{
if(ModelState.IsValid)
{
return PartialView(model);
}
return PartialView(model);
}
Add these lines to your view and also use #Html.Partial as shown below
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
so that your main view is
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
#for (var i = 0; i < 10; i++)
{
var idTest = "Test_" + i;
<div id="#idTest">
#Html.Partial("Detail", new Test { Id = i })}
</div>
}
Scripts would be for unobtrusive ajax so that your ajax button works and Html.Partial so that first time when you load your page in foreach only partial view is rendered (not the full view)

Return selected objects for MultiSelect DropDownList

I have this DropDownListFor added to my (partial) View _CreateUser.cshtml as:
<div class="modal fade bs-example-modal-lg" id="createUserModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Create A User</h4>
</div>
#using (Html.BeginForm("Create", "Users", FormMethod.Post))
{
<div class="modal-body">
#Html.LabelFor(model => model.Departments)
<div>
#Html.DropDownListFor(
model => model.SelectedDepartmentIds,
new SelectList(Model.DepartmentSelectList, "Value", "Text"),
new {#class = "multiselect departments", multiple = "multiple"})
</div>
...
Above partial view is declared in main View Index.cshtml and is shown as modal popup on click of 'Create' button
#Html.ActionLink("Create user", "Create", "Users", new { #class = "btn btn-primary", #href = "#createUserModal", #data_toggle="modal" })
...
#Html.Partial("_CreateUser", new UserViewModel()
{ //Populating SelectList required for loading dropdownlist
DepartmentSelectList = modelFactory != null ?
modelFactory.Departments.Select(dept => new SelectListItem{Value = dept.Id.ToString(),Text = dept.Name}).ToList()
: null
})
UserViewModel.cs
public IEnumerable<String> Departments { get; set; }
//Used purely for binding DropDownList
public IEnumerable<SelectListItem> DepartmentSelectList { get; set; }
public string SelectedDepartmentIds { get; set; }
//Ideally, I want to populate selected items in this list
public IEnumerable<DepartmentModel> DepartmentModels { get; set; }
UserController.cs
[HttpPost]
public ActionResult Create(UserViewModel postedModel)
{
if (ModelState.IsValid)
{
//Incorrect - returns only first selected Id
string selectedIds = postedModel.SelectedDepartmentIds;
//Correct - returns correct selected Ids
string selectedIds1 = ModelState["SelectedDepartmentId"].Value.AttemptedValue;
...
...
Two questions:
How can I retrieve all selected items from DropDownList as part of model property "SelectedDepartmentId"? Do I need to use jQuery to update the ModalProperty at run-time?
Can I setup my View so as to bind Modal property "DepartmentModels" with DropDownList - It will help me to retrieve complete objects for selected items?
Currently, if I try to do that, I get this error:
{"The parameter conversion from type 'System.String' to type '...DepartmentModel' failed because no type converter can convert between these types."}
Thank you!
Resolved after looking through various Google searches :-)
Below is the code that loads and returns selected objects correctly -
Model:
public class UserGroupViewModel
{
public IEnumerable<SelectListItem> GroupSelectList
{
get
{
List<Group> groups = new List<Group>
{
new Group() {Id = 1, Name = "AA"},
new Group() {Id = 2, Name = "BB"},
new Group() {Id = 3, Name = "CC"}
};
IEnumerable<SelectListItem> groupsSelectList = groups.Select(group => new SelectListItem { Text = group.Name, Value = group.Id.ToString(), Selected = group.Id == 2}).ToList();
return groupsSelectList;
}
}
[Required]
[Display(Name = "Group")]
public int[] SelectedGroupIds { get; set; }
}
View:
<div class="form-group">
#Html.LabelFor(model => model.SelectedGroupIds, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedGroupIds,
Model.GroupSelectList,
new { #class = "multiselect", multiple = "multiple" })
#Html.ValidationMessageFor(model => model.SelectedGroupIds)
</div>
</div>
For showing some of the items as 'Selected' on form load, simply set the SelectListItem's 'Selected' property to true.
HTH.