I created a partial view that should display a list of user with a check box , so i can reuse this partial view in various pages.
The problem is that, i'm not able to have the correct htmlprefix the input generated
(I would like to remove the . of the prefix )
Model:
public class CircleEditViewModel
{
[Key]
public int CircleId { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
public bool IsSystem { get; set; }
public List<SimpleUserListViewModel> Users { get; set; }
public CircleEditViewModel()
{
Users = new List<SimpleUserListViewModel>();
}
}
public class SimpleUserListViewModel
{
public SimpleUserListViewModel()
{
}
public SimpleUserListViewModel(User user)
{
this.UserId = user.UserId;
FullName = user.FullName;
}
public int UserId { get; set; }
public byte[] Picture { get; set; }
public string FullName { get; set; }
public bool IsCheckedForAction { get; set; }
}
'Main view':
#model Wims.Website.ViewModels.CircleEditViewModel
<script type="text/javascript">
$(document).ready(function () {
$.validator.unobtrusive.parse('form');
});
</script>
#using (Ajax.BeginForm(Html.ViewContext.RouteData.Values["Action"].ToString(), null, new AjaxOptions { HttpMethod = "POST", OnSuccess = "SaveDone(data)" }, new { id = "editform" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Circle</legend>
#Html.Label(DateTime.Now.ToString());
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
#Html.Partial("~/Views/Shared/_UserList.cshtml", Model.Users,
new ViewDataDictionary(Html.ViewDataContainer.ViewData)
{
TemplateInfo = new System.Web.Mvc.TemplateInfo { HtmlFieldPrefix = "Users" }
})
#Html.GenerateSecureDataControls(model => model.CircleId)
<input type="submit" value="Save" />
}
Partial view:
#model List<Wims.Website.ViewModels.Shared.SimpleUserListViewModel>
#{
if (Model != null)
{
for (int i = 0; i < Model.Count; i++)
{
<div class="userDetail">
<div>
<div>
#Html.CheckBoxFor(model => model[i].IsCheckedForAction)
</div>
<div class="iconDiv">
#Html.Image("~/Content/Images/defaultUser.jpg", Model[i].FullName, null)
</div>
<div>
#Html.TextBoxFor(model => model[i].FullName)
#Html.HiddenFor(model => model[i].UserId)
</div>
</div>
</div>
<div style="clear: both"></div>
}
}
}
I am almost there, the input generated id's are
id="Users.[0].FullName
Is there any way i can remove the first dot?
I've found some solution yesterday on a blog (which i can't find anymore...) but it was for MVC3 and I couldn't make it work anyway...
Thanks for the help!
EDIT:
Maybe I should use EditorFor instead of partial view:
.NET MVC 4 Strongly typed ViewModel containing Strongly typed Model with EditorFor and EditorTemplate partial view not binding
Will check tonight
Alrighty, The EditorFor worked perfectly..
I need to read more about this.
Related
I have the following ViewModel:
public class ActivityReportViewModel
{
public Dictionary<int, List<string>> Periods { get; set; }
public List<Project> Projects { get; set; }
public List<Templates> Templates { get; set; }
public DateTime TimePeriod { get; set; }
}
public class Project
{
public string Customer { get; set; }
public string ProjectNumber { get; set; }
public string ProjectDescription { get; set; }
public bool IsSelected { get; set; }
public int TemplateId { get; set; }
public bool XLSX { get; set; }
public bool PDF { get; set; }
}
I fill this ViewModel in my controller and then send it to my Create view, which works fine and the values of the Projects property are all there. However, when I postback the data to the server, the values are gone. I tried supplying HiddenFields to all properties of each Project to no avail. Here's my relevant view markup:
<div>
#Html.LabelFor(model => model.Projects, htmlAttributes: new { #class = "ms-Label" })
<ul class="ms-List" style="list-style:none;">
#for (int x = 0; x < Model.Projects.Count; x++)
{
<li class="ms-ListItem">
<span class="ms-ListItem-primaryText">#Model.Projects[x].ProjectDescription</span>
<span class="ms-ListItem-secondaryText">#Model.Projects[x].Customer</span>
<span class="ms-ListItem-tertiaryText">#Model.Projects[x].ProjectNumber</span>
#*<div class="ms-ListItem-selectionTarget js-toggleSelection"></div>*#
#Html.HiddenFor(m => Model.Projects[x].IsSelected)
#Html.HiddenFor(m => Model.Projects[x].ProjectDescription)
#Html.HiddenFor(m => Model.Projects[x].Customer)
#Html.HiddenFor(m => Model.Projects[x].ProjectNumber)
#Html.HiddenFor(m => Model.Projects[x].XLSX)
#Html.HiddenFor(m => Model.Projects[x].PDF)
<div class="ms-Dropdown">
<i class="ms-Dropdown-caretDown ms-Icon ms-Icon--caretDown"></i>
#Html.DropDownListFor(m => m.Projects[x].TemplateId, new SelectList(Model.Templates, "Id", "Name"), new { #class = "ms-Dropdown-select" })
</div>
<div class="ms-ChoiceField">
<input id="excel+#Model.Projects[x].ProjectNumber" class="ms-ChoiceField-input" value="#Model.Projects[x].XLSX" type="checkbox">
<label for="excel+#Model.Projects[x].ProjectNumber" class="ms-ChoiceField-field"><span class="ms-Label is-required">Excel</span></label>
</div>
<div class="ms-ChoiceField">
<input id="pdf+#Model.Projects[x].ProjectNumber" class="ms-ChoiceField-input" value="#Model.Projects[x].PDF" type="checkbox">
<label for="pdf+#Model.Projects[x].ProjectNumber" class="ms-ChoiceField-field"><span class="ms-Label is-required">PDF</span></label>
</div>
</li>
}
</ul>
<div>
#Html.ValidationMessageFor(model => model.Projects, "", new { #class = "text-danger" })
</div>
</div>
EDIT:
Here's my POST action method:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ActivityReportViewModel report)
{
using (AppContainer _db = new AppContainer())
{
if (ModelState.IsValid)
{
_db.SaveChanges();
return RedirectToAction("Index");
}
return PartialView(report);
}
}
The DefaultViewModel Binder uses the HTML attribute name to determine which property to bind back to on the server. I cannot see name attribute specified on your input element. Please specify name attribute on the elements you wish to post back to the server with the property of the view model.
Specifiy name attribute as below. Notice I have added name attribute with value as the property of your view model
<input id="excel+#Model.Projects[x].ProjectNumber" class="ms-ChoiceField-input" name="#Model.Projects[x].ProjectNumber" value="#Model.Projects[x].XLSX" type="checkbox">
This question already has answers here:
ASP.NET MVC 4 - for loop posts model collection properties but foreach does not
(2 answers)
Closed 8 years ago.
I am new in ASP.MVC4 and I am facing with problem to pass object from view to controller:
Let me explain my problem from beginning:
My class which is used by is for example: UserAndRolesModel
public class UserAndRolesModel
{
public user User { get; set; }
public IEnumerable<UserAndRoles> AsignedRoles { get; set; }
}
As You can see class UserAndRolesModel persists of 2 objects: user and Enumerable
public class user
{
[HiddenInput(DisplayValue=false)]
public virtual int user_id { get; set; }
[Required(ErrorMessage = "Please provide name")]
[Display(Name = "Name")]
public virtual string user_name { get; set; }
[Display(Name = "Is active?")]
public virtual bool user_active { get; set; }
}
public class UserAndRoles
{
public string RoleName { get; set; }
public bool IsAssigned { get; set; }
}
My controller action is simple, it assign user to UserWithRoles.User and creates tempList with UserAndRoles objects.
public ActionResult Edit(int userid=0)
{
//object which will be passed to View
UserAndRolesModel UserWithRoles = new UserAndRolesModel();
//user
UserWithRoles.User = repoUsers.Users.FirstOrDefault(x => x.user_id == userid);
//roles
IList<UserAndRoles> tempList = new List<UserAndRoles>();
UserAndRoles temp1 = new UserAndRoles();
temp1.RoleName="Admin";
temp1.IsAssigned=true;
UserAndRoles temp2 = new UserAndRoles();
temp2.RoleName="User";
temp2.IsAssigned=false;
tempList.Add(temp1);
tempList.Add(temp2);
//assign tempList to model
UserWithRoles.AsignedRoles = tempList;
return View(UserWithRoles);
)
At this stage I am successfully passing to View:
UserWithRoles.User.user_id=1;
UserWithRoles.User.user_name="UserName1";
UserWithRoles.User.user_active=true;
UserWithRoles.AsignedRoles[1].RoleName = "Admin";
UserWithRoles.AsignedRoles[1].IsAssigned = true ;
UserWithRoles.AsignedRoles[2].RoleName = "User";
UserWithRoles.AsignedRoles[2].IsAssigned = false;
I am able to display above View properly:
#model Models.UserAndRolesModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<div class="editor-field">
#Html.EditorFor(model => model.User.user_id)
#Html.ValidationMessageFor(model => model.User.user_id)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.user_name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.User.user_name)
#Html.ValidationMessageFor(model => model.User.user_name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.User.user_active)
</div>
<div class="editor-field">
#Html.CheckBoxFor(model => model.User.user_active)
#Html.ValidationMessageFor(model => model.User.user_active)
</div>
<br /> ROLES below is piece of code which makes me cry<br />
#foreach (var item in Model.AsignedRoles)
{
<div class="editor-field">
<div class="editor-field">
#Html.LabelFor(model => item.RoleName)
#Html.CheckBoxFor(model => item.IsAssigned)
</div>
</div>
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
When i Click submit all data regarding user was passed properly but IEnumerable AsignedRoles is always null.
Here is my controller method on post:
[HttpPost]
public ActionResult Edit(UserAndRolesModel UserWithRoles)
{
if (ModelState.IsValid)
{
if (UserWithRoles.AsignedRoles==null)
Console.WriteLine("I am still crying");
else
Console.WriteLine("Got it!");
}
return View(UserWithRoles);
}
In View I tried to use other loops for example:
#for (int i = 0; i < Model.AsignedRoles.Count(); i++)
{
<div class="editor-field">
#Html.LabelFor(model => item[i].IsAssigned)
#Html.CheckBoxFor(model => item[i].IsAssigned)
</div>
}
But above also does not pass IEnumerable.
Can anyone help me to resolve this issue? How can I pass back to controller UserAndRolesModelwhich contains IEnumerable?
I will be very grateful. Advance thanks for the help!
You do need the for loop, but the one you tried you have referenced item[i], yet item[i] no longer exists. Try this, note that I have also added a HiddenFor for RoleName otherwise that won't get passed back:
#for (int i = 0; i < Model.AsignedRoles.Count(); i++)
{
<div class="editor-field">
#Html.LabelFor(model => model.AssignedRoles[i].IsAssigned)
#Html.CheckBoxFor(model => model.AssignedRoles[i].IsAssigned)
#Html.HiddenFor(model => model.AssignedRoles[i].RoleName)
</div>
}
I am trying to learn ASP.net by making an e-commerce site. I am trying to set up the ability to create Items and assign Images to the item being created via File Upload.
I managed to get the multiple file upload working, but only to the content/Images folder. I cant figure out out to marry this to the creation of Items so you can assign multiple images to an Item all in the creation of the item.
It would be a fair to say I dont know where to go from here and would appreciate any help.
Item Model Class: Table in the database to store each item. Is referenced from the Images Table with a 1 to many relationship.
public class Item
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ItemId { get; set; }
public int CategoryId { get; set; }
public int DesignerId { get; set; }
public int ImageId { get; set; }
[Required]
[MaxLength(250)]
public string ItemName { get; set; }
[Required]
[Range(0,9999)]
public decimal ItemPrice { get; set; }
[MaxLength(1000)]
public string ItemDescription { get; set; }
[Range(4,22)]
public int ItemSize { get; set; }
[Column("CategoryId")]
public virtual List<Category> Category { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
public virtual List<Image> Images { get; set; }
}
Image Model Class: Stores the URL for each Image in the content Directory of the site. Can have many images for each Item.
public class Image
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ImageId { get; set; }
[Required]
public string ImageURL { get; set; }
[Required]
public string ItemId { get; set; }
//Files Being Uploaded by the User
public IEnumerable<HttpPostedFileBase> Files { get; set; }
[Column("ItemId")]
public virtual List<Item> Item { get; set; }
}
Store Manager Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Item item,HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
//The below successfully saves the file to the content folder when separated into the Index Action.
foreach (var f in item.Files)
{
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(f.FileName);
var path = Path.Combine(Server.MapPath("~/Content/ItemImages/"+item), fileName);
file.SaveAs(path);
}
}
// The below also works when I dont have the Above in the Action.
db.Items.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(item);
}
Create Item View
#model Project.Models.Item
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Item</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ItemName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ItemName)
#Html.ValidationMessageFor(model => model.ItemName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ItemPrice)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ItemPrice)
#Html.ValidationMessageFor(model => model.ItemPrice)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ItemDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ItemDescription)
#Html.ValidationMessageFor(model => model.ItemDescription)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ItemColour)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ItemColour)
#Html.ValidationMessageFor(model => model.ItemColour)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ItemSize)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ItemSize)
#Html.ValidationMessageFor(model => model.ItemSize)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div>
<table>
<tr>
<td>Files</td>
<td><input type="file" name="Files" id="Files" multiple/></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name="submit" value="Upload" /></td>
</tr>
</table>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
So it looks like you're pretty close.
You just have to add the images to the item before adding it to the database.
Since you're using EF, it should be something similar to this
//in your action
//add the images to the item
item.Images.Add(new Image { ImageUrl = ... });
//you should be able to just insert the whole entity graph here
db.Items.Add(item);
db.SaveChanges();
Something like that I think is what you're looking for.
Also in your model constructor normally I think you want to initalize those lists so you don't get null reference execeptions when doing something like the above
public class Item
{
public Item()
{
this.Images = new List<Image>();
}
//...
}
I'm trying to get a dropdown menu along side my other text inputs, I've been tracking a music store tutorial for this specific feature, but I cant seem to get mine to work. I've been revising my code and comparing for hours but I cant see my errors, If anyone can help it would be greatly appreciated.
Here is my controller code:
// GET: /Default1/Create
public ActionResult Create()
{
ViewBag.CardTypeId = new SelectList(db.CardTypee, "CardTypeId", "Type");
return View();
}
//
// POST: /Default1/Create
[HttpPost]
public ActionResult Create(Card card)
{
if (ModelState.IsValid)
{
db.Cards.Add(card);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CardTypeId = new SelectList(db.CardTypee, "CardTypeId", "Type", card.CardTypeId);
return View(card);
}
My models that I have:
Card.cs
namespace ActualPayment.Models
{
public class Card
{
public int Id { get; set; }
public CardType CardType { get; set; }
public int CardTypeId { get; set; }
public string Name { get; set; }
public string CardNumber { get; set; }
public int SortCode { get; set; }
public int SecurityCode { get; set; }
public int ExpirationDate { get; set; }
}
}
CardType.cs
namespace ActualPayment.Models
{
public partial class CardType
{
[Key] public int CardTypeId { get; set; }
public string Type { get; set; }
}
}
CardTypes.cs
namespace ActualPayment.Models
{
public class CardTypes : DropCreateDatabaseIfModelChanges<CardPayment>
{
protected override void Seed(CardPayment context)
{
var cardType = new List<CardType>
{
new CardType { Type = "Visa/Delta/Electron" },
new CardType { Type = "Master Card/Euro Card" },
new CardType { Type = "American Express" },
new CardType { Type = "Solo/Maestro" },
new CardType { Type = "Maestro" },
};
}
}
}
CardPayments.cs
namespace ActualPayment.Models
{
public class CardPayment : DbContext
{
public DbSet<CardType> CardTypee { get; set; }
public DbSet<Card> Cards { get; set; }
}
}
And my view:
#model ActualPayment.Models.Card
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"> </script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Card</legend>
<div class="editor-label">
#Html.LabelFor(model => model.CardTypeId, "CardType")
</div>
<div class="editor-field">
#Html.DropDownList("CardTypeId", String.Empty)
#Html.ValidationMessageFor(model => model.CardTypeId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CardNumber)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CardNumber)
#Html.ValidationMessageFor(model => model.CardNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SortCode)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SortCode)
#Html.ValidationMessageFor(model => model.SortCode)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SecurityCode)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.SecurityCode)
#Html.ValidationMessageFor(model => model.SecurityCode)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ExpirationDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ExpirationDate)
#Html.ValidationMessageFor(model => model.ExpirationDate)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Seems like there's nothing wrong with the code.
Chrome capture for posted values
value at action
Did you mean the CardTypeId is not getting populated??
For registering a new customer I have several partials on my view which refer to corresponding tables in my database.
Primary keys are identity fields, that's why no IDs on the view. To insert a new customer I need to insert 3 occurrences of address(visiting, postal and contact person's), then insert a row for contract, contact_person(with using addressId from one of already inserted addresses) and finally proceed with inserting a new customer which will contain foreign keys referencing to just inserted visiting and postal addresses, contract and contact person.
Could you recommend the best/easiest way to pass the data from these partial views as objects to CustomerController and handle the objects there please?
Links to examples of handling similar situations will be also highly appreciated.
Image of my page:
http://i1345.photobucket.com/albums/p673/swell_daze/customer_registration_zps563341d0.png
Image of tables:
http://i1345.photobucket.com/albums/p673/swell_daze/tables_zpsc60b644a.png
View code:
#model CIM.Models.customer
<h2>Register new customer</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>New customer registration</legend>
<fieldset class="span3">
<legend>Basic information</legend>
<div class="editor-label">
#Html.LabelFor(model => model.name, "Name")
</div>
<div class="editor-field">
#Html.EditorFor(model => model.name)
#Html.ValidationMessageFor(model => model.name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.groupId, "Customer group")
</div>
<div class="editor-field">
#Html.DropDownList("groupId", String.Empty)
#Html.ValidationMessageFor(model => model.groupId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.status, "Status")
</div>
<div class="editor-field">
#Html.DropDownList("status")
#Html.ValidationMessageFor(model => model.status)
</div>
</fieldset>
<fieldset class="span3">
<legend>Visiting address</legend>
<div>
#{
Html.RenderPartial("AddressPartial");
}
</div>
</fieldset>
<fieldset style="width:270px">
<legend>Postal address</legend>
<div>
#{
Html.RenderPartial("AddressPartial");
}
</div>
</fieldset>
<fieldset class="span3">
<legend>Contract</legend>
<div>
#{
Html.RenderPartial("ContractPartial");
}
</div>
</fieldset>
<fieldset class="span3" style="width:540px">
<legend>Contact person</legend>
<div>
#{
Html.RenderPartial("ContactPersonPartial");
}
</div>
<p>
<input type="submit" class="btn btn-success" value="Submit" style="margin-right:1em"/>
#Html.ActionLink("Cancel", "Index", "Customer", new {#class="btn btn-danger", #type="button"})
</p>
</fieldset>
</fieldset>
}
you can wrap your view data in a viewmodel, then when you submit your data it will be mapped to your viewmodel, something like this:
Create a viewmodel:
public class MyViewModel
{
public string name { get; set; }
public string someprop1 { get; set; }
public string someprop2 { get; set; }
public MyAddress visitingaddress { get; set; }
public MyAddress postaladdress { get; set; }
public MyContactAddress contactaddress { get; set; }
}
public class MyAddress
{
public string town { get; set; }
public string street { get; set; }
public string housenumber { get; set; }
public string postalcode { get; set; }
}
public class MyContactAddress : MyAddress
{
public string firstname { get; set; }
public string lastname { get; set; }
public string email { get; set; }
public string phonenumber { get; set; }
}
Create your view just like you have done but by using your viewmodel instead of the model you are using now (CIM.Models.customer), and then in your partials, for example in the postal address partial view do something like this:
.......
#Html.EditorFor(x => x.postaladdress.town)
......
Create your controller actions by using your viewmodel:
public ActionResult Index()
{
MyViewModel vm = new MyViewModel();
//doing something here....
return View(vm);
}
[HttpPost]
public ActionResult Index(MyViewModel vm)
{
//save your data, here your viewmodel will be correctly filled
return View(vm);
}
hope this helps