How to solve empty model trouble using both Ajax.BeginForm and Html.BeginForm in MVC - asp.net-mvc-4

I am new to MVC and stuck in passing modal to controller.
I have read many similar threads in SO, to no avail.
Here, I have a view for entering order details.
User will enter order item details (using ajax.BeginForm) and when he clicks save, whole order will be saved in backend (using Html.BeginForm). Ajax.BeginForm is working properly and passing + displaying records properly. But Html.BeginForm is passing model as nothing.
Here is my code ...
My Models
public class OrderItemsModel
{
public string SrNo { get; set; }
public int? ItemCode { get; set; }
public string ItemName { get; set; }
public Decimal? Qty { get; set; }
public Decimal? Rate { get; set; }
public Decimal? Amount { get; set; }
}
public class OrderModel
{
public string OrderNumber { get; set; }
public string OrderDate { get; set; }
public int? CustomerCode { get; set; }
public string CustomerName { get; set; }
public string Note { get; set; }
//List of items selected in Order
public List<OrderItemsModel> ItemsSelected { get; set; }
}
Extract from My View
#model OrderApplication.Models.OrderModel
#{
ViewBag.Title = "Index";
Model.ItemsSelected = ViewBag.getlist;
}
#using (Ajax.BeginForm("UpdateItemList", "Order", new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "selectedtable" }))
{
<h2 style="margin-left:5%;">Order Entry</h2>
//Order No & Date
<div class="row">
<div class="col-sm-6">
<div class="col-sm-3">
#Html.LabelFor(model => model.OrderNumber, "OrderNo"):
</div>
<div class="col-sm-3">
#Html.TextBoxFor(model => model.OrderNumber, new { #class = "form-control", #readonly = "readonly" })
</div>
</div>
<div class="col-sm-6">
<div class="col-sm-3">
#Html.LabelFor(model => model.OrderDate, "Date"):
</div>
<div class="col-sm-3">
#Html.TextBoxFor(model => model.OrderDate, new { #class = "form-control" })
</div>
</div>
</div>
<br />
//Table of entries
<div id="selectedtable">
#Html.Partial("_selectTable", Model);
</div>
<br />
}
#*Main Save*#
<div class="row">
<div class="col-sm-12">
<div class="col-sm-3">
#using (Html.BeginForm("SaveData", "Order", new { order = Model, id = "loginform", #class = "justify-content-center" }))
{
<input type="submit" value="Save Order" class="btn btn-success" />
}
</div>
<div class="col-sm-3">
<input type="button" class="btn btn-success" value="Clear Form" onclick="location.href='#Url.Action("Clear", "Order")'" />
</div>
</div>
</div>
My Controller
public class OrderController : Controller
{
public List<OrderItemsModel> dd = new List<OrderItemsModel>() ;
[HttpPost]
public ActionResult SaveData(OrderModel order, string id)
{
if (order == null) //order is always Nothing
{
return View(order);
}
if (order.CustomerCode == 0)
{
return View(order);
}
return View(order);
}
}
}

You shouldn't use both Ajax.BeginForm and Html.BeginForm, as it won't work. Please, check this post as might be of help to decide which one you wish to choose:
https://forums.asp.net/t/1757936.aspx?When+to+use+Html+BeginForm+vs+ajax+BeginForm
If you still want to use Html.BeginForm, just move the using sentence to replace your Ajax.BeginForm at the top of the page, so the form covers all fields, and the model won't be empty.

Related

Partial view is return NULL on POSTBACK

