Form POST gives empty model fields when using EditorFor - c#

Currently I have a form which I am wanting to place in multiple views. Currently when I submit this form, the model return has null for all it's values and i am unsure why.
When I debug through it is reaching the POST event but the model values are null. (not the model itself).
Contact Model
public class ContactModel : BasePageModel
{
public string Introduction { get; set; }
public ContactForm Form { get; set; }
public ContactModel(TreeNode node, ContactForm form = null) : base(node)
{
Introduction = node.GetEditableTextStringValue("Introduction", String.Empty);
Form = form;
}
}
View Model for the form
public class ContactForm
{
public string Title { get; set; }
public string FirstName { get; set; }
....
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SendContact(ContactForm model)
{
// Do stuff
}
This is the view of the page I want to "embed" if you will the contact form I want to submit.
#model MVCApp.Models.PageModels.ContactModel
....
#using (Html.BeginForm("SendContact", "Contact", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.EditorFor(model => model.Form)
}
And the editor template of the Contact Form itself
#model MVCApp.Models.FormModels.ContactForm
....
#Html.LabelFor(model => model.Title)
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title, "")
#Html.LabelFor(model => model.FirstName)
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName, "")
....
<input type="submit" value="Submit" />
The form renders both parts (The surrounding text and fields) as well as the contact form correctly. But as I said above when I click on submit the model that gets passed back in the POST to the controller (which it does hit through debugging) has all it's fields as null.
Any pointers in where I may have gone wrong would be great.

Marking this as the answer since Stephens comment can't be marked as such.
Using Stephens suggestion, I changed the ActionResult to use
public ActionResult SendContact([Bind(Prefix = "Form")]ContactForm model)
And it worked perfectly. The fields and the model are populated correctly.

Related

