How to Bind ViewModel's object with the controller [duplicate] - c#

This question already has answers here:
Asp.Net MVC: Why is my view passing NULL models back to my controller?
(2 answers)
Closed 6 years ago.
In my MVC application, I have a model class like below
public class PROJECT
{
[Display(Name = "Project No.")]
public string PROJECT_NO { get; set; }
[Display(Name = "Title")]
[DataType(DataType.MultilineText)]
[Required]
public string TITLE { get; set; }
[Display(Name = "Description")]
[DataType(DataType.MultilineText)]
public string DESCRIPTION { get; set; }
[Display(Name = "Remarks")]
public string REMARKS { get; set; }
}
And I have a ViewModel like this
public class ProjectViewModel
{
public PROJECT Project { get; set; }
public bool IsSelected { get; set; }
public COMPANY Companies { get; set; }
public CLIENT Clients { get; set; }
}
The ViewModel is the one I am creating the controller and views for. I have created the Index, Details, Delete and Create Views as well. Index, Details and Delete Views just work fine but Create controller model binding does not seem to be working. The ProjectViewModel object that comes in as input to the controller Create is null. How do I bind the ViewModel as a parameter to this object?
// POST: /Project/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ProjectViewModel project)
{
if (ModelState.IsValid)
{
db.PROJECTs.Add(project.Project);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CLIENT_ID = new SelectList(db.CLIENTs, "ID", "NAME", project.Project.CLIENT_ID);
ViewBag.COMPANY_ID = new SelectList(db.COMPANies, "ID", "NAME", project.Project.COMPANY_ID);
return View(project);
}
And here is the Create View
#model IMCCBluePrints.Models.ProjectViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>PROJECT</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Project.PROJECT_NO, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.PROJECT_NO)
#Html.ValidationMessageFor(model => model.Project.PROJECT_NO)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.TITLE, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.TITLE)
#Html.ValidationMessageFor(model => model.Project.TITLE)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.DESCRIPTION, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.DESCRIPTION)
#Html.ValidationMessageFor(model => model.Project.DESCRIPTION)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.REMARKS, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Project.REMARKS)
#Html.ValidationMessageFor(model => model.Project.REMARKS)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.COMPANY_ID, "COMPANY_ID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("COMPANY_ID", String.Empty)
#Html.ValidationMessageFor(model => model.Project.COMPANY_ID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Project.CLIENT_ID, "CLIENT_ID", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CLIENT_ID", String.Empty)
#Html.ValidationMessageFor(model => model.Project.CLIENT_ID)
</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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>

In your GET action instantiate your project class and try:
...
ProjectViewModel.project = new PROJECT();
...
return View(ProjectViewModel);

The Issue that when form submit project conflict with type Project as it is complex type, Just rename it to be model for example
public ActionResult Create(ProjectViewModel model)
try this and it will work, i've tried by myself.

Related

Insert data into database from another models view

I have two model classes for my project,
public class BookModel
{
public Int64 ISBN { get; set; }
public DateTime AddedDate { get; set; }
public bool isActive { get; set; }
public int Quantity { get; set; }
public int? FormatID { get; set; }
public virtual FormatModel Format { get; set; }
}
and
public class FormatModel
{
public FormatModel()
{
Books = new HashSet<BookModel>();
}
public int ID { get; set; }
public string Value { get; set; }
public virtual ICollection<BookModel> Books { get; }
}
I want to create a modal in my Create.cshtml file to add a new format if the specified format doesn't exist.
How can I create a Submit button for my modal to add the format in FormatModel?
This is my view:
#model RookBookMVC.Models.BookModel
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.ReturnUrl = "/Book/Create";
}
<h2>Create</h2>
#using (Html.BeginForm("Create", "Book", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>BookModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.FormatID, "FormatID", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="input-group col-md-10">
#Html.DropDownList("FormatID", null, "Select Book Format", htmlAttributes: new { #class = "form-control" })
<div class="input-group-append">
<button type="button" class="btn btn-secondary" data-toggle="modal" data-target="#exampleModalCenter">
Add New
</button>
</div>
</div>
#Html.ValidationMessageFor(model => model.FormatID, "", new { #class = "text-danger" })
</div>
<div class="form-group">
#Html.LabelFor(model => model.ISBN, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ISBN, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ISBN, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Quantity, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Quantity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Quantity, "", new { #class = "text-danger" })
</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>
}
#using (Html.BeginForm("Create", "Format", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="modal fade" id="exampleModalCenter" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Add New Format</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
#Html.LabelFor(model => model.Format.Value, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Format.Value, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Format.Value, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<input type="submit" value="Create Format" class="btn btn-default" />
</div>
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
after I click on the 'Create Format' button. it returns false for ModelState.IsValid and doesn't add any data to my database
What should I do for making 'Create Format' Button make a new Entity for Format and save it in my database?
for Additional I need to create two models via a single page
You might have to create a View Model and pass the view model property as parameter to the Action - Format/ Create post.
ViewModel property:
public string FormatValue { get; set; }
Modal view:
#Html.EditorFor(model => model.FormatValue, new { htmlAttributes = new { #class = "form-control" } })
Controller code with parameter as the same name as the view model property being passed from the modal form:
[HttpPost]
public ActionResult Create(string formatValue)
{
using (var dataContext = new YourDbContextModel())
{
FormatModel fmtModel = new FormatModel();
fmtModel.Value = formatValue;
// rest of your code to save data
}
}
public class MYViewModel{ public string FormatValue { get; set; } }
Create.cshtml
#model MYViewModel
<div>
#Html.EditorFor(model => model.FormatValue, new { htmlAttributes = new { #class = "form-control" } })
</div>
Do You Mean something Like that?
OR
public class MYViewModel{
public string FormatValue { get; set; }
Public BookModel Book {get; set;}
}