My partial view is returning Null on submitting the form
The main view is not null, it contains values on the form submission, I'm using render partial to include the partial view. Passing the model to partial view as well, tried passing the model in partial view action result as well but the result was the same. Any help would be highly appreciated
Thanks
View
``` <div class="container">
#using (Html.BeginForm("Menu", "Booking", FormMethod.Post))
{
#Html.AntiForgeryToken()
<fieldset class="scheduler-border">
#*<div id="">
<input type="text" id="checkduplication" style="background: white; outline: none; border: none; border-color: transparent; color: red; font-weight: bold;" disabled />
</div>*#
<div class="form-group row">
#Html.HiddenFor(model => model.MenuID)
<label class="control-label col-sm-2"> Menu Name </label>
<div class="col-sm-4">
#Html.TextBoxFor(model => model.MenuName, new { #class = " form-control", #id = "FunctionName" })
#Html.ValidationMessageFor(model => model.MenuName)
</div>
<label class="control-label col-sm-2"> Menu Price </label>
<div class="col-sm-4">
#Html.TextBoxFor(model => model.MenuPrice, new { #class = " form-control", #id = "FunctionName" })
#Html.ValidationMessageFor(model => model.MenuPrice)
</div>
</div>
<div id="MyPartialContainer">
#if (Model.MenuItems != null)
{
foreach (var x in Model.MenuItems)
{
{ Html.RenderPartial("menuitems", x); }
}
}
</div>
<div class="row" style="float: right">
<input type="submit" id="submit" title="Save" value="Save" class="btn-success btn-lg" />
<input type="button" onclick="window.location.reload();" id="cancel" title="Cancel" value="Cancel" class="btn-cancel btn-lg" />
<br /> <br />
</div>
</fieldset>
}
</div> ```
Model Classes
``` public class VMMenu
{
public VMMenu()
{
MenuItems = new List<VMMenuItems> { new VMMenuItems() };
}
public int MenuID { get; set; }
public string MenuName { get; set; }
public string MenuPrice { get; set; }
//public int BookingDetailID { get; set; }
//public int PaymentDetailID { get; set; }
public IList<VMMenuItems> MenuItems { get; set; }
}
public class VMMenuItems
{
public int MenuItemID { get; set; }
public int? MenuID { get; set; }
public string ItemName { get; set; }
public string ItemPrice { get; set; }
public int? ItemCategory { get; set; }
public string ItemCategoryName { get; set; }
} ```
Action Methods
public ActionResult Menu() {
VMMenu menu = new VMMenu();
return View(menu);
}
[HttpPost]
public ActionResult Menu(VMMenu _MenuDetails)
{
if (ModelState.IsValid)
{
VMMenu VMMenu=new VMMenu();
Mapper.CreateMap<VMMenu,Menu>();
Mapper.CreateMap<VMMenuItems,MenuItem>();
Menu obj = Mapper.Map<VMMenu, Menu>(_MenuDetails);
BookingRepo _repo = new BookingRepo();
_repo.CreateMenu(obj);
return View();
}
return View();
}
public ActionResult menuitems()
{
return PartialView();
}

How to do validation from a viewmodel

I have a model with some validations below
public class RequestABook
{
[Required(ErrorMessage = "Name Required")]
[DisplayName("Book Name")]
public string BookName { get; set; }
[Required(ErrorMessage = "Zipcode Required")]
[DisplayName("Zipcode")]
public string ZipCode { get; set; }
[Required(ErrorMessage = "Contact Name Required")]
[DisplayName("Contact Name")]
public string ContactName { get; set; }
[Required(ErrorMessage = "Email Id Required")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required(ErrorMessage = "Book Description Required")]
public string BookDescription { get; set; }
[Required(ErrorMessage = "You need to check one answer")]
public string Answer { get; set; }
}
I have a view model here
public class RequestViewModel
{
public RequestABook MyTestViewModel { get; set; }
}
I have my main page here that loads
#model BookRequestValidation.Models.RequestViewModel
#{
ViewBag.Title = "RequestABook";
}
<h2>RequestABook</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Request Book</legend>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.BookName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.BookName)
#Html.ValidationMessageFor(m => m.MyTestViewModel.BookName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.ZipCode)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.ZipCode)
#Html.ValidationMessageFor(m => m.MyTestViewModel.ZipCode)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.ContactName)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.ContactName)
#Html.ValidationMessageFor(m => m.MyTestViewModel.ContactName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.Email)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.Email)
#Html.ValidationMessageFor(m => m.MyTestViewModel.Email)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.MyTestViewModel.BookDescription)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.MyTestViewModel.BookDescription)
#Html.ValidationMessageFor(m => m.MyTestViewModel.BookDescription)
</div>
<div id="HCBudget" class="validation">
<label for="budgethealth">Budget Health</label>
#Html.RadioButton("Answer", "Red")
#Html.RadioButton("Answer", "Yellow")
#Html.RadioButton("Answer", "Green")
#Html.ValidationMessageFor(m => m.MyTestViewModel.Answer)
</div>
<input type="submit" value="Request Book" />
</fieldset>
}
Question: How do you guys handle validation with models used in a viewmodel.
Before I used this in a viewmodel everything was working well. By the time I used a viewmodel
validation stopped working.
Here is what the post action looks like.
public ActionResult RequestABook()
{
return View();
}
[HttpPost]
public ActionResult RequestABook(RequestABook quote)
{
return View();
}
It would help greatly if you posted your POST action. However, generally, I can say that validation is only run on related class instances if they are non-null. So, unless a value is posted for at least one of the properties on MyTestViewModel, it will not be instantiated by the modelbinder (MyTestViewModel will be null), and validation on its properties will not be run.
You can fix this scenario by always instantiating the MyTestViewModel property, either via the constructor of your view model or, probably better, using a custom getter and setter:
Constructor
public class RequestViewModel
{
public RequestViewModel()
{
MyTestViewModel = new RequestABook();
}
...
}
Custom Getter and Setter
private RequestABook myTestViewModel;
public RequestABook MyTestViewModel
{
get
{
if (myTestViewModel == null)
{
myTestViewModel = new RequestABook();
}
return myTestViewModel;
}
set { myTestViewModel = value; }
}

