ASP.NET MVC 5 - How to Add [Required] Data Annotation in ViewModel - c#

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.

Related

Multiple foreign keys pointing to same navigational property

I'm struggling to solve a problem in relation to foreign keys.
Scenario: I'm building a very basic wrestling simulator game.
Some of the model classes that I have are:
public class Wrestler
{
public int WrestlerId { get; set; }
public string Name { get; set; }
public int Overall { get; set; }
public string Finisher { get; set; }
public virtual ICollection<Match> Matches { get; set; }
}
public class Show
{
public int ShowId { get; set; }
public string Name { get; set; }
public int PromotionId {get; set;}
public int MatchId { get; set; }
public virtual ICollection<Match> Matches { get; set; }
public virtual Promotion Promotion { get; set; }
}
public class Promotion
{
public int PromotionId { get; set; }
public string Name { get; set; }
public decimal Budget { get; set; }
public string Size { get; set; }
public virtual ICollection<Championship> Championship { get; set; }
}
public class Championship
{
public int ChampionshipId { get; set; }
public string Name { get; set; }
public string Prestige { get; set; }
public int PromotionId { get; set; }
public virtual Promotion Promotion { get; set; }
}
The problem: I would like to add the functionality to create a Wrestling Match using a simple drop-down style form to select two wrestlers to face off and also to decide a winner, however, I can't figure out how to do this.
Here is what I have so far.
public class Match
{
public int MatchId { get; set; }
public int WrestlerId { get; set; }
public int WrestlerTwoId { get; set; }
public int WinnerId { get; set; }
public int ShowId { get; set; }
public virtual Wrestler Wrestler { get; set; }
public virtual Show Show { get; set; }
}
WrestlerId should be for example Hulk Hogan (WrestlerId = 1), and WrestlerTwoId should be "The Rock" (WrestlerTwoId = 2) and let's say Hulk Hogan is the winner so (WinnerId=1)
So how do I build a drop-down, like:
enter image description here
Here is the create view, Note, I know it may be incorrect
<div class="form-horizontal">
<h4>Match</h4>
<div class="form-group">
<label class="control-label col-md-2" for="Wrestler">Wrestler</label>
<div class="col-md-10">
#Html.DropDownList("WrestlerId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.WrestlerId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Wrestler">Wrestler</label>
<div class="col-md-10">
#Html.DropDownList("WrestlerTwoId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.WrestlerTwoId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Winner">Winner</label>
<div class="col-md-10">
#Html.DropDownList("WinnerId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.WinnerId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<label class="control-label col-md-2" for="Show">Show</label>
<div class="col-md-10">
#Html.DropDownList("ShowId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ShowId, "", new { #class = "text-danger" })
</div>
</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>
And here is the create action on the match controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "MatchId,WrestlerId,WrestlerTwoId,WinnerId,ShowId")]Match match)
{
try
{
if (ModelState.IsValid)
{
db.Matches.Add(match);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
PopulateShowsDropDownList(match.ShowId);
return View(match);
}
Also if needed, here is the context class:
public class WrestlingContext : DbContext
{
public DbSet<Wrestler> Wrestlers { get; set; }
public DbSet<Promotion> Promotions { get; set; }
public DbSet<Championship> Championships { get; set; }
public DbSet<Match> Matches { get; set; }
public DbSet<Show> Shows { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
I have searched high and low, with this answer being the closest to what I thought I needed, but it didn't seem to work.
I really appreciate the feedback as this is a difficult one for me to solve.

Dropdownlist dependent on another? ASP.NET MVC

This section creates job offers (it is a job portal), from which, you need to choose Area and Subarea. When I select an Area, I should see the Subareas of that Area. I leave an image to see the composition of the tables:
tables area & subarea
My job offer model is this:
namespace ProyectoBase4.Models
{
using System;
using System.Collections.Generic;
public partial class OfertaLaboral
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public OfertaLaboral()
{
this.OfertaPostulante = new HashSet<OfertaPostulante>();
}
public int Of_ID { get; set; }
public Nullable<int> OfEmp_ID { get; set; }
public string Of_Titulo { get; set; }
public string Of_Puesto { get; set; }
public Nullable<int> Of_Area { get; set; }
public Nullable<int> Of_Subarea { get; set; }
public string Of_Descrp { get; set; }
public string Of_Lugar { get; set; }
public Nullable<int> Of_Vacante { get; set; }
public Nullable<System.DateTime> Of_FechaIn { get; set; }
public Nullable<System.DateTime> Of_FechaFin { get; set; }
public Nullable<int> Of_Salario { get; set; }
public Nullable<int> Of_Jornada { get; set; }
public Nullable<int> Of_Mov { get; set; }
public Nullable<int> Of_Edu { get; set; }
public Nullable<int> Of_TContrato { get; set; }
public Nullable<int> Of_Estado { get; set; }
public virtual Area Area { get; set; }
public virtual Educacion Educacion { get; set; }
public virtual Estado Estado { get; set; }
public virtual Jornada_Compl Jornada_Compl { get; set; }
public virtual Movilidad Movilidad { get; set; }
public virtual Subarea Subarea { get; set; }
public virtual TipoContrato TipoContrato { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<OfertaPostulante> OfertaPostulante { get; set; }
}
}
How can I do that by choosing a field in Area, I display the corresponding Subtareas? This is the view:
<div class="">
<div class="form-group col-md-8">
#Html.LabelFor(model => model.Of_Titulo, htmlAttributes: new { style = "" })
<div class="">
#Html.EditorFor(model => model.Of_Titulo, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Of_Titulo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group col-md-3">
#Html.LabelFor(model => model.Of_Area, htmlAttributes: new { #class = "", style = "margin-left:10px;" })
<div class="">
#Html.DropDownList("Of_Area", null, htmlAttributes: new { #class = "form-control form-control-75", style = "margin-left:10px;" })
#Html.ValidationMessageFor(model => model.Of_Area, "", new { #class = "text-danger" })
</div>
</div>
</div>
<br /><br /><br /><br />
<div>
<div class="form-group col-md-4">
#Html.LabelFor(model => model.Of_Vacante, htmlAttributes: new { #class = "" })
<div class="">
#Html.EditorFor(model => model.Of_Vacante, new { htmlAttributes = new { #class = "form-control form-control-50" } })
#Html.ValidationMessageFor(model => model.Of_Vacante, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group col-md-4">
#Html.LabelFor(model => model.Of_Salario, htmlAttributes: new { #class = "" })
<div class="">
#Html.EditorFor(model => model.Of_Salario, new { htmlAttributes = new { #class = "form-control form-control-50" } })
</div>
</div>
<div class="form-group col-md-3">
#Html.LabelFor(model => model.Of_Subarea, htmlAttributes: new { #class = "", style = "margin-left:40px;" })
<div class="">
#Html.DropDownList("Of_Subarea", null, htmlAttributes: new { #class = "form-control form-control-75", style = "margin-left:40px;" })
#Html.ValidationMessageFor(model => model.Of_Subarea, "", new { #class = "text-danger" })
</div>
</div>
As I mentioned before, I need that when I choose an option, then when I select the sub-option, only the options of that area appear to me.
Example:
view
Thanks
First, add an attribute to the subarea options with the area.
An example can be found here: SelectListItem with data-attributes
Second, handle the change() event of the area drop-down in jQuery. Use that event handler to hide() all options not in that area, and show() those that are.
$("#Of_Area").change(function(){
$("#Of_Subarea>option").hide();
$("#Of_Subarea>option[area=" + $("#Of_Area>option:selected").attr("value") + "]").show();
});

How do I use same view model but for different view excluding the required properties?

I have a view model for a view AddAppointment. It has many properties of which 2 are Required (I wrote Required attribute over it).
Now I want to use the same model for another view but excluding the properties which are required but it doesn't work i.e. it's invalid.
What to do apart from writing another view model?
View Model:
public class AddBookingsViewModel
{
public string CustomerName { get; set; }
public string ContactNo { get; set; }
public string VehicleRegNo { get; set; }
public short fk_VehicleMakeID { get; set; }
public string VehicleModel { get; set; }
[Required(ErrorMessage = "Select appointment time ")]
public int fk_TimeSlotID { get; set; }
public byte fk_BookingModeID { get; set; }
public int EntryUserID { get; set; }
public int ReturnBookingID { get; set; }
[Required(ErrorMessage="Fill in the appointment date")]
[DataType(DataType.Date)]
public DateTime? AppointmentDate { get; set; }
}
View: (Where it is used)
#model ZahidCarWash.ViewModels.AddBookingsViewModel
#{
ViewBag.Title = "Add Appointment";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<!-- page banner -->
<!-- end page banner -->
#using (Html.BeginForm())
{
<!-- appointments -->
<div id="appointments" class="appointment-main-block appointment-two-main-block">
<div class="container">
<div class="row">
<div class="section text-center">
<h3 class="section-heading text-center">Get an Appointment</h3>
</div>
<div class="col-md-8 col-sm-12">
<div class="appointment-block">
<h5 class="form-heading-title"><span class="form-heading-no">1.</span>Vehicle Information</h5>
<div class="row">
<div class="col-sm-4">
<div class="dropdown">
#Html.DropDownListFor(Model => Model.fk_VehicleMakeID, new SelectList(ZahidCarWash.DAL.VehicleMakesRepository.getVehicleMakes(), "VehicleMakeID", "MakeTitle"),
new { #class = "form-control" })
</div>
</div>
<div class="col-sm-4">
#Html.EditorFor(Model => Model.VehicleModel, new { htmlAttributes = new { #class = "form-control", placeholder = "Enter Vehicle Model" } })
</div>
<div class="col-sm-4">
#Html.EditorFor(Model => Model.VehicleRegNo, new { htmlAttributes = new { #class = "form-control", placeholder = "Enter Vehicle Reg No." } })
</div>
</div>
<h5 class="form-heading-title"><span class="form-heading-no">2.</span>Contact Details</h5>
<div class="row">
<div class="col-sm-4">
#Html.EditorFor(Model => Model.CustomerName, new { htmlAttributes = new { #class = "form-control", placeholder = "Customer Name" } })
#Html.ValidationMessageFor(Model => Model.CustomerName, "", new { #class = "ErrorMessages" })
</div>
<div class="col-sm-4">
#Html.EditorFor(Model => Model.ContactNo, new { htmlAttributes = new { #class = "form-control", placeholder = "Enter Contact Number." } })
#Html.ValidationMessageFor(Model => Model.ContactNo, "", new { #class = "ErrorMessages" })
</div>
</div>
<button type="submit" class="btn btn-default pull-right">Book Now</button>
</div>
</div>
</div>
</div>
</div>
}
Controller:
public JsonResult AddManualAppointment(AddBookingsViewModel AddBookingVM)
{
if (ModelState.IsValid)
{
AddBookingVM.fk_BookingModeID = 2;
int ReturnRowsCount = BookingRep.InsertCustomerAppointments(AddBookingVM, out ReturnStatus, out ReturnMessage, out ReturnBookingID);
}
else
{
}
return Json(new { ReturnMessageJSON = ReturnMessage, ReturnStatusJSON = ReturnStatus });
}
Data is passed through ajax:
<script type="text/javascript">
//to add an appointment
$('form').submit(function (e) {
e.preventDefault();
if (!$(this).valid()) {
return;
}
var url = '#Url.Action("AddManualAppointment")';
var data = $(this).serialize();
$.post(url, data, function (response) {
if (response.ReturnStatusJSON == true) {
swal("Booked !", response.ReturnMessageJSON, "success");
$("#VehicleRegNo").val("");
$("#VehicleModel").val("");
$("#CustomerName").val("");
$("#ContactNo").val("");
}
else {
swal("Sorry !", response.ReturnMessageJSON, "error");
}
});
});
</script>
<!--End Custom Scripts-->
}
I guess the quick and dirty way is to use #Html.Hiddenfor and fill the value with a new datetime from inside your controller
You can split your view model into a version with and without the required attributes using inheritance:
public class AddBookingsViewModel
{
public string CustomerName { get; set; }
public string ContactNo { get; set; }
public string VehicleRegNo { get; set; }
public short fk_VehicleMakeID { get; set; }
public string VehicleModel { get; set; }
public byte fk_BookingModeID { get; set; }
public int EntryUserID { get; set; }
public int ReturnBookingID { get; set; }
}
public class AddBookingsViewModelWithAppointment : AddBookingsViewModel
{
[Required(ErrorMessage = "Select appointment time ")]
public int fk_TimeSlotID { get; set; }
[Required(ErrorMessage="Fill in the appointment date")]
[DataType(DataType.Date)]
public DateTime? AppointmentDate { get; set; }
}
This allows you to use the appropriate view model in your situation and still maintain compatibilty through polymorphism.
If you need the optional properties in your base class, you can make your properties virtual and apply the attribute in the derived class:
public class AddBookingsViewModel
{
public string CustomerName { get; set; }
public string ContactNo { get; set; }
public string VehicleRegNo { get; set; }
public short fk_VehicleMakeID { get; set; }
public string VehicleModel { get; set; }
public byte fk_BookingModeID { get; set; }
public int EntryUserID { get; set; }
public int ReturnBookingID { get; set; }
public virtual int fk_TimeSlotID { get; set; }
public virtual DateTime? AppointmentDate { get; set; }
}
public class AddBookingsViewModelWithAppointment : AddBookingsViewModel
{
[Required(ErrorMessage = "Select appointment time ")]
public override int fk_TimeSlotID {
get => base.fk_TimeSlotID;
set => base.fk_TimeSlotID = value;
}
[Required(ErrorMessage="Fill in the appointment date")]
[DataType(DataType.Date)]
public override DateTime? AppointmentDate {
get => base.AppointmentDate;
set => base.AppointmentDate = value;
}
}
Use the veriant that works best in your business case.

Why is my View not displaying value of ViewBag?

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>

Model Invalidation in Model view viewmodel controller

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.

Categories

Resources