c# MVC ViewModel is not passing to controller - c#

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"/>
}

Related

Passing two models to Controller using Ajax BeginForm()

I have View like this:
#model MVCApp.Models.User
#{
ViewBag.Title = "EditUser";
}
<h2>Edycja użytkownika</h2>
#using (Ajax.BeginForm("SaveUser", "My", new AjaxOptions { UpdateTargetId = "Result" }))
{
<fieldset>
<legend>Zmień dane użytkownika</legend>
<div id="EditUserForm">
<div>
#Html.LabelFor(m => Model.Login)
#Html.TextBoxFor(m => Model.Login)
</div>
<div>
#Html.LabelFor(m => Model.Password)
#Html.TextBoxFor(m => Model.Password)
</div>
<div>
#Html.LabelFor(m => Model.Name)
#Html.TextBoxFor(m => Model.Name)
</div>
<div>
#Html.LabelFor(m => Model.Surname)
#Html.TextBoxFor(m => Model.Surname)
</div>
<div>
#Html.LabelFor(m => Model.UserRole.Role)
#Html.TextBoxFor(m => Model.UserRole.Role)
</div>
<input type="submit" value="Zapisz zmiany" />
#Html.HiddenFor(m => Model.UserRole)
#Html.HiddenFor(m => Model.UserRoleID)
#Html.HiddenFor(m => Model.UserID)
</div>
</fieldset>
<div id="Result"></div>
#Html.ValidationSummary(true)
}
and method in MyController like this:
[HttpPost]
public ActionResult SaveUser(User user, UserRole role)
{
//code here
}
but object role is not passed, either user.UserRole.
My User model class:
namespace MVCApp.Models
{
public partial class User
{
public User()
{
this.Factures = new HashSet<Facture>();
this.Settings = new HashSet<Setting>();
this.Companies = new HashSet<Company>();
}
public int UserID { get; set; }
public Nullable<int> UserRoleID { get; set; }
public string Login { get; set; }
public string Password { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public virtual ICollection<Facture> Factures { get; set; }
public virtual ICollection<Setting> Settings { get; set; }
public virtual UserRole UserRole { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
}
And Role model class:
namespace MVCApp.Models
{
public partial class UserRole
{
public UserRole()
{
this.Users = new HashSet<User>();
}
public int UserRoleID { get; set; }
public string Role { get; set; }
public virtual ICollection<User> Users { get; set; }
}
}
so, How can I pass models like this, which has other reference types inside?
The following line in your view make no sense
#Html.HiddenFor(m => Model.UserRole)
UserRole is a complex object, and depending on whether you have overridden its .ToString() method, it will render <input ... value="MVCApp.Models.UserRole" /> so when this posts back the DefaultModelBinder is trying to do model.UserRole = "MVCApp.Models.UserRole" which of course fails and the property is therefore null
Remove it, and instead bind to the properties of UserRole that you want posted back - as you have done with #Html.TextBoxFor(m => Model.UserRole.Role). For example #Html.HiddenFor(m => Model.UserRole.UserRoleID) but you already seem to have bound this with #Html.HiddenFor(m => Model.UserRoleID) so it might not be necessay to repeat it.
you can create your own submitting mechanism. Just use $.ajax and pass in its data property all your values. A bit more js code, but a lot more flexibility.

Passing part of a viewmodel to a controller

I have a Customer index page that takes a CustomerIndexViewModel to populate the page with a list of customers, the time the request took, and many other pieces of information.
I have a CustomerSearchArgsModel inside the CustomerIndexViewModel.
public class CustomerIndexViewModel : BaseIndexViewModel
{
public IEnumerable<Customer> Customers{ get; set; }
public double RequestTime { get; set; }
public CustomerSearchArgsModel CustomerSearchArgsModel { get; set; }
public OtherTypes OtherType {get;set;}
}
public class CustomerSearchArgsModel
{
public string CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
On my Customer Index page I want to have something like -
#model CustomerIndexViewModel
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("Index","Customer",FormMethod.Post, new { id="searchSubmit"}))
{
#Html.LabelFor(model => model.CustomerSearchArgsModel.ConsumerID)
#Html.EditorFor(model => model.CustomerSearchArgsModel.ConsumerID)
#Html.LabelFor(model => model.CustomerSearchArgsModel.LastName)
#Html.EditorFor(model => model.CustomerSearchArgsModel.LastName)
#Html.LabelFor(model => model.CustomerSearchArgsModel.FirstName)
#Html.EditorFor(model => model.CustomerSearchArgsModel.FirstName)
<input type="submit" value="Search" />
}
I want to return the typed in values to the Index (POST) method on the Customer controller in a CustomerSearchArgsModel.
But I don't know how to return a Model that is different from the one defined at the top of the page.
You can put your "searchSubmit" form inside a partial view. Then pass the model.CustomerSearchArgsModel to the partial view. Be sure that model.CustomerSearchArgsModel is not null; otherwise, you will get an exception.
Index Page
#Html.Partial("_search", model.CustomerSearchArgsModel)
_search Partial View
#model CustomerSearchArgsModel
#using (Html.BeginForm("Index","Customer",FormMethod.Post, new { id="searchSubmit"}))
{
#Html.LabelFor(model => model.ConsumerID)
#Html.EditorFor(model => model.ConsumerID)
#Html.LabelFor(model => model.LastName)
#Html.EditorFor(model => model.LastName)
#Html.LabelFor(model => model.FirstName)
#Html.EditorFor(model => model.FirstName)
<input type="submit" value="Search" />
}
The problem with this approach is you will get a 0 value displayed in your textbox for ConsumerID. To solve this problem you can use Html.Action instead of Html.Partial.
Hope this helps.

Model not Binding to Action passing Null as value

I don't know why, the Model is not binding to the Create Action:
ViewModel:
public class AddressContactViewModel
{
// Primary properties
public int Id { get; set; }
public string Contact { get; set; }
// Navigation properties
[Display(ResourceType = typeof(HeelpResources), Name = "AddressContactViewModelNameLabel")]
[Required(ErrorMessageResourceName = "ErrorMsgRequiredField", ErrorMessageResourceType = typeof(HeelpResources))]
public int ContactType_Id { get; set; }
public int Address_Id { get; set; }
// ViewModel dropdownlists
public IEnumerable<SelectListItem> ContactTypeList { get; set; }
}
View:
#model Heelp.ViewModels.AddressContactViewModel
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Address_Id)
<fieldset>
<legend>AddressContactViewModel</legend>
<div id="year">
#Html.DisplayNameFor(m => m.ContactType_Id)
#Html.DropDownListFor(m => m.ContactType_Id, Model.ContactTypeList, HeelpResources.AddressContactViewModelContactTypesListDropDownFirstRecord)
#Html.ValidationMessageFor(m => m.ContactType_Id)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Contact)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Contact)
#Html.ValidationMessageFor(m => m.Contact)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Action Post:
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult ContactCreate(AddressContactViewModel contact)
{
var contactDto = Mapper.Map<AddressContactViewModel, AddressContactDto>(contact);
_addressContactService.Create(contactDto);
return RedirectToAction(MVC.Address.ContactList());
}
EDIT: Get:
public virtual ActionResult ContactCreate(int addressId)
{
var model = new AddressContactViewModel
{
Address_Id = addressId,
ContactTypeList = ContactTypeDropDownList()
};
return View(model);
}
When I submit the Form, I receive "null" in the contact parameter of the ContactCreate action, why is this happening?
Thanks
Unbelievable, I found the problem, the parameter of the action has the same name of a field in the ViewModel, a change of the parameter name and everything works find:
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult ContactCreate(AddressContactViewModel addressContact) // HERE I CHANGE FROM contact TO addressContact AND THE PROBLEM GET SOLVED
{
var contactDto = Mapper.Map<AddressContactViewModel, AddressContactDto>(addressContact);
_addressContactService.Create(contactDto);
return RedirectToAction(MVC.Address.ContactList(addressContact.Address_Id));
}
I ran into this same problem on my last app. try encompassing the button and the field you want to submit into the same div:
#model Heelp.ViewModels.AddressContactViewModel
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Address_Id)
<fieldset>
<legend>AddressContactViewModel</legend>
<div id="year">
#Html.DisplayNameFor(m => m.ContactType_Id)
#Html.DropDownListFor(m => m.ContactType_Id, Model.ContactTypeList, HeelpResources.AddressContactViewModelContactTypesListDropDownFirstRecord)
#Html.ValidationMessageFor(m => m.ContactType_Id)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Contact)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.Contact)
#Html.ValidationMessageFor(m => m.Contact)
<input type="submit" value="Create" />
</div>
</fieldset>
}

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.