ASP.Net MVC: When form post then one view model property getting null

i have simple form where one dropdown and one submit button. i have two index function one for get and one for form post. when i select a product from dropdown and click on submit button then my index action getting invoke but there i notice my products property getting null. see my code please and tell me where i made the mistake.
view code
#model AuthTest.Models.SampleViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DateValTest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Products, htmlAttributes: new { #class = "control-label col-md-2", style = "padding-top:0px;" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedProductId, new SelectList(Model.Products, "ID", "Name"), "-- Select Product--")
#Html.ValidationMessageFor(model => model.SelectedProductId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
model code
public class Product
{
public int ID { set; get; }
public string Name { set; get; }
}
public class SampleViewModel
{
[Display(Name = "Products")]
public List<Product> Products { set; get; }
[Required(ErrorMessage = "Select any one")]
public int SelectedProductId { set; get; }
}
controller code
public class TestValController : Controller
{
// GET: TestVal
public ActionResult Index()
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
return View(SampleVM);
}
[HttpPost]
public ActionResult Index(SampleViewModel vm)
{
var SampleVM = new SampleViewModel();
SampleVM.Products = new List<Product>
{
new Product{ ID=1, Name="IPhone" },
new Product{ ID=2, Name="MacBook Pro" },
new Product{ ID=3, Name="iPod" }
};
if (ModelState.IsValid)
return View(vm);
else
return View(SampleVM);
}
}
when i debug second action then i saw vm products property getting null
please tell me where i made the mistake?
thanks
You are not making any mistake, You are not getting the list of products back because you are not including them in the HTML input form.
If you want to include the list of products you can add the following inside the input form
#for (int i = 0; i < Model.Products.Count(); i++)
{
<div>
#Html.HiddenFor(model => Model.Products[i].Name)
#Html.HiddenFor(model => Model.Products[i].ID)
</div>
}
#Mou,
Please modify your razor view and try this.
In Razor View nowhere you have specified the Http verb(Get,Post).
#using (Html.BeginForm("Index", "TestVal", FormMethod.Post)

asp.net mvc 4 dropdownlist does not return a value

