I have a little blog application with posts and tags. This is my model for Post:
namespace HelloWorld.Models
{
public class Post
{
[Required]
[DataType(DataType.Text)]
public string Title { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime PostDate { get; set; }
public List<Tag> Tags { get; set; }
[Required]
public int PostId { get; set; }
}
public class CreatePostView
{
[Required]
[DataType(DataType.Text)]
public string Title { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Display(Name = "Tags")]
[Required(ErrorMessage = "Please select a tag")]
public string SelectedTag { get; set; }
public SelectList TagList { get; set; }
[Required]
public int PostId { get; set; }
}
}
And model of Tag consist of string TagName, int TagId, List Posts.
When I create a new Post I use CreatePostView and my view is:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="create-post-form">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<strong>Title</strong>
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<strong>Description</strong>
<div class="col-md-10">
#Html.EditorFor(model => model.Description, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "text-danger" })
</div>
</div>
#Html.DropDownListFor(m => m.SelectedTag, Model.TagList, "Add tag")
#Html.ValidationMessageFor(m => m.SelectedTag)
<div class="post-create-button">
<input type="submit" value="Create">
</div>
<div class="back-to-list-button">
#Html.ActionLink("Back", "Index")
</div>
</div>
}
And now I want to display my tag that I selected. I put value of selected tag in ViewBag, but it does not display. Maybe it's silly, but I do not know how to fix it. My Create action of PostsController:
// POST: Posts/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CreatePostView post)
{
Post currPost = new Post {
Title = post.Title,
Description = post.Description,
PostDate = DateTime.Now,
Tags = null };
ViewBag.Tag = post.SelectedTag.ToString();
ViewBag.Trash = "texttexttexttexttext"; // It's strange, but it not displayed.
if (ModelState.IsValid)
{
//var tags = db.Tags.Where(s => s.TagName.Equals(post.SelectedTag)).ToList();
//currPost.Tags = tags;
db.Posts.Add(currPost);
db.SaveChanges();
return RedirectToAction("Index", "Posts");
}
return View(currPost);
}
My view with all Posts (use model Post)
#foreach (var item in Model)
{
<article class="post">
<h3>#Html.DisplayFor(modelItem => item.Title)</h3>
<p>#Html.DisplayFor(modelItem => item.Description)</p>
<!--None of them is not shown-->
<p><strong>Tag: #ViewBag.Tag</strong></p>
<p><strong>Trash: #ViewBag.Trash</strong></p>
</article>
}
ViewBag is used when returning a view, not when redirecting to another action. Basically it doesn't persist across separate requests. Try using TempData instead:
TempData["Tag"] = post.SelectedTag.ToString();
and in the view:
<p><strong>Tag: #TempData["Tag"]</strong></p>
Related
In my form, which I created in a view, the user can press add or search.
If the "add" button is pressed, a different model should be used in the background than with the "search" option. The add model is validated but otherwise does not differ from the search model.
By clicking "search" the user shouldn't be forced to fill in all fields.
Code
Model - AddModel
[Key]
public int Id { get; set; }
[Required]
[Display(Name = "Name")]
[StringLength(200, MinimumLength = 1, ErrorMessage = "Not Allowed")]
public string Name { get; set; }
[Required]
[Display(Name = "Place")]
[RegularExpression(#"^[\w ]*$", ErrorMessage = "Not Allowed")]
public string Place { get; set; }
Model - SearchModel
public int Id { get; set; }
public string Name { get; set; }
public string Place{ get; set; }
Controller
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Add(AddModel p) {
if (ModelState.IsValid) {
_ = InsertData(p);
ModelState.Clear();
return RedirectToAction("Add", new { Success = true });
}
return View();
}
public IActionResult Select(SearchModel p)
{
Task.WaitAll(SelectData(p));
return View(per); // per => list of selected data
}
View
#model **AddModel**
#if (ViewBag.success)
{
...
}
<form method="POST">
<div class="form-group">
#Html.LabelFor(m => m.Name, new { })
#Html.EditorFor(m => m.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Name, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Place, new { })
#Html.EditorFor(m => m.Place, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m.Place, "", new { #class = "text-danger" })
</div>
<input asp-action="Add" type="submit" class="btn btn-outline-primary" value="Add" />
<input asp-action="Select" type="submit" class="btn btn-outline-success" value="Search" />
</form>
The AddModel is still used in the View, but I would like to specify in the controller which model I would like to use. So if you press "search" the SearchModel and with "add" the AddModel should be used. I've already tried it with dynamic, but then it came to problems with the #html helpers.
Does somebody has any idea?
Would appreciate ;)
I think what you are looking to do is called a ViewModel, this should help : https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-3
I am trying to define ViewModels that faithfully represent the view (to make strict use of that concept).
Some of the elements of the ViewModel are updated dynamically. The problem I have, is that when I do the Post, the ViewModel returns without the elements that were updated dynamically.
The update is done through jQuery, when an event is performed. An action is invoked through Url.Action, and a Div is updated.
I made an example to clarify the scenario. An application that only stores a location (state and city). For this I have three ViewModels: one to represent the States in a SelectList, one to represent the Cities in a SelectList, and finally one to represent the Location (formed by the two ViewModel that I mentioned first).
Models:
public class State
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
public class City
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int StateId { get; set; }
public virtual State State { get; set; }
}
ViewModels:
public class CitySelectListViewModel
{
public CitySelectListViewModel() { }
public CitySelectListViewModel(IEnumerable<Models.City> cities)
{
this.Cities = cities;
}
[Display(Name = "Cities")]
[Required]
public int? SelectedCityId { get; set; }
public IEnumerable<City> Cities { get; }
}
public class StateSelectListViewModel
{
public StateSelectListViewModel() { }
public StateSelectListViewModel(IEnumerable<State> states)
{
this.States = states;
}
[Display(Name = "States")]
[Required]
public int? SelectedStateId { get; set; }
public IEnumerable<State> States { get; }
}
public class LocationCreateViewModel
{
public LocationCreateViewModel() { }
public LocationCreateViewModel(ICollection<State> states)
{
this.StateSelectListViewModels = new StateSelectListViewModel(states);
this.CitySelectListViewModel = new CitySelectListViewModel();
}
public StateSelectListViewModel StateSelectListViewModels { set; get; }
public CitySelectListViewModel CitySelectListViewModel { set; get; }
}
Location [Controller]:
public class LocationController : Controller
{
private DALDbContext db = new DALDbContext();
// GET: Location/Create
public ActionResult Create()
{
LocationCreateViewModel locationCreateViewModel = new LocationCreateViewModel(db.States.ToList());
return View(locationCreateViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(LocationCreateViewModel pLocationCreateViewModel)
{
if (ModelState.IsValid)
{
//db.States.Add(state);
//db.SaveChanges();
return RedirectToAction("Index", "Home");
}
LocationCreateViewModel locationCreateViewModel = new LocationCreateViewModel(db.States.ToList());
return View(locationCreateViewModel);
}
public ActionResult CitySelectList(int? stateId)
{
CitySelectListViewModel citySelectListViewModel = new CitySelectListViewModel(db.Cities.Where(c => c.StateId == stateId).ToList());
return View(citySelectListViewModel);
}
}
Create [View]:
#model ViewModelExample.ViewModels.LocationCreateViewModel
....
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>State</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.StateSelectListViewModels.SelectedStateId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.StateSelectListViewModels.SelectedStateId, new SelectList(Model.StateSelectListViewModels.States, "Id", "Name"), "Select a State", htmlAttributes: new { #class = "form-control", #id = "StateSelectList" })
#Html.ValidationMessageFor(model => model.StateSelectListViewModels.SelectedStateId, "", new { #class = "text-danger" })
</div>
</div>
<div id="CityContainer">
#Html.Action("CitySelectList")
</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>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
$(function () {
// Fill City DropDownList
$('#StateSelectList').change(function () {
var selectedStateId = this.value;
$('#CityContainer').load('#Url.Action("CitySelectList")?stateId=' + selectedStateId);
});
});
</script>
}
CitySelectList [View]:
#model ViewModelExample.ViewModels.CitySelectListViewModel
....
<div class="form-group">
#Html.LabelFor(model => model.SelectedCityId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedCityId, new SelectList(Model.Cities, "Id", "Name"), "Select a City", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.SelectedCityId, "", new { #class = "text-danger" })
</div>
</div>
I will show the execution of my example, and I will show the problem through the inspection of the ViewModel that I receive after the Post:
I select a State and a City, and I press Create.
I inspect the ViewModel received after the Post. We can see how CitySelectListViewModel is null, and what I want is to bring the last ViewModel that was updated through jQuery.
I admit that I have provided a long example, but it is the only way I found to explain what I need. Thanks in advance.
VS-Project of the example
I'ts because you are preventing the modelBinder to accurately bind to LocationCreateViewModel in your Create action when replacing the inner HTML of <div id="CityContainer"> (thats what you do with $('#CityContainer').load(...). You instruct the model binder to bind to
#model ViewModelExample.ViewModels.CitySelectListViewModel and as a result you get this HTML for the city select list:
One way of solving this is modifying CitySelectList.cshtml to:
#model ViewModelExample.ViewModels.LocationCreateViewModel
#{
Layout = null;
}
<div class="form-group">
#Html.LabelFor(model => model.CitySelectListViewModel.SelectedCityId,
htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model =>
model.CitySelectListViewModel.SelectedCityId, new
SelectList(Model.CitySelectListViewModel.Cities, "Id", "Name"), "Select a City", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CitySelectListViewModel.SelectedCityId, "", new { #class = "text-danger" })
</div>
</div>
and your CitySelectList action to:
public ActionResult CitySelectList(int? stateId)
{
LocationCreateViewModel locationCreateViewModel = new LocationCreateViewModel();
locationCreateViewModel.CitySelectListViewModel = new CitySelectListViewModel(db.Cities.Where(c => c.StateId == stateId).ToList());
return View(locationCreateViewModel);
}
But I would recommend custom model binding as well.
I'm calling three Models (Unit, Site, Work_Type) in my view model called UnitAdminViewModel. I need to set one field as required from the Unit Model. Since I'm using Database First approach, I cannot modify the Unit Model directly since this gets autogenerated. How can I successfully add:
[Required(ErrorMessage = "Group is required")]
public string GroupName { get; set; }
to my view model UnitAdminViewModel?
public class UnitAdminViewModel
{
public Unit Unit { get; set; }
public List<Site> Site { get; set; }
public IEnumerable<Work_Type> Work_Type { get; set; }
}
In the Unit Model, I want to set the field GroupName as [Required]
public partial class Unit
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Unit()
{
this.Staffs = new HashSet<Staff>();
}
public int UnitID { get; set; }
public string UnitCode { get; set; }
public string UnitName { get; set; }
public string GroupName { get; set; }
public byte IncentiveUnit { get; set; }
public bool CallCenter { get; set; }
public bool CDWUnit { get; set; }
public string CDWSite { get; set; }
public Nullable<int> SiteID { get; set; }
public Nullable<int> DivisionID { get; set; }
public bool WFCUnit { get; set; }
public bool QAMonitored { get; set; }
public bool NICEMonitored { get; set; }
public string ListPrefix { get; set; }
public string TSHSource { get; set; }
public string StatsSource { get; set; }
public string DialerSource { get; set; }
public Nullable<int> CostCenterID { get; set; }
public int WaterfallView { get; set; }
public bool Locked { get; set; }
public string Platform { get; set; }
public Nullable<int> Supplier { get; set; }
public string Work_Type { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Staff> Staffs { get; set; }
}
Update
I tried going off #Izzy example. I feel like i'm closer, but the [Required] still doesn't seem to trigger a validation error when I submit a form without populating that field. #Izzy, is there something I might be missing?
View Model
public class UnitAdminViewModel
{
public Unit Unit { get; set; }
public List<Site> Site { get; set; }
public IEnumerable<Work_Type> Work_Type { get; set; }
}
UnitMetaData class
[MetadataType(typeof(UnitMetaData))]
public partial class Unit
{
}
public class UnitMetaData {
[Required(ErrorMessage = "Group is required")]
public string GroupName { get; set; }
[Required(ErrorMessage = "UnitName is required")]
public string UnitName { get; set; }
public string CDWSite { get; set; }
public string Platform { get; set; }
public Nullable<int> Supplier { get; set; }
public string Work_Type { get; set; }
}
VIEW
#model WebReportingToolDAL.Models.ViewModels.UnitAdminViewModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Unit</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Unit.UnitName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Unit.UnitName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Unit.UnitName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Unit.GroupName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Unit.GroupName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Unit.GroupName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Unit.CDWSite, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Unit.CDWSite, new SelectList(Model.Site, "SiteName", "SiteName"), new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Unit.Platform, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Unit.Platform, new List<SelectListItem> { new SelectListItem { Text = "PSCC", Value = "PSCC" }, new SelectListItem { Text = "RC", Value = "RC" } }, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Unit.Supplier, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Unit.Supplier, new List<SelectListItem> { new SelectListItem { Text = "0", Value = "0" }, new SelectListItem { Text = "1", Value = "1" } }, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Unit.Work_Type, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Unit.Work_Type,new SelectList(Model.Work_Type, "Name", "Name"),new { #class = "form-control" })
</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>
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "UnitID,UnitCode,UnitName,GroupName,IncentiveUnit,CallCenter,CDWUnit,CDWSite,SiteID,DivisionID,WFCUnit,QAMonitored,NICEMonitored,ListPrefix,TSHSource,StatsSource,DialerSource,CostCenterID,WaterfallView,Locked,Platform,Supplier,Work_Type")] Unit unit)
{
if (ModelState.IsValid)
{
unit.UnitCode = "XX";
unit.IncentiveUnit = 1;
unit.CallCenter = true;
unit.CDWUnit = true;
unit.DivisionID = 2;
unit.WFCUnit = false;
unit.QAMonitored = false;
unit.NICEMonitored = true;
unit.ListPrefix = null;
unit.TSHSource = null;
unit.StatsSource = null;
unit.DialerSource = null;
unit.CostCenterID = 3;
unit.WaterfallView = 1;
unit.Locked = false;
var siteId = (from s in db.Sites
where s.SiteName.ToLower().Equals(unit.CDWSite.ToLower())
select s.SiteID).First();
unit.SiteID = siteId;
db.Units.Add(unit);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(unit);
}
When using Database first approach you'll realise that the class is marked as partial So what you can do is make use of MetadataType attribute to achieve what you're after.
So go ahead and create a file and name it e.g. UnitMetaData. Your code should look something like:
public class UnitMetaData
{
[Required(ErrorMessage = "Group is required")]
public string GroupName { get; set; }
//more properties
}
Your Unit class is partial so you can create it another file and use MetadataType as:
[MetadataType(typeof(UnitMetaData))]
public partial class Unit
{
}
More about MetadataType here
partial definition:
It is possible to split the definition of a class or a struct, an interface or a method over two or more source files. Each source file contains a section of the type or method definition, and all parts are combined when the application is compiled.
source
Please Note: Ensure the namespace is same as the generated Unit class, otherwise it will not work
You can use a real view model, for one. Simply wrapping a bunch of entities in a class is missing the point of what view models are for. Your view models should only contain the properties that should be displayed/edited and it should hold the business logic for your view, such as the fact that GroupName is required (when it apparently isn't at the database level).
That means creating something like:
public class UnitViewModel
{
// other properties you want to edit
[Required]
public string GroupName { get; set; }
}
Then, you use this rather than Unit in your view, and map the posted properties from UnitViewModel onto your Unit instance.
I want to pack my data in form with foreign key and send that to database.I collect data from database with controller and show in my view and when I complete the form can not send that to database and see the exception
my controller code is
public ActionResult Register()
{
testContext test = new testContext();
List<SelectListItem> listselecteditem = new List<SelectListItem>();
foreach (Gender item in test.genders)
{
SelectListItem selectlist = new SelectListItem()
{
Text = item.GenderType,
Value = item.GenderID.ToString(),
};
listselecteditem.Add(selectlist);
}
ViewBag.Datalist = new SelectList(listselecteditem, "Value", "Text");
return View();
}
this controller get data from database and send to dropdownlist
and this controller save my data in database
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(Person p)
{
using (testContext test=new testContext())
{
if (ModelState.IsValid)
{
try
{
test.persons.Add(p);
test.SaveChanges();
ViewBag.Message="Success";
}
catch (Exception ec)
{
ViewBag.Message = ec.Message;
}
}
}
return View(p);
}
this is my view
#model testmvc.Models.Person
<div class="container">
<div class="row">
<div class="pull-right col-sm-offset-3 col-sm-6">
<div class="panel panel-default">
<div class="panel-heading ">
<p>register</p>
</div>
<div class="panel-body">
#using (Html.BeginForm("Register", "RegisterLogin", FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.ValidationSummary(true)
<fieldset>
#Html.AntiForgeryToken()
#if (ViewBag.Messsage != null)
{
<div class="alert alert-success">
<p>#ViewBag.Message</p>
</div>
}
<div class="form-group">
#Html.TextBoxFor(model => model.Fullname, new { #class = "form-control", #placeholder = "Full name" })
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Username, new { #class = "form-control input-sm", #id = "last_name", #placeholder = "Username" })
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.EmailAddress, new { #class = "form-control input-sm", #id = "email", #placeholder = "Email address" })
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Password, new { #class = "form-control input-sm floatlabel", #id = "first_name", #placeholder = "Password" })
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Comfirmpassword, new { #class = "form-control input-sm", #id = "last_name", #placeholder = "confirmpassword" })
</div>
<div class="form-group">
#*<select>
#foreach (var item in ViewBag.DataList)
{
<option>#item.Text</option>
}
</select>*#
#Html.DropDownList("Datalist",String.Empty)
</div>
<div class="form-group">
#Html.TextBoxFor(model => model.Birthday, new { #class = "form-control input-sm", #id = "password_confirmation", #placeholder = "Birthday yyyy/dd/mm" })
</div>
<div>
<input type="submit" value="Register" class="btn btn-primary">
</div>
</fieldset>
}
</div>
</div>
</div>
</div>
</div>
and my model code
public partial class Person
{
[Key]
public int personID { get; set; }
[Required]
public String Fullname { get; set; }
[Required]
public String Username { get; set; }
[Required]
public String Password { get; set; }
[Required]
[NotMapped]
public String Comfirmpassword { get; set; }
[Required]
public String EmailAddress { get; set; }
[DataType(DataType.DateTime)]
[Required]
public DateTime Birthday { get; set; }
public int GenderID { get; set; }
[ForeignKey("GenderID")]
public virtual Gender Gender { get; set; }
}
[Table("Gender")]
public partial class Gender
{
[Key]
public int GenderID { get; set; }
[Required]
public String GenderType { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
this exception said there is not any viewdata key with "Datalist". how can I solve that and what is my code problem
The reason you are getting this exception is because inside [HttpPost] action you didn't populate the ViewBag.Datalist property, the way you did in your Get action. Since you redisplay the same view and this view requires this information in order to properly render the dropdown, you will need to populate it. To avoid repetition you could place this logic in a separate method:
private SelectList GetGenders()
{
using (testContext test = new testContext())
{
List<SelectListItem> listselecteditem = new List<SelectListItem>();
foreach (Gender item in test.genders)
{
SelectListItem selectlist = new SelectListItem()
{
Text = item.GenderType,
Value = item.GenderID.ToString(),
};
listselecteditem.Add(selectlist);
}
return new SelectList(listselecteditem, "Value", "Text");
}
}
which you are going to call in your 2 actions:
public ActionResult Register()
{
ViewBag.Datalist = GetGenders();
return View();
}
and:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(Person p)
{
using (testContext test = new testContext())
{
if (ModelState.IsValid)
{
try
{
test.persons.Add(p);
test.SaveChanges();
ViewBag.Message = "Success";
}
catch (Exception ec)
{
ViewBag.Message = ec.Message;
}
}
}
ViewBag.Datalist = GetGenders();
return View(p);
}
I am new to ASP.NET MVC 5. I am trying to make a user-management software. but in the user registration(create) controller my model is getting invalidated. Don't know why. I might have a mistake in my model Binding. Here is the attached code. Any Help is appreciated.
Model file
public class UserData
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int userid { get; set; }
[Required(ErrorMessage = "Domain ID")]
[Display(Name = "Domain ID")]
public string domainid { get; set; }
[Required(ErrorMessage = "Choose Role")]
[Display(Name = "Role")]
public string role { get; set; }
[Required(ErrorMessage = "Choose Country")]
[Display(Name = "Country")]
public string country { get; set; }
[Required(ErrorMessage = "Choose BU")]
[Display(Name = "BU")]
public string bu { get; set; }
[Required]
[RegularExpression(#"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))#"
+ #"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
[0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
+ #"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?
[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
+ #"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})$", ErrorMessage = "Please Provide Valid Email-ID")]
[Display(Name = "Email"),DataType(DataType.EmailAddress)]
public string email { get; set; }
[HiddenInput(DisplayValue=true)]
public DateTime date_from { get; set; }
[HiddenInput(DisplayValue = true)]
public DateTime date_to { get; set; }
[HiddenInput(DisplayValue=true)]
public bool active { get; set; }
}
ViewModel File
public class UserRegistrationViewModel
{
public UserData userdata { get; set; }
public string SelectedRole { get; set; }
public IEnumerable<SelectListItem> RoleList { get; set; }
public string SelectedCountry { get; set; }
public IEnumerable<SelectListItem> CountryList { get; set; }
public string SelectedBU { get; set; }
public IEnumerable<SelectListItem> BUList { get; set; }
}
Controller file
public class UserDatasController : Controller
{
private ApplicationDataContext db = new ApplicationDataContext();
// GET: UserDatas
public ActionResult Index()
{
return View(db.UsersData.ToList());
}
public ActionResult Create()
{
var model = new UserRegistrationViewModel();
model.CountryList = from p in XDocument.Load("C:/Users/inkosah/Documents/Visual Studio 2013/Projects/Policy Assessment/Policy Assessment/country_list.xml").Descendants("Name")
//var a=Path.GetFullPath("Policy Asse")
let value = (string)p.Element("Text")
select new SelectListItem
{
Selected = (value == model.SelectedCountry),
Text = (string)p.Element("Text"),
Value = value
};
model.BUList = from q in XDocument.Load("C:/Users/inkosah/Documents/Visual Studio 2013/Projects/Policy Assessment/Policy Assessment/bu_list.xml").Descendants("BU")
let value2 = (string)q.Element("BU_Name")
select new SelectListItem
{
Selected = (value2 == model.SelectedBU),
Text = (string)q.Element("BU_Name"),
Value = value2
};
model.RoleList = from n in XDocument.Load("C:/Users/inkosah/Documents/Visual Studio 2013/Projects/Policy Assessment/Policy Assessment/UserRoleList.xml").Descendants("Role")
let value1 = (string)n.Element("Role_Name")
select new SelectListItem
{
Selected = (value1 == model.SelectedRole),
Text = (string)n.Element("Role_Name"),
Value = value1
};
return View(model);
}
// POST: UserDatas/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 = "SelectedRole,SelectedCountry,SelectedBU")]UserRegistrationViewModel RegisterData,[Bind(Include="domainid,email")] UserData userdata)
{
userdata.date_from = DateTime.Now;
userdata.date_to = DateTime.MaxValue;
userdata.active = false;
userdata.role = RegisterData.SelectedRole;
userdata.bu = RegisterData.SelectedBU;
userdata.country = RegisterData.SelectedCountry;
if (ModelState.IsValid)
{
db.UsersData.Add(userdata);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userdata);
}
create.cshtml
#model Policy_Assessment.ViewModels.UserRegistrationViewModel
#{
ViewBag.Title = "User Registration Page";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>User Input</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.userdata.domainid, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.userdata.domainid)
#Html.ValidationMessageFor(model => model.userdata.domainid)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.userdata.role, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#*#Html.EditorFor(model => model.role)*#
#Html.DropDownListFor(model => model.SelectedRole, Model.RoleList, "-----Role-----")
#Html.ValidationMessageFor(model=>model.SelectedRole)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.userdata.country, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#*#Html.EditorFor(model => model.country)*#
#Html.DropDownListFor(model => model.SelectedCountry,Model.CountryList,"----Country-----")
#Html.ValidationMessageFor(model => model.userdata.country)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.userdata.bu, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model=>model.SelectedBU,Model.BUList,"--Select BU----")
#Html.ValidationMessageFor(model => model.userdata.bu)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.userdata.email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.userdata.email)
#Html.ValidationMessageFor(model => model.userdata.email)
</div>
</div>
#*<div class="form-group">
#Html.HiddenFor(model => model.userdata.date_from, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.HiddenFor(model => model.userdata.date_from)
#Html.ValidationMessageFor(model => model.userdata.date_from)
</div>
</div>
<div class="form-group">
#Html.HiddenFor(model => model.userdata.date_to, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.HiddenFor(model => model.userdata.date_to)
#Html.ValidationMessageFor(model => model.userdata.date_to)
</div>
</div>
<div class="form-group">
#Html.HiddenFor(model => model.userdata.active, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.HiddenFor(model => model.userdata.active)
#Html.ValidationMessageFor(model => model.userdata.active)
</div>
</div>*#
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Register" class="btn btn-default" />
</div>
</div>
</div>
}
#*<div>
#Html.ActionLink("Back to List", "Index")
</div>*#
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
Context File
public class ApplicationDataContext : DbContext
{
public ApplicationDataContext()
: base("DefaultConnection")
{ }
public System.Data.Entity.DbSet<Policy_Assessment.ViewModels.UserRegistrationViewModel> UserRegistrationData { get; set; }
public System.Data.Entity.DbSet<Policy_Assessment.Models.UserData> UsersData { get; set; }
}
Please Note i am a beginner in MVC ASP.Net. Any Help or Explanation would be helpful.
Your posting back your data model (included as a property of your view model). The data model has a [Required] attribute for property role but you are creating a control for this property so nothing is bound, meaning its null and therefore its invalid. Two ways to solve this.
A. Remove property string SelectedRole from the view model and bind directly to the the data model that's included in the view model
#Html.DropDownListFor(m => m.userdata.role, Model.RoleList, ...)
Now, userdata.role will contain the selected option value and will be valid (note you would need to do this for the other 2 properties as well).
B. Remove property UserData userdata from the view model and include in the view model the properties from UserData that you are editing
public class UserRegistrationViewModel
{
[Required(ErrorMessage = "Domain ID")]
[Display(Name = "Domain ID")]
public string domainid { get; set; }
[Required(ErrorMessage = "Choose Role")]
[Display(Name = "Role")]
public string role { get; set; }
[Required(ErrorMessage = "Choose Country")]
[Display(Name = "Country")]
public string country { get; set; }
[Required(ErrorMessage = "Choose BU")]
[Display(Name = "BU")]
public string bu { get; set; }
[Required]
[Display(Name = "Email"),DataType(DataType.EmailAddress)]
[EmailAddress]
public string email { get; set; }
public SelectList RoleList { get; set; }
public SelectList CountryList { get; set; }
public SelectList BUList { get; set; }
}
Note I've excluded properties you don't appear to be editing, used [EmailAddress] rather than your Regex (could not see what the regex was doing that an EmailAddress attribute is not already doing) and used SelectList rather than IEnumerable<SelectListItem> which means you can simplify it to
public ActionResult Create()
{
var model = new UserRegistrationViewModel();
var roles = from n in XDocument.Load(....
model.RoleList = new SelectList(roles, "value", "value");
....
return View(model);
}
and then in the POST method, map the properties from the view model to a new instance of the data model, setting the other properties such as date_from if required (alternatively you could put these default values in a constructor) and save to the database.