Pass a model to a partial view?

this is my partial:
#model RazorSharpBlog.Models.MarkdownTextAreaModel
<div class="wmd-panel">
<div id="wmd-button-bar-#Model.Name"></div>
#Html.TextAreaFor(m => m.Name, new { #id = "wmd-input-" + #Model.Name, #class = "wmd-input" })
</div>
<div class="wmd-panel-separator"></div>
<div id="wmd-preview-#Model.Name" class="wmd-panel wmd-preview"></div>
<div class="wmd-panel-separator"></div>
I'm trying to include it like this in my View:
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.Title)
#Html.TextBoxFor(m => m.Title)
#Html.Partial("MarkdownTextArea", new { Name = "content" })
<input type="submit" value="Post" />
}
these are the model classes:
public class MarkdownTextAreaModel
{
[Required]
public string Name { get; set; }
}
public class BlogContentModel
{
[Required]
[Display(Name = "Post Title")]
public string Title { get; set; }
[Required]
[DataType(DataType.MultilineText)]
[Display(Name = "Post Content")]
public string Content { get; set; }
}
What am I doing wrong, how should I do this in order to make my partial reusable?
Your partial expects an instance of the MarkdownTextAreaModel class. So do so, instead of passing an anonymous object which would throw anyways:
#Html.Partial("MarkdownTextArea", new MarkdownTextAreaModel { Name = "content" })
Now this being said a far better solution would be to adapt your view model, so that it contains a reference to MarkdownTextAreaModel and use editor templates instead of partials in your views, just like so:
public class BlogContentModel
{
[Required]
[Display(Name = "Post Title")]
public string Title { get; set; }
[Required]
[DataType(DataType.MultilineText)]
[Display(Name = "Post Content")]
public string Content { get; set; }
public MarkdownTextAreaModel MarkDown { get; set; }
}
then of course readapt the controller serving this view so that it populates the MarkDown of your view model:
public ActionResult Foo()
{
BlogContentModel model = .... fetch this model from somewhere (a repository?)
model.MarkDown = new MarkdownTextAreaModel
{
Name = "contect"
};
return View(model);
}
and then inside your main view simply:
#using (Html.BeginForm())
{
#Html.LabelFor(m => m.Title)
#Html.TextBoxFor(m => m.Title)
#Html.EditorFor(x => x.MarkDown)
<input type="submit" value="Post" />
}
and then in order to follow standard conventions move your partial to ~/Views/YourControllerName/EditorTemplates/MarkdownTextAreaModel.cshtml and now everything will magically come into place as it should.
#using (Html.BeginForm()) {
#Html.LabelFor(m => m.Title) #Html.TextBoxFor(m => m.Title)
#Html.Partial("MarkdownTextArea", new MarkdownTextAreaModel { Name = "content" })
<input type="submit" value="Post" />
}

Categories

Resources