How do I insert data from textbox to model in MVC?`

So I'm pretty new in MVC, I got Mission model:
public class Mission
{
public ObjectId _id { get; set; }
public string MissionType { get; set; }
public string ElipseNumber { get; set; }
public string MissionDate { get; set; }
public string ReminderNumber { get; set; }
public string Notes { get; set; }
}
When the user is selecting a specific mission, it goes to the view as #ViewBag.SelectedMission
Now, I want to let the user have the option to add a note to a selected mission, so using a modal I added a textbox like so:
<div class="notesLabel">
#Html.LabelFor(model => model.Notes)
</div>
<div class="notesTextBox">
#Html.TextBoxFor(model => model.Notes)
</div>
Not sure exactly what to do, How do I take the input from the textbox and adding it to the SelectedMission.Notes?
Thanks from advance.
I am also a beginner but trying to help. From what I understand you can do something like below
Contoller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Mission model)
{
var notes= model.Notes;
//Set the values to view model accordingly and save to DB eventually
}
View
#model Mission
#using (Html.BeginForm("Create", "ControllerName"))
{
#Html.AntiForgeryToken()
// ALL YOUR HTML FIELD WILL COME HERE
<div class="notesLabel">
#Html.LabelFor(model => model.Notes)
</div>
<div class="notesTextBox">
#Html.TextBoxFor(model => model.Notes)
</div>
<input type="submit" value="save">
}
Try below code. The form will submit the details entered by user on button click. Assumption: the Controller name is Home and Action name which saves the notes data is SaveNotes. When user clicks the submit button, the data is sent to the SaveNotes action in HomeController. In action function, after validation is done, values are saved into to DB. If you don't want to save to DB, you can do anything as per your logic/design. Value will be in objMission.Notes.
<% Html.BeginForm("SaveNotes", "Home", FormMethod.Post); %>
#Html.AntiForgeryToken()
:
:
<div class="notesLabel">
#Html.LabelFor(model => model.Notes)
</div>
<div class="notesTextBox">
#Html.TextBoxFor(model => model.Notes)
</div>
:
:
<input type="submit" name="submit" value="Save" />
<% Html.EndForm(); %>
public class HomeController : Controller
{
:
:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SaveNotes(Mission objMission)
{
//Set the values to view model accordingly and save to DB eventually
if (ModelState.IsValid)
{
db.Missions.Add(objMission);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(objMission);
}
:
:
}

mvc 4 Model is null

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; }
}

Inserting data into SQL database using EF

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.

MVC3,PartialViews,Ajax Passing values

This is the model example.cs
namespace View_Partial_Editor.Models
{
public class ExampleView
{
...
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public string Field4 { get; set; }
public string Field5 { get; set; }
...
}
}
I have this view example.cshtml:
#model View_Partial_Editor.Models.ExampleView
#{Html.RenderPartial("EditExample",Model);}
#Html.TextBoxFor(m => m.Field1)
Then i have this partialView EditExample.cshtml:
#model View_Partial_Editor.Models.ExampleView
#Html.HiddenFor(m => m.Field1)
#using (Ajax.BeginForm("EditExample", new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "partial" }))
{
<div>
#Html.EditorFor(m => m, "Editor", null)
</div>
<p>
<input id="buttona" type="submit" value="Save" />
</p>
}
I have this controller ExampleController:
namespace View_Partial_Editor.Controllers
{
public class ExampleController : Controller
{
//
// GET: /Example/
public ActionResult Example()
{
return View();
}
[HttpPost]
public ActionResult EditExample(ExampleView example)
{
example.Field1 ="7";
return View("Example", example);
}
}
}
And this is the editor that is called in the partial editor.cshtml
#model View_Partial_Editor.Models.ExampleView
<div class="editor-label">
#Html.LabelFor(m => m.Field1 )
</div>
<div class="editor-field">
#Html.EditorFor(m => m.Field1)
#Html.ValidationMessageFor(m => m.Field1)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Field2)
</div>
<div class="editor-field">
#Html.EditorFor(m => m.Field2)
#Html.ValidationMessageFor(m => m.Field2)
</div>
My problem is that i want to modify the data of the model in the controller in the ajax call, and return the model modified to the exampleView.But when the ajax called finish the value that i change in the controller is not changed in the model
Edit: The thing that i want is to send the call to the ajax methos, save something in the database, then modify the model, and in the example view i want to have that model with the changes.
In this moment, if i replace the partial view with the result of the ajax, the model in the example view is not modified.Another way is to replace the full example view, so the model is received there, but i have to pass a lot of fields using Html.HiddenFor, is possible to make this without replacing the views only returnng the model with the changes
Try calling ModelState.Clear() in your HttpPost action method. Html helpers use the values in the ModelState first, then the Model. If you change a value in the model on a post therefore, you need to clear the value from the ModelState.

Using Editor Templates to Display Multiple Forms

My question is very similar to this one. The application I'm developing is written in MVC 3 and Razor. It lets its users select items from a store and send each one to a different address.
Here are my ViewModels:
public class DeliveryDetailsViewModel
{
public FromDetailsViewModel From { get; set; }
public IList<ToDetailsViewModel> To { get; set; }
}
public class DetailsViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
public class FromDetailsViewModel : DetailsViewModel
{
public string StreetAddress { get; set; }
public string Suburb { get; set; }
public string Postcode { get; set; }
}
public class ToDetailsViewModel : DetailsViewModel
{
public string Message { get; set; }
}
My View is similar to below.
#model Store.ViewModels.DeliveryDetailsViewModel
#Html.EditorFor(m => m.From)
#Html.EditorFor(m => m.To)
My intention is that a collection of forms (one per item in their shopping cart) will be displayed to enable the user to type different delivery details in. Each form has its own submit button.
The editor template which renders the "To" form is as follows:
#model Store.ViewModels.ToDetailsViewModel
#using (Html.BeginForm("ConfirmTo", "Delivery"))
{
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
#Html.TextBoxFor(m => m.Email)
#Html.TextBoxFor(m => m.Message)
<input type="submit" value="Confirm" />
}
My controller:
public class DeliveryController : Controller
{
public ActionResult Index()
{
var model = new DeliveryDetailsViewModel();
model.From = new FromDetailsViewModel();
model.To = new List<ToDetailsViewModel>();
return View(model);
}
public ActionResult ConfirmTo(ToDetailsViewModel toDetails)
{
// Save to database.
}
}
I have a couple of problems:
The "to" editor template isn't rendering anything (though it used to). It cites that the model types don't match (i.e., ToDetailsViewModel isn't the same as List<ToDetailsViewModel>), even though I thought editor templates were supposed to append indices to input field names to enable proper binding.
When clicking Confirm and submitting the first form in the To list the controller receives the view model with the correct bindings. Submitting any of the following forms (with index 1 or greater) calls the ConfirmTo action and passes a ToDetailsViewModel which is null.
Any help would be appreciated, and if you'd like more information about the problem I'm having or the code I'm using, please don't hesitate to ask.
1) The "to" editor template isn't rendering anything
In your controller action you didn't put anything in the list. You just instantiated it. So put some elements:
model.To = new List<ToDetailsViewModel>();
model.To.Add(new ToDetailsViewModel());
model.To.Add(new ToDetailsViewModel());
...
2) When clicking Confirm and submitting the first form in the To list the controller receives the view model with the correct bindings. Submitting any of the following forms (with index 1 or greater) calls the ConfirmTo action and passes a ToDetailsViewModel which is null.
I would be surprised if this works even for the first element because the input fields currently do not have correct names. They are all prefixed with To[someIndex] whereas your ConfirmTo expects a flat model, not a collection.
So You could set the prefix to an empty string so that correct input elements are generated in your ~/Views/Shared/EditorTemplates/ToDetailsViewModel.cshtml editor template:
#model ToDetailsViewModel
#{
ViewData.TemplateInfo.HtmlFieldPrefix = "";
}
#using (Html.BeginForm("ConfirmTo", "Home"))
{
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
#Html.TextBoxFor(m => m.Email)
#Html.TextBoxFor(m => m.Message)
<input type="submit" value="Confirm" />
}
1) have you try this, because your view model has
public IList<ToDetailsViewModel> To { get; set; }
To is a list therefore your editor template should have
#model IEnumerable<Store.ViewModels.ToDetailsViewModel>
and the template should use a foreach
#foreach(model in Model){}

Categories

Resources