Asp.Net MVC 5 Html.DropDownList is not validating

I've been stuck on this problem for a while now and can't seem to find anything online that helps.
I have a Html.DropDownList in an Editor Template. I use that template in my form and don't select anything for the dropdownlist. When I click submit, I expect the form to notify me that the required dropdownlist doesn't have a value selected, but it doesn't. What am I doing wrong?
Here are the View Models:
public class RequestCreateViewModel
{
public int ID { get; set; }
public TesterLocationCreateViewModel TesterLocationCreateViewModel { get; set; }
....
public RequestCreateViewModel()
{
}
}
public class TesterLocationCreateViewModel
{
public int ID { get; set; }
[Required]
[UIHint("OEM")]
public string OEM { get; set; }
[Required]
[DisplayName("City")]
public string LocationCity { get; set; }
public TesterLocationCreateViewModel()
{
}
}
Here's a snippet of the Create.cshtml
#model WdcTesterManager.Models.Request
#{
ViewBag.Title = "Create Request";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create Request</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.EditorFor(model => model.TesterLocationCreateViewModel)
</div>
</div>
}
Here's the TesterLocationCreateViewModel.cshtml (Editor Template):
#model WdcTesterManager.Models.TesterLocationCreateViewModel
<div class="col-md-6">
<h4>Tester Location</h4>
<div class="container-fluid">
<div class="form-group">
#Html.LabelFor(model => model.OEM, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.DropDownList("OEM", (SelectList)ViewBag.OemList, "Choose one", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.OEM, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.LocationCity, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-8">
#Html.EditorFor(model => model.LocationCity, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.LocationCity, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
When I submit the form without filling anything out, I get a validation error for the City but nothing for the OEM. Any ideas?
#StephenMuecke pointed out that something looked wrong with the code, since the code seemed to be referencing the Request model rather than the Request View model.
So, I took a second look at the code and realized that the Create.cshtml was using the Request model rather than the RequestCreateViewModel.
I'm getting the correct validation errors after changing the Create.cshtml to use the RequestCreateViewModel:
#model WdcTesterManager.Models.RequestCreateViewModel

