When posting the html form I can see null in the controller, so I am not able to add a Product to the database. What is wrong here?
public partial class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
}
public ActionResult CreateProduct()
{
return View();
}
[HttpPost]
public ActionResult CreateProduct(Product product) // this is always null
{
service.CreateProduct(product);
return RedirectToAction("Index");
}
#model WebApplication1.Models.Product
#{
ViewBag.Title = "CreateProduct";
}
<h2>CreateProduct</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
#Html.LabelFor(model => model.Name)
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
<input type="submit" value="Create" />
</fieldset>
}
in your get pass the model with empty values
public ActionResult CreateProduct()
{
Product prd= new Product();
return View(prd);
}
and check if this solves your problem
Related
I've got a model:
public class PersonViewModel
{
public int Id { get; set; }
[MaxLength(25)]
public string Firstname { get; set; }
[MaxLength(50)]
public string Surname { get; set; }
}
And a ViewModel to contain one instance and a list of the above:
public class PeopleSearchViewModel
{
public PersonViewModel Person { get; set; }
public List<PersonViewModel> People { get; set; }
}
Then my View:
#model PeopleSearchViewModel
#using (Html.BeginForm("Search", "Home", FormMethod.Post))
{
#Html.LabelFor(m => m.Person.Firstname)
#Html.HiddenFor(m => m.Person.Firstname)
#Html.TextBoxFor(m => m.Person.Firstname)
#Html.ValidationMessageFor(m => m.Person.Firstname)
<input type="submit" value="Search" id="Whatever"/>
}
And finally the controller:
[HttpPost]
public ActionResult Search(PeopleSearchViewModel theModelIsntPassing)
{
}
The model is not being passed to the controller on form submission?
Or maybe it is, but the individual properties aren't populated.
The ActionResult Search method is definitely being called, just theModelIsntPassing has no values in its nested property
Not sure why you are having both HiddenFor and TextBoxFor for FirstName.
Please try comment/remove #Html.HiddenFor(m => m.Person.Firstname) statement in your view
#model PeopleSearchViewModel
#using (Html.BeginForm("Search", "Home", FormMethod.Post))
{
#Html.LabelFor(m => m.Person.Firstname)
#*#Html.HiddenFor(m => m.Person.Firstname)*#
#Html.TextBoxFor(m => m.Person.Firstname)
#Html.ValidationMessageFor(m => m.Person.Firstname)
<input type="submit" value="Search" id="Whatever"/>
}
I have a simple mvc5 code first application, It has a ms SQL database in the back-end and and a form in the front-end.
While I insert into database via the front end form, it does not generate any error, everything seems OK but when i check the back end database table, then all values in the newly inserted row are showing as NULL.
This is my code for model:
public class students
{
public int Id { get; set; }
[Display(Name = "Name")]
public string st_name { get; set; }
[Display(Name = "Father's Name")]
public string st_father_name { get; set; }
public string st_contact { get; set; }
}
This is the View Model class:
public class AddStudentViewModel
{
public students stdntss { get; set; }
}
This is the controller:
public ActionResult Index()
{
var std = _context.stdnts;
if (std==null)
{
return Content("Nothing Found");
}
return View(std);
}
public ActionResult AddStudent()
{
return View();
}
[HttpPost]
public ActionResult Insert(students st)
{
_context.stdnts.Add(st);
_context.SaveChanges();
return RedirectToAction("Index","Students");
}
And finally this is the view:
#model school2.ViewModels.AddStudentViewModel
#{
ViewBag.Title = "AddStudent";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>New student's registration form</h2>
#using (Html.BeginForm("Insert","Students"))
{
<div class="form-group">
#Html.LabelFor(m=> m.stdntss.st_name)
#Html.TextBoxFor(m=> m.stdntss.st_name, new { #class="form-control"})
</div>
<div class="form-group">
#Html.LabelFor(m => m.stdntss.st_father_name)
#Html.TextBoxFor(m => m.stdntss.st_father_name, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.stdntss.st_contact)
#Html.TextBoxFor(m => m.stdntss.st_contact, new { #class = "form-control" })
</div>
<button type="submit" class="btn btn-primary">Save</button>
}
Kindly assist me if anyone has any clue?
One way to solve this is to change the POST method to accept the same model as the view.
try changing
public ActionResult Insert(students st)
{
_context.stdnts.Add(st);
_context.SaveChanges();
return RedirectToAction("Index","Students");
}
to
public ActionResult Insert(AddStudentViewModel st)
{
_context.stdnts.Add(st.stdntss );
_context.SaveChanges();
return RedirectToAction("Index","Students");
}
or changing the model of the form to simply be student.
I think that change #Html.TextBoxFor(model=> model.stdntss.st_name, new { #class="form-control"}). because call Model, #model school2.ViewModels.AddStudentViewModel . Variable default Model.
My ViewModel looks like this:
public class ProjectViewModel
{
public ICollection<Project> Projects { get; set; }
}
My controller:
[HttpPost]
[Route("ChooseProject")]
public ActionResult ChooseProject(Project projects)
{
return RedirectToAction("Index", "Application");
}
And finally my view:
#using ServicePortal.Web.Controllers
#model ServicePortal.Web.Models.ProjectViewModel
#{
Layout = "~/Views/Shared/_Layout.cshtml";
ViewBag.Title = "Choose project";
}
<div class="pure-form pure-form-aligned">
#using (Html.BeginForm("ChooseProject", "Login", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="innerForm formgroup">
#Html.ValidationSummary(true, "", new { #class = "" })
#Html.DropDownListFor((model => model.Projects), new SelectList(Model.Projects,"ProjectKey", "ProjectName"))
<div class="row">
<button type="submit" class="button pull-right">Go on</button>,
</div>
</div>
}
</div>
The dropdownlist is populated with the correct values, but I would like to return either a Project or a string with ProjectKey to the controller. However, when I submit the form and check the value in the parameter in the controller, it is always null, both when I try to return a Project and when I try with string. I don't get any errors. What am I doing wrong?
Try this out:
public class ProjectViewModel
{
public string SelectedProjectKey { get; set; } // string may need to be int depending on what type ProjectKey is in your class
public ICollection<Project> Projects { get; set; }
}
In the view:
#Html.DropDownListFor((model => model.SelectedProjectKey), new SelectList(Model.Projects,"ProjectKey", "ProjectName"))
Your controller action:
public ActionResult ChooseProject(ProjectViewModel model)
This will get you the key of whatever project you selected in model.SelectedProjectKey and you can use that in your controller chooseproject action method to do whatever you intended.
In my MedicalProductController, I am trying to make my Create and Delete actions able to create and delete multiple objects on one page by passing IEnumerable<ViewModel> to the view. The approach I am using with my Create action worked with my Edit Action in this stack overflow question. But it is not working with my create action, so I suspect something isn't right.
The problem is that no fields appear on the page. The page simply says: "Create", "Save" (button), "Back to list" (a clickable link) on that page. In-between "Create" and "Save" there should be the html editor fields and labels created by MedicalProductViewModel.cshtml shown below.
Here is what my setup looks like.
Create.cshtml
#model IEnumerable<MvcMedicalStore.Models.MedicalProductViewModel>
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
#Html.EditorFor(m => m)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Views\MedicalProduct\EditorTemplates\MedicalProductViewModel.cshtml
#model MvcMedicalStore.Models.MedicalProductViewModel
#Html.HiddenFor(item => Model.ID)
<div class="editor-label">
#Html.LabelFor(item => Model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Name)
#Html.ValidationMessageFor(item => Model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(item => Model.Price)
#Html.ValidationMessageFor(item => Model.Price)
</div>
<div class="editor-label">
#Html.LabelFor(item => Model.ID)
</div>
<div class="editor-field">
#Html.DropDownListFor(item => Model.BrandID, Model.BrandSelectListItem)
#Html.ValidationMessageFor(item => Model.BrandID)
</div>
MedicalProductController.cs
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();
public ActionResult Index()
{
var productList = _db.Products.ToArray();
var viewModelList = GetMedicalProductViewModelList(productList);
return View(viewModelList);
}
public IEnumerable<MedicalProductViewModel> GetMedicalProductViewModelList(IEnumerable<MedicalProduct> productList)
{
var brandList = _db.Brands.ToArray();
var mapper = new MedicalProductMapper();
return mapper.MapMedicalProductViewModel(productList, brandList);
}
public IEnumerable<MedicalProduct> GetMedicalProductList(IEnumerable<MedicalProductViewModel> viewModelList)
{
var mapper = new MedicalProductMapper();
return mapper.MapMedicalProductList(viewModelList);
}
//
// GET: /MedicalSupply/Create
public ActionResult Create()
{
return View();
}
//
// POST: /MedicalSupply/Create
[HttpPost]
[ValidateAntiForgeryToken]
//public ActionResult Create(MedicalProduct medicalProduct)
public ActionResult Create(IEnumerable<MedicalProductViewModel> productViewModelList)
{
var productList = GetMedicalProductList(productViewModelList);
if (ModelState.IsValid)
{
foreach (MedicalProduct product in productList)
_db.Products.Add(product);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(productViewModelList);
}
}
MedicalProductMapper.cs
public class MedicalProductMapper
{
public IEnumerable<MedicalProductViewModel> MapMedicalProductViewModel(IEnumerable<MedicalProduct> productList, IEnumerable<Brand> brandList)
{
var brandSelectListItem = brandList.Select(b => new SelectListItem()
{
Text = b.Name,
Value = b.ID.ToString()
});
var viewModelList = productList.Select(p => new MedicalProductViewModel()
{
BrandID = p.BrandID,
//BrandName = brandList.SingleOrDefault(b => b.ID == p.BrandID).Name,
BrandSelectListItem = brandSelectListItem,
ID = p.ID,
Price = p.Price,
Name = p.Name
});
return viewModelList;
}
public IEnumerable<MedicalProduct> MapMedicalProductList(IEnumerable<MedicalProductViewModel> viewModelList)
{
var modelList = viewModelList.ToArray().Select( viewModel => new MedicalProduct()
{
Name = viewModel.Name,
Price = viewModel.Price,
BrandID = viewModel.BrandID
});
return modelList;
}
}
EDIT
My edit view is very similar to my create view:
EDIT.cshtml
#model IEnumerable<MvcMedicalStore.Models.MedicalProductViewModel>
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
#Html.EditorFor(m => m)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Your Create() controller method is passing a null to your view, hence the editortemplate doesn't fire.
public ActionResult Create()
{
return View();
}
If you pass in a new List<>, then it will work like your editor.
public ActionResult Create()
{
return View(new List<MedicalProductViewModel>() { new MedicalProductViewModel() });
}
Is there a reason why you aren't just passing in a single viewmodel instead of a list? You will be able to use your editor template the same way.
Another thing to note is that it is probably not a good idea to pass in IEnumerable<> as your viewmodel. What if you need to pass other non-MedicalProduct properties to your view?
I would advocate a composite viewmodel instead, like so.
public class EditMedicalProductViewModel
{
public IEnumerable<MedicalProductViewModel> MedicalProducts { get; set; }
}
public class CreateMedicalProductViewModel
{
public MedicalProductViewModel MedicalProduct { get; set; }
}
In your edit view you'd do this instead
#Html.EditorFor(m => m.MedicalProducts)
And for your create view you'd do this
#Html.EditorFor(m=> m.MedicalProduct)
In your create method you should create a new medical product
return View(new CreateMEdicalProductViewModel() { MedicalProduct = new MedicalProductViewModel() } );
It's not working because your model is a IEnumerable<MedicalProductViewModel>, but your editor template is for a MedicalProductViewModel
You could try this:
<fieldset>
<legend>MedicalProduct</legend>
#foreach(var item in Model)
{
Html.EditorFor(model => item)
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
But this will probably not format the fields quite the way you want, since it will create numerous editors. The easiest method would be to refactor your controller so that it allows editing one record at a time.
i'm using razor's listboxfor for the first time, but my Model is always null.
after reading similar posts and tryouts it still won't work.
Person.cshtml
#model SampleApp.Web.ViewModel.PersonViewModel
#{
ViewBag.Title = "Welcome";
}
<article>
<p>
Welcome to example page.
</p>
<p>
<div class="container">
//Post data works as expected, controllers create method write to db successfully
#using (Html.BeginForm("Create", "Person", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Personen</legend>
<div class="editor-label">
#* #Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Age)
#Html.ValidationMessageFor(model => model.Age)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Surrname)
</div>
</fielset>
</div>
<p>
<input type="submit" value="Create" />
</p>
}
//binding to Model fails, Model is null. Not be able to debug anything in controller action, it stops when "loading" the page
#using (Html.BeginForm("GetListBoxData", "Person"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.ListBoxFor(model => model.ListboxData, Model.ListboxData);
}
</div>
PersonController.cs
[AcceptVerbs(HttpVerbs.Get)]
[ValidateAntiForgeryToken]
public ActionResult GetListBoxData()
{
var data = new List<PersonViewModel>();
data.Add(new PersonViewModel{Name = "Test", Surrname="testsurrname", Age=30});
var viewModel = new PersonViewModel()
{
ListboxData = data.AsEnumerable().Select(s=> new SelectListItem{Value=s.Name ,Text = s.Surrname}),
};
return View(viewModel);
}
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult GetListBoxData(PersonViewModel persondata)
{
//TODO: handle values from View
return View(this);
}
[ValidateAntiForgeryToken]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Include = "Name, Surrname, Age")] PersonViewModel persondata)
{
try
{
PersonService personDataProvider = new PersonService();
personDataProvider.SavePerson(persondata);
return new RedirectResult("SomewhereToGo");
}
catch (DataException ex)
{
//TODO: Log
}
return View(this);
}
PersonViewModel
public class PersonViewModel
{
public int PersonId{ get; set; }
public int Age { get; set; }
public string Name { get; set; }
public string Surrname { get; set; }
public IEnumerable<SelectListItem> ListboxData { get; set; }
}
writing values from editFor to db works as expected without code for listboxfor.
after adding it to my html it should be filled from db on page loading, but I get a ReferenceNotSet Exception on page loading. Model.ListboxData is null, before GetListBoxData action is called.
Thanks a lot for your help!
Your form should submit the data via POST, not GET. And, you don't need to use enctype = "multipart/form-data", unless you want to upload files through your from.
You need two Index Actions in your Controller, one is for sending the data from your Controller to the View, and the other one is for getting the data back from the View, when the form is submitted (POST) to the server.
The first argument you pass to your ListBox (the expression) refers to the Property in your Model that the selected item from your ListBox will be stored in, which in this case is PersonId.
So, your View should look like this:
#model MVCApplication.Web.ViewModel.PersonViewModel
#using (Html.BeginForm("Index", "Person"))
{
#Html.ListBoxFor(model => model.PersonId, Model.ListBoxData)
<input type="submit" value="Save" />
}
Then, in your Controller, you'll have two Actions like this:
public ActionResult Index()
{
var viewModel = new PersonViewModel()
{
ListboxData = data.Select(s => new SelectListItem { Value = s.PersonId.ToString(), Text = s.PersonId.ToString() }).AsEnumerable();
};
return View(viewModel);
}
[HttpPost]
public ActionResult Index(PersonViewModel viewModel)
{
// code to save the data in the database or whatever you want to do with the data coming from the View
}
By the way, in your ViewModel, you don't have to define your ListBoxData property like that, just do this:
public class PersonViewModel
{
public int PersonId{ get; set; }
public IEnumerable<SelectListItem> ListBoxData { get; set; }
}