I have 2 models - Question and Category -
public class Question
{
[ScaffoldColumn(false)]
public int QuestionId { get; set; }
[Required]
public string QuestionText { get; set; }
[Required]
public string AnswerA { get; set; }
[Required]
public string AnswerB { get; set; }
[Required]
public string AnswerC { get; set; }
[Required]
public string AnswerD { get; set; }
[Required]
public int Correct { get; set; }
[ForeignKey("Category")]
[Display(Name = "Category")]
[Required]
public int categoryId;
//Navigation property
public virtual Category Category { get; set; }
}
public class Category
{
[ScaffoldColumn(false)]
public int CategoryId { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Question> Question { get; set; }
}
In my QuestionController, I have added code to be able to access the available categories for a dropdownlist in the view -
private void PopulateCategoryDropDownList(object selectedCategory = null)
{
var categoryQuery = from c in db.Categories
orderby c.Name
select c;
ViewBag.categoryId = new SelectList(categoryQuery, "CategoryId", "Name", selectedCategory);
}
And I have the following methods for create -
// GET: /Question/Create
public ActionResult Create()
{
PopulateCategoryDropDownList();
return View();
}
//
// POST: /Question/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Question question)
{
try
{
var errors = ModelState.Values.SelectMany(v => v.Errors);
if (ModelState.IsValid)
{
db.Questions.Add(question);
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException dex)
{
ModelState.AddModelError("",dex.Message);
}
PopulateCategoryDropDownList(question.Category.CategoryId);
return View(question);
}
My view for creating a new question is as follows -
#model Quiz.Models.Question
#{
ViewBag.Title = "Create";
}
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Question</legend>
<div class="editor-label">
#Html.LabelFor(model => model.QuestionText)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.QuestionText)
#Html.ValidationMessageFor(model => model.QuestionText)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AnswerA)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AnswerA)
#Html.ValidationMessageFor(model => model.AnswerA)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AnswerB)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AnswerB)
#Html.ValidationMessageFor(model => model.AnswerB)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AnswerC)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AnswerC)
#Html.ValidationMessageFor(model => model.AnswerC)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.AnswerD)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.AnswerD)
#Html.ValidationMessageFor(model => model.AnswerD)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Correct)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Correct)
#Html.ValidationMessageFor(model => model.Correct)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.categoryId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.categoryId,(SelectList)ViewBag.categoryId)
#Html.ValidationMessageFor(model => model.categoryId)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
So, the issue is that although the question can be created, the categoryId from the dropdownlist is always null.
I have tried a bunch of things, ranging from attempting to access the dropdownlist directly to creating a different viewmodel. However, none of them work as required. Also, my code follows the tutorials available online. I'm not able to figure out what is different.
Please do help me find the mistake in my code.
We might have to narrow things down, I have a feeling something is going wrong with the models being read correctly from ViewBag. Try replacing your Create actions for a moment with the following, where your custom ViewBag filling function has been removed:
public ActionResult Create() {
ViewBag.categoryId = new SelectList(db.Categories, "CategoryId", "Name");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Question question) {
try {
var errors = ModelState.Values.SelectMany(v => v.Errors);
if (ModelState.IsValid) {
db.Questions.Add(question);
db.SaveChanges();
return RedirectToAction("Index");
}
} catch (DataException dex) {
ModelState.AddModelError("",dex.Message);
}
ViewBag.categoryId = new SelectList(db.Categories, "CategoryId", "Name", question.Category.CategoryId);
return View(question);
}
Does this run correctly?
If this doesn't work it must be a model-binding issue. The last thing I can think of trying is change the ViewBag calls to effect the field Category instead of CategoryId. Also update your view when making the DropDownList.

Model Validation with View Model which is inherited from Business Object

In MVC4 app, I have some Business Objects which am inheriting as view model. I want to use DataAnnotations in view model rather than specifying it in Business Object. Is that possible.
Business Objects
public class Country
{
[Display(Name = "Country")]
public virtual int CountryId { get; set; }
public virtual string CountryName { get; set; }
public virtual string CountryCode { get; set; }
}
public class State : Country
{
[Display(Name = "State")]
public virtual int StateId { get; set; }
public virtual string StateName { get; set; }
public virtual string StateCode { get; set; }
}
public class City : State
{
public virtual int CityId { get; set; }
[Display(Name = "City")]
public virtual string CityName { get; set; }
}
View Model
public class Regional
{
public Regional()
{
Countries = new Collection<Country>();
States = new Collection<State>();
City = new City();
}
public Collection<Country> Countries { get; set; }
public Collection<State> States { get; set; }
[Required(ErrorMessage = "Required")]
public City City { get; set; }
}
Here in the above code, I am using data annotations for City Property.
The problem here am facing is,even if I don't have this required annotation the form getting validated for Country and State but not validating for City Name.
When I add the required annotation, it is still getting validated only for country and state but not city name.
Any ideas?
My View
#using (Html.BeginForm(Actions.AddCity, Controllers.AdminUtility, FormMethod.Post))
{
<div class="alert-error">
#Html.ValidationSummary()
</div>
<div class="row-fluid">
<fieldset class="form-horizontal">
<legend class="header">Add City</legend>
<div class="control-group">
#Html.LabelFor(m => m.City.CountryId, new { #class = "control-label" })
<div class="controls">
#Html.DropDownListFor(m => m.City.CountryId, new SelectList(Model.Countries, "CountryId", "CountryName"),"", new { #class = "chosen", data_placeholder = "Choose Country..." })
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.City.StateId, new { #class = "control-label" })
<div class="controls">
#Html.DropDownListFor(m => m.City.StateId,new SelectList(Model.States, "StateId","StateName"), "",new { #class = "chosen", data_placeholder = "Choose State..." })
</div>
</div>
<div class="control-group">
#Html.LabelFor(m => m.City.CityName, new { #class = "control-label" })
<div class="controls">
#Html.TextBoxFor(m => m.City.CityName)
</div>
</div>
<div class="control-group">
<div class="controls">
<input type="submit" />
</div>
</div>
</fieldset>
</div>
}
Your validation is saying that the class is required. You want the CityName to be required, so in your scenario, no, you have to specify the attributes on the domain model.
Just a note - I don't think your domain model is very clear. City, State, and Country don't inherit from one another, they are just related. A State is in a country, it isn't a country... Also, you should always validate your domain objects since they may be populated from multiple sources. Your ViewModel would then have additional validation that is specific to that view -- maybe it has some additional data used for ui processing that doesn't need to be passed on to your domain, etc.