Adding another form to a view on button clicked

I have a Create view which contains a form. I want to give the user two options, save which simply saves the form and redirects to Index or add more tickets which will show another identical form in the view. I am unsure where to start with this, should I save the first ticket details then refresh the page? Or is there a way to have a second form hidden until add more tickets is selected then save all forms at once? I know this is quite vague but I've no idea where to start, but any advice would be great
Here is my model;
public class Ticket
{
public int TicketID { get; set; }
[Required]
[ForeignKey("Event")]
//foreign key
public int EventID { get; set; }
[Required]
public string Description { get; set; }
[Required]
public float Price { get; set; }
//navigation property
public virtual Event Event { get; set; }
//navigation property
public ICollection<OrderDetails> OrderDetails { get; set; }
}
Here is my view so far;
#model GeogSocSite.Models.Ticket
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Ticket</h4>
<hr />
#Html.HiddenFor(model => model.EventID)
<div class="form-group">
#Html.LabelFor(model => model.Description, htmlAttributes: new { #class = "control-label col-md-2" })
<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>
<div class="form-group">
#Html.LabelFor(model => model.Price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Price, "", new { #class = "text-danger" })
</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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My choice in this situation is using appropriate view model(as #StephenMuecke ) sugested) and using Bootsrtaap modal(here) to show hidden fields.

NullReferenceException ASP.NET MVC 5

I have a web application with this controller:
public class ServiceRequestController : Controller
{
[Authorize(Roles = "Customer")]
public ActionResult Create()
{
return View();
}
[Authorize(Roles = "Customer")]
public ActionResult CreateNewUserAccount()
{
return View();
}
[Authorize(Roles = "Customer")]
[HttpPost]
public ActionResult CreateNewUserAccount(ServiceRequest serviceRequest)
{
if (ModelState.IsValid)
{
serviceRequest.Log.Id = User.Identity.GetUserId().ToString();
serviceRequest.Log.DateTimeLogged = System.DateTime.Now;
serviceRequest.LogID = db.Logs.Max(item => item.LogID);
serviceRequest.EstimatedResolveDate serviceRequest.CalculateEstimatedResolveDate();
db.ServiceRequests.Add(serviceRequest);
db.SaveChanges();
return RedirectToAction("AllServiceRequests", "Log");
}
return View(serviceRequest);
}
The serviceRequest.Log.Id = User.Identity.GetUserId().ToString(); (And any preceding line if this is commented out) throws a null reference exception. I presume the serviceRequest is somehow null?
The ActionLink which requests the CreateNewUserAccount() page is:
#Html.ActionLink("New User Account", "CreateNewUserAccount", "ServiceRequest")
I'm not sure how to resolve this exception?
The model is:
public partial class ServiceRequest
{
public int ServiceRequestID { get; set; }
public Nullable<int> LogID { get; set; }
public string RequestType { get; set; }
[DisplayName("Additional Information")]
[Required]
[StringLength(200)]
public string AdditionalInformation { get; set; }
public DateTime EstimatedResolveDate { get; set; }
[Required]
[DisplayName("Delivery Date")]
public DateTime DeliveryDate { get; set; }
public virtual Log Log { get; set; }
public DateTime CalculateEstimatedResolveDate()
{
return System.DateTime.Now.AddDays(3);
}
}
View code:
#model OfficiumWebApp.Models.ServiceRequest
#{
ViewBag.Title = "New User Account";
}
#using(Html.BeginForm("CreateNewUserAccount", "ServiceRequest", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.RequestType, new { #class = "control-label col-md-2" })
<div class="col-md-3">
<div class="editor-field">
#Html.TextBoxFor(model => model.RequestType, new { #Value = ViewBag.Title, #readonly = "readonly" })
#Html.ValidationMessageFor(model => model.RequestType)
</div>
</div>
</div>
<div class="form-group">
#Html.Label("Name of Account Holder", new { #class = "control-label col-md-2" })
<div class="col-md-3">
<div class="editor-field">
#Html.TextBox("AccountName")
#Html.ValidationMessageFor(model => model.RequestType)
</div>
</div>
</div>
<div class="form-group">
#Html.Label("Department", new { #class = "control-label col-md-2" })
<div class="col-md-3">
<div class="editor-field">
#Html.TextBox("Department")
#Html.ValidationMessageFor(model => model.RequestType)
</div>
</div>
</div>
<div class="form-group">
#Html.Label("Location", new { #class = "control-label col-md-2" })
<div class="col-md-3">
<div class="editor-field">
#Html.TextBox("Location", null, new { id = "Location" }))
#Html.ValidationMessageFor(model => model.RequestType)
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AdditionalInformation, new { #class = "control-label col-md-2" })
<div class="tags">
<div class="col-md-10">
#Html.TextAreaFor(model => model.AdditionalInformation)
#Html.ValidationMessageFor(model => model.AdditionalInformation)
</div>
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DeliveryDate, new { #id = "VisitDateLabel", #class = "control-label col-md-2" })
<div class="col-md-3">
<div class="editor-field">
#Html.JQueryUI().DatepickerFor(model => model.DeliveryDate).Inline(false)
#Html.ValidationMessageFor(model => model.DeliveryDate)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-floppy-save"></span></button>
</div>
</div>
</div>
}
You need to return the view i.e.
[Authorize(Roles = "Customer")]
public ActionResult CreateNewUserAccount()
{
var model = new ServiceRequest();
model.Log = new Log();
return View(model);
}
In your view you need to add a model reference too at the top i.e.
#model ServiceRequest
You could also initialise the Log object in your model as follows:
public class ServiceRequest
{
public ServiceRequest()
{
Log = new Log();
}
....
}
An action link will not post your model back, you need to include it within a form and include all the model values that you want to be updated on the client i.e.
#using (Html.BeginForm("CreateNewUserAccount", "ServiceRequest", FormMethod.Post)){
#Html.EditorFor(m => m.AdditionalInformation)
...
<input type="submit" value="submit" />
Update
Taken from the below comments, this was resolved by creating a new log on post i.e.
var log = new Log {
Id = User.Identity.GetUserId().ToString(),
DateTimeLogged = System.DateTime.Now,
LogID = db.Logs.Max(item => item.LogID) };
serviceRequest.Log = log;

Error with generated view

What is wrong with the following code? It was generated by teh add view wizard. And when I navigate to the controller I get the following error: String was not recognised as a valid Boolean and the following line is highlighted: #using (Html.BeginForm())
Here is the view:
#model Radio_Management_Interface.DomainModel.Entities.Network
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout_main.cshtml";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Network</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.name)
#Html.ValidationMessageFor(model => model.name)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.startCode, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.startCode)
#Html.ValidationMessageFor(model => model.startCode)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.frequency, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.frequency)
#Html.ValidationMessageFor(model => model.frequency)
</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>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}`
Controller:
//
// GET: /Networks/Details/5
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Network network = db.Networks.Find(id);
if (network == null)
{
return HttpNotFound();
}
return View(network);
}
Domain Model:
public class Network
{
public int networkID { get; set; }
public string name { get; set; }
public int startCode { get; set; }
public decimal frequency { get; set; }
public virtual List<Block> blocks { get; set; }
}
Your id is a nullable type, replace it with this line instead:
Network network = db.Networks.Find(id.Value);
And this:
if (id == null)
Can be changed to this:
if (!id.HasValue)
The problem will be with the following line:
Network network = db.Networks.Find(id);
The id is actually a nullable type that is passed to your Find method when it is expecting the value.
With nullable types you can read the underlying value with the Value property i.e.
id.Value

Categories