I'm trying to create HttpPost method to create a new database entry. It should take 2 foreign IDs from different database tables and "name". Here's the model:
public class Domena
{
public int DomenaID { get; set; } // this domains ID
public int TLDID { get; set; } // foreign id
public int KlientID { get; set; } // foreign id
public string Nazwa { get; set; }
public virtual TLD TLD { get; set; }
public virtual Klient Klient { get; set; }
}
Right, so basically this is what I have now :
// GET: /Domena/Add_Domain
public ActionResult Add_Domain()
{
ViewBag.TLDID = new SelectList(db.TLDs, "TLDID", "Typ");
ViewBag.KlientID = new SelectList(db.Klienci, "KlientID", "KlientID");
return View();
}
//
// POST: /Domena/Add_Domain
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Add_Domain(Domena domena)
{
if (ModelState.IsValid)
{
db.Domeny.Add(domena);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.TLDID = new SelectList(db.TLDs, "TLDID", "Typ", domena.TLDID);
ViewBag.KlientID = new SelectList(db.Klienci, "KlientID", "KlientID", domena.KlientID);
return View(domena);
}
The way it works now is, it will display a drop-down list from which I can choose TLDID by "Typ" and KlientID by "KlientID" entry in the database. Also it ask for a "Nazwa", which is name that has to be written.
I want to remove the option to choose the KlientID from the dropdownlist and instead make HttpPost take the KlientID from the link. Example :
I go to client's details page : /Klient/Details/6
I click on Add_Domain link which takes currently viewed KlientID and takes me to: /Domena/Add_Domain/6
So, my question is, how can I modify both Get and Post methods in order to create a new "domena" entry in the database to the client's id which is in the link ?
Do I have to change anything in view as well ?
Here is my current Add_Domain view fieldset :
<fieldset>
<legend>Domena</legend>
<div class="editor-label">
#Html.LabelFor(model => model.TLDID)
</div>
<div class="editor-field">
#Html.DropDownList("TLDID", String.Empty)
#Html.ValidationMessageFor(model => model.TLDID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.KlientID)
</div>
<div class="editor-field">
#Html.DropDownList("KlientID", String.Empty)
#Html.ValidationMessageFor(model => model.KlientID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Nazwa)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Nazwa)
#Html.ValidationMessageFor(model => model.Nazwa)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
Thanks in advance!
If you use the default MVC routes {controller}/{action}/{id} you should able to leverage the id parameter. The Add_Domain link in your client page would set a route value:
#Html.ActionLink("Add a domain", "Add_Domain", "Domena",
new { id = Model.KlientID }, null)
You give the GET action a parameter and set this parameter on the model:
public ActionResult Add_Domain(int id)
{
ViewBag.TLDID = new SelectList(db.TLDs, "TLDID", "Typ");
var model = new Domena { KlientID = id };
return View(model);
}
And in your view you remove label and validation message for KlientID and replace the drop down list by a hidden input:
#Html.HiddenFor(model => model.KlientID)
In the POST action you only remove the ViewBag.KlientID = ... line. The rest can remain unchanged. The MVC model binder will bind the hidden field to the domena.KlientID property.
The id parameter is a bit misused here because this id usually is related to the model the Domena controller is dealing with, so usually a DomenaID and not a KlientID. But it should still work. I would consider to use a query parameter instead to make it clearer that the last parameter in the route is a KlientID:
The action link would be:
#Html.ActionLink("Add a domain", "Add_Domain", "Domena",
new { klientID = Model.KlientID }, null)
And the GET action is:
public ActionResult Add_Domain(int klientID)
{
ViewBag.TLDID = new SelectList(db.TLDs, "TLDID", "Typ");
var model = new Domena { KlientID = klientID };
return View(model);
}
POST action is the same. The created link is then /Domena/Add_Domain?klientID=6.
Related
Alright I've been with this issue for 2 days. For simplicity I'll write code as less as possible.
I'm using the same controller both for viewing and saving data. My domain class, view, controller are as follows (I'm having issue with the last prop public DateTime CreatedOn, the whole problem is explained at the end so you can skip to it first):
Domain Class:
public partial class Admin_TestCategory
{
[Key]
[Column(Order = 0)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public short TestCategoryId { get; set; }
[StringLength(100)]
[Column(Order = 1)]
[Required]
public string TestCategoryName { get; set; }
[Column(Order = 2)]
public int? CreatedBy { get; set; }
[Column(Order = 3)]
public DateTime CreatedOn { get; set; }
}
View:
#model BOL.Entities.Admin.Admin_TestCategory
#{
ViewBag.Title = "Edit Category";
Layout = "~/Views/Admin/_Admin.cshtml";
}
#using (Html.BeginForm("EditTestCategory", "Admin", FormMethod.Post))
{
<div class="row">
<div class="col-lg-12">
<fieldset>
<legend>Edit Category</legend>
<div class="form-group row">
#Html.HiddenFor(m => m.TestCategoryId)
#Html.LabelFor(m => m.TestCategoryName, "Category Name :", new {#class = "col-lg-2"})
#Html.TextBoxFor(m => m.TestCategoryName, new {#class = "col-lg-3 form-control"})
<div class="col-lg-1"></div>
#Html.LabelFor(m => m.CreatedBy, "Created By :", new {#class = "col-lg-2"})
#Html.TextBoxFor(m => m.CreatedBy, new {#class = "col-lg-3 form-control"})
</div>
<div class="form-group row">
<div class="col-lg-6">
<button type="submit" class="btn btn-primary form-group col-lg-2">Update</button>
</div>
<div class="col-lg-6">
<div class="row">
#Html.LabelFor(m => m.CreatedOn, "Created On :", new {#class = "col-lg-4"})
#Html.TextBoxFor(m => m.CreatedOn, "{0:dd-MM-yyyy}", new {Name = Html.NameFor(m => m.CreatedOn), #class = "col-lg-6 form-control datepicker" })
</div>
</div>
</div>
</fieldset>
</div>
</div>
}
and the Controller:
// GET /Admin/EditTestCategory
[HttpGet]
public ActionResult EditTestCategory(Admin_TestCategory newTestCategory)
{
var testCatService = new TestCategoryService();
var oldTestCategory = testCatService.GetById(newTestCategory.TestCategoryId);
//check if that category exists or not
//category does not exist
if (oldTestCategory == null)
return HttpNotFound("Can not find the ID of the given Test Category");
//category exists
return View(oldTestCategory);
}
// POST /Admin/UpdateTestCategory
[HttpPost]
public ActionResult UpdateTestCategory(Admin_TestCategory newTestCategory)
{
var testCatService = new TestCategoryService();
var oldTestCategory = testCatService.GetById(newTestCategory.TestCategoryId);
//check if that category exists or not
//category does not exist
if (oldTestCategory == null)
return HttpNotFound("Can not find the ID of the given Test Category");
//category exists
//check if the new data is changed or not
//data is not changed
var o = JsonConvert.SerializeObject(oldTestCategory);
var n = JsonConvert.SerializeObject(newTestCategory);
if (o == n)
return RedirectToAction("TestCategory", "Admin");
//data is changed
testCatService.UpdateTestCategory(newTestCategory);
return RedirectToAction("TestCategory", "Admin");
}
How the controller works is all written with comments. The way it works is, when this controller is hit for first time it will only have the id property then it will fetch other data using that property and fill the View. Then when I hit the button of my view this controller's parameter will have all the changed data and hence it will update database. The only problem is when the controller is hit for 2nd time, it gets all the other data through model binding but only the last one CreatedOn which is a DateTime. I provided Name attribute to make sure it binds the model but it doesn't, in the controller the date becomes reset, although in the browser I inspected it and yes it has the exact Name that it needs.
I tried to make this form POST, tried to add attribute [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")] in the DateTime prop in my domain class but it doesn't work because in view its a TextBoxFor and I need it that way for auto validation. So what else should I try?
UPDATE
I have split the controller in 2, one for showing all data using GET another for updating all data using POST. Still same problem.
Add Below code to your Post action it will helpful to findout which field is causing error.
I tried executing your code on my machine and it seems error with "Date" format. try using "{0:MM/dd/yyyy}"; format in your code it will resolve your error.
foreach (ModelState modelState in ViewData.ModelState.Values)
{
foreach (ModelError error in modelState.Errors)
{
var errorMsg = error.ErrorMessage;
}
}
Happy Coding :)
Updated: Add DisplayFormat attribute in ur model property as it is in ur view code
as per the comments, You need to have a separate GET and POST action. On the Get action you need to find the item you want to edit and return it as a View Model:
public ActionResult EditTestCategory(short id)
{
var testCatService = new TestCategoryService();
var categoryToEdit = testCatService.GetById(id);
//check if that category exists or not
//category does not exist
if (categoryToEdit == null)
return HttpNotFound("Can not find the ID of the given Test Category");
return View(categoryToEdit);
}
Then you need a POST action that accepts the edited model like so:
[HttpPost]
public ActionResult EditTestCategory(Admin_TestCategory viewModel)
{
var testCatService = new TestCategoryService();
Admin_TestCategory categoryToEdit = testCatService.GetById(viewModel.TestCategoryId);
if(ModelState.IsValid)
{
categoryToEdit.TestCategoryName = viewModel.TestCategoryName;
categoryToEdit.CreatedBy = viewModel.CreatedBy;
//etc
testCatService.UpdateTestCategory(categoryToEdit);
}
return RedirectToAction("TestCategory", "Admin");
}
The other issue you have is that CreatedBy is data type int? in Admin_TestCategory.cs
In your view you're using:
#Html.TextBoxFor(m => m.CreatedBy, new { #class = "col-lg-3 form-control" })
If you change to:
#Html.EditorFor(m => m.CreatedBy, new { #class = "col-lg-3 form-control" })
Model Binds Fine
I am a new to MVC and still learning! I am trying to create a very basic App in my web which allows users to convert money value according to their preference. I made the web APi and was successful to call the service to my forms. However, in my controller I managed to get the currencies (names) to the index view, but cannot post the form back once entering a value in the text box to generate the partial view! What am I doing wrong in my codes?!
Currency Controller
namespace MVC_ATM.Controllers
{
public class CurrencyController : Controller
{
[HttpGet]
// GET: Currency
public ActionResult Index()
{
CurrenciesClient Cur = new CurrenciesClient();
var listCurrency = Cur.findAll();
SelectList list = new SelectList(listCurrency,"Id", "CurrencyName");
ViewBag.listCurrencies = list;
return View();
}
[HttpPost]
public ActionResult Index(Currencies cur)
{
if (!ModelState.IsValid)
{
string errors = string.Join("<br />", ModelState.Values
.SelectMany(x => x.Errors)
.Select(x => x.ErrorMessage));
return new ContentResult { Content = errors };
var rate = Convert.ToDecimal(cur.ConversionRate);
if (cur.CurrencyName == cur.CurrencyName)
{
ModelState.AddModelError("CurrencyCountry", "Can't make the conversion for the same value");
}
else if (cur.CurrencyName != cur.CurrencyName)
{
foreach (var currency in cur.CurrencyName)
{
ViewBag.Theresult = rate * cur.Value;
}
return PartialView("_CurrencyValue");
}
}
return View();
}
}
}
Currencies Model
namespace Project.Model
{
public class Currencies
{
public int Id { get; set; }
public string CurrencyName { get; set; }
public string CurrencyCountry {get; set;}
public decimal Value { get; set; }
public string ConversionRate { get; set; }
}
}
Index View
#model Project.Model.Currencies
#{
ViewBag.Title = "Index";
}
<h2>Currency</h2>
<body>
<div class="converter">
Convert: #Html.TextBoxFor(m => m.ConversionRate, new { #size = "5" })
<div class="form-group">
#Html.Label("Convert from", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownList("Currency List", ViewBag.listCurrencies as SelectList, "Please Select a currency")
</div>
</div>
<div class="form-group">
#Html.Label("Convert to", new { #class = "col-md-2 control-label" })
<div class="col-md-10">
#Html.DropDownList("Currency List", ViewBag.listCurrencies as SelectList, "Please Select a currency")
</div>
</div>
<div>
<button type="submit" class="btn btn-primary">Convert</button>
</div>
</div>
</body>
Couple of things to notice, is the POST action and missing form tag in the view . You created a POST action that accepts Currencies model but the form doesn't post that. Only ConversionRate will bind to the model. To get the "Currency From" and "Currency To" and the "Conversion Rate" you will require a different approach/small changes.
ConversionModel.cs a new Model for index page that will capture your required fields.
public class ConversionModel
{
[Required]//decimal would be better but up to you requirement
public decimal ConversionRate { get; set; }
[Required]
public int FromCurrencyId {get;set;}
public SelectList FromCurrencies {get;set;}
[Required]
public int ToCurrencyId {get;set;}
public SelectList ToCurrencies {get;set;}
}
Get: while there is nothing wrong with what you've done, lets use a model approach and tightly bind it.
public ActionResult Index()
{
CurrenciesClient Cur = new CurrenciesClient();
var listCurrency = Cur.findAll();
ConversionModel model = new ConversionModel();
model.FromCurrencies = new SelectList(listCurrency,"Id", "CurrencyName");
model.ToCurrencies = new SelectList(listCurrency,"Id", "CurrencyName");
return View(model);
}
Post: Important thing here to notice is the SelectList will not be posted back. Only the ConversionRate, FromCurrencyId and ToCurrencyId are sent back not the Lists. If error occurs you will need to rebuild the lists and send it back in the model.
[HttpPost]
public ActionResult Index(ConversionModel curModel)
{
if(ModelState.IsValid)
{
if(curModel.FromCurrencyId ==curModel.ToCurrencyId)
{
//do something if same currecnies and return.
}
else
{
//Get the currencyList with rates from db
//use currency ToCurrencyId and FromCurrencyId to fetch the 2 currencies
// perform conversion with curModel.ConversionRate with existing logic
}
}
//Don'f forget to rebuild the Select Lists...
return View(curModel);
}
View:
#model Project.Model.ConversionModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("Index", "Currency", FormMethod.Post)
{
#Html.TextBoxFor(m => m.ConversionRate, new { #size = "5" })
#* Please check the syntax *#
#Html.DropDownListFor(m => m.FromCurrencyId , Model.FromCurrencies as SelectList)
#Html.DropDownListFor(m => m.ToCurrencyId , Model.ToCurrencies as SelectList)
<button type="submit" class="btn btn-primary">Convert</button>
}
Not a CUT_COPY_PASTE. please do check for errors if any. It is only an approach.
ajax POST probably the next thing to learn... Let us know.
You need to put your items inside a form like this:
#using (Html.BeginForm("Index", "Currency", FormMethod.Post)
{
// Your form items
}
Using MVC4 am wanting to implement functionality which will allow a user to add new items to the database.
I've managed to achieve this adding items to a single table, but now I need to display data from multiple tables, then populate the added / selected data to those tables.
I have these 3 tables
Threats
ID
Description
ThreatHasSecurityEvent
ThreatID
SecurityEventID
SecrutiyEvents
ID
Description
And here's my code so far:
ViewModel
public class ThreatWithSecurityEvents
{
public Threat Threat { get; set; }
public SecurityEvent SecurityEvent { get; set; }
public List<int> SecurityEventIds { get; set; }
public ThreatWithSecurityEvents()
{
SecurityEventIds = new List<int>();
}
}
Get Controller
[HttpGet]
public ActionResult AddNewThreat()
{
ThreatWithSecurityEvents ViewModel = new ThreatWithSecurityEvents();
var SecurityEvents = _DBContext.SecurityEvents.Select(x => new SelectListItem()
{
Text = x.Description,
Value = x.ID.ToString()
});
ViewBag.SecurityEventDropdown = SecurityEvents;
return View(ViewModel);
}
View
#model RiskAssesmentApplication.Models.ThreatWithSecurityEvents
#{
ViewBag.Title = "AddNewThreat";
//Layout = "~/Views/Shared/MasterLayout.cshtml";
}
<div style="font-family: Calibri">
<h2>AddNewThreat</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Threat</legend>
#using (Html.BeginForm("Add New Threat", "Threats"))
{
Html.HiddenFor(model => model.SecurityEventIds);
<div class="editor-label">
#Html.LabelFor(model => #Model.Threat.Description, "Threat Description")
</div>
<div class="editor-field">
#Html.EditorFor(model => #Model.Threat.Description)
#Html.ValidationMessageFor(model => #Model.Threat.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => #Model.SecurityEvent.Description, "Associated Security Event")
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.SecurityEventIds, ViewBag.SecurityEventDropdown as IEnumerable<SelectListItem>)
</div>
<p>
<input type="submit" value="Add New" />
</p>
}
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</div>
Am unsure how to implement the Post Action Method and a Save Method in the repository.
Previously I could inject a new Threat Object and send it to the edit view doing something like:
Previous Get Method - AddNewThreat
[HttpGet]
public ActionResult AddNewThreat()
{
return View("EditThreat", new Threat());
}
and I would then use the EditThreat Action Method to post back
Previous Post Action - AddNewThreat
[HttpPost]
public ActionResult EditThreat(Threat Threat)
{
if (ModelState.IsValid)
{
repository.SaveThreat(Threat);
TempData["message"] = string.Format("{0} new description has been saved", Threat.Description);
return RedirectToAction("GetThreat", new { ThreatID = Threat.ID });
}
else
{
// something is incorrect!
return View(Threat);
}
}
Previous Save Method - SaveThreat From Repository
public void SaveThreat(Threat Threat)
{
if (Threat.ID == 0)
{
_context.Threats.Add(Threat);
}
else
{
Threat dbEntry = _context.Threats.Find(Threat.ID);
if (dbEntry != null)
{
dbEntry.Description = Threat.Description;
}
}
_context.SaveChanges();
}
That's as far as I have got so far.
I want the user to be able to enter a new threat description and then select a security event or multiple events from a drop down list which will be associated with the new threat.
I realize am going to have to change the post back action method in the controller and the Save method in my repository, but I cant work out how to get both the new Threat description and the existing security events saved back to the database. I've had a search but as of yet haven't found / understood anything.
Any advice/help would be great.
Thanks
You view model should be
public class NewThreatVM
{
public string Description { get; set; } // add validation attributes as required
public List<int> SelectedSecurityEvents { get; set; }
public SelectList SecurityEventList { get; set; } // or IEnumerable<SelectListItem>
}
Side note: The Threat.ID property is not required in a create view, however if your want to use this for editing an existing Threat as well, add property int? ID and use if (model.ID.HasValue) in the POST method to determine if its a new or existing Threat
and the simplified view
#model yourAssembly.NewThreatVM
#Html.BeginForm())
{
#Html.TextBoxFor(m => m.Description)
#Html.ListBoxFor(m => m.SelectedSecurityEvents, Model.SecurityEventList)
<input type="Submit" value="Create" />
}
Side notes: Your view should not include a hidden input for the Security Event ID's (you cannot bind an input to a complex object or collection)
then the controller
public ActionResult Create()
{
NewThreatVM model = new NewThreatVM model();
ConfigureViewModel(model);
return View(model);
}
[HttpPost]
public ActionResult Create(NewThreatVM model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
// Initialize new data model and map properties from view model
Threat threat = new Threat() { Description = model.Description };
// Save it (which will set its ID property)
_context.Threats.Add(Threat);
_context.SaveChanges();
// Save each selected security event
foreach (int selectedEvent in model.SelectedSecurityEvents)
{
ThreatHasSecurityEvent securityEvent = new ThreatHasSecurityEvent()
{
ThreatID = threat.ID,
SecurityEventID = selectedEvent
};
_context.ThreatHasSecurityEvents.Add(securityEvent);
}
_context.SaveChanges();
return RedirectToAction("GetThreat", new { ThreatID = threat.ID });
}
private void ConfigureViewModel(NewThreatVM model)
{
var securityEvents = _context.SecurityEvents;
model.SecurityEventList = new SelectList(securityEvents, "ID", "Description");
}
I believe the easiest way to achieve this, is "dividing" your form into separated steps.
You have2 entities: Threats, SecurityEventID
Threat has a collection of SecurityEvents
Create a form to add/edit Threats (url: Threats/Add | Threats/Edit/ThreatId)
Create a form to add/delete Events of an existing Threat (url: Threats/AddEvent/ThreatIdHere
Use custom ViewModels instead of the original class to send data to controller. Examples:
public class AddThreatViewModel
{
public string Description { get; set; }
//since it's a add view model, we dont need a ThreatId here
}
[HttpPost]
public ActionResult AddThreat(AddThreatViewModel model)
{
//convert the view model to Threat, add to database
}
public class AddThreatEvent
{
public int ThreatId { get; set; }
public int SecrutiyEventId { get; set; }
}
[HttpPost]
public ActionResult AddThreatEvent(AddThreatEventmodel)
{
//add threat event into existing threat
}
This is my model:
[Display(Name = "Company")]
public int CompanyId { get; set; }
public SelectList Companies { get; set; }
Setting Data for view
[HttpGet]
[AllowAnonymous]
public ActionResult Index()
{
var model = new Ticket
{
Companies = new SelectList(new List<SelectListItem> { new SelectListItem { Text = "0", Value = "0" } }, "Value", "Text")
};
return View(model);
}
The View:
<div class="form-group">
#Html.LabelFor(m => m.CompanyId, new { #class = "col-md-6 col-md-pull-1 control-label" })
<div class="col-md-3 col-md-pull-1">
#Html.DropDownListFor(m => m.CompanyId, Model.Companies, new { #class = "selectpicker form-control" })
</div>
</div>
Results:
How can i get the SelectList to be posted back to the Controller so i dont have to keep populating it?
Model.Companies wont be binded because is not in the Form, the ModelBinder is looking for an input in the Form or a querystring value with the name of the property (in this case Companies) but you don´t have one in the view with that name, on the contrary, you have a field with the name CompanyId, that´s why the property CompanyId is binded.
A good way to save your Companies list and get it in the Post action is to use TempData, with TempData you can save an item in an action, and it´ll be available to the next action. Is like Session, but just for one Request.
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; }
}