I have a strongly typed view of type ProductListingViewModel which in turn contains a ProductViewModel. (both custom view models).
I have some form elements on my page and these are created like so:
<%: Html.DropDownListFor(m => m.ProductViewModel.CategoryId, Model.Categories)%>
which generates the HTML:
<select name="ProductViewModel.CategoryId" id="CategoryId">
With the default model binding I expected that when I post to my controller action which accepts a parameter of type ProductListingViewModel, that it'd know to populate the ProductViewModel.CategoryId with the relevant data.
The name of the select list seems to indicate that it knows there's a ProductViewModel with a CategoryId property however when I post to my controller method, the ProductViewModel is null. If I create this during construction of the ProductListingViewModel then it's no longer null but the default binder doesn't seem to be populating the properties as I expected.
Is this a case for a custom model binder or am I just missing something fundamental?
Cheers.
Let me try to summarize (correct me if I am wrong).
Model:
public class ProductListingViewModel
{
public ProductViewModel ProductViewModel { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
public class ProductViewModel
{
public string CategoryId { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new ProductListingViewModel
{
Categories = new SelectList(new[]
{
new { Value = "1", Text = "category 1" },
new { Value = "2", Text = "category 2" }
}, "Value", "Text")
};
return View(model);
}
[HttpPost]
public ActionResult Index(ProductListingViewModel model)
{
return View(model);
}
}
View:
<% using (Html.BeginForm()) { %>
<%: Html.DropDownListFor(m => m.ProductViewModel.CategoryId, Model.Categories)%>
<input type="submit" value="OK" />
<% } %>
Now when you submit the form you will get:
model.ProductViewModel.CategoryId = the id that was selected in the drop down list
Isn't what you are after?
It seems to me that the default binder should work in this case.
Did you try using Fiddler for checking the data sent from the client?
What exactly is the signature of the controller action?
Related
I have an a href link to a page which adds a parameter to the link for example:
tsw/register-your-interest?Course=979
What I am trying to do is to extract the value in Course i.e 979 and display it in the view. When attempting with the below code, I only return with 0 rather than the course value expected. ideally I'd like to avoid using routes.
Here is the view:
<div class="contact" data-component="components/checkout">
#using (Html.BeginUmbracoForm<CourseEnquiryPageSurfaceController>("PostCourseEnquiryForm", FormMethod.Post, new { id = "checkout__form" }))
{
//#Html.ValidationSummary(false)
#Model.Course;
}
And my controller:
public ActionResult CourseEnquiry(string Course)
{
var model = Mapper.Map<CourseEnquiryVM>(CurrentContent);
model.Course = Request.QueryString["Course"];
return model
}
This is the View Model:
public class CourseEnquiryVM : PageContentVM
{
public List<OfficeLocation> OfficeLocations { get; set; }
public string Test { get; set; }
public string Course { get; set; }
public List<Source> SourceTypes { get; set; }
}
SOLUTION:
After some research and comments I've adjusted the code to the below which now retrieves the value as expected
#Html.HiddenFor(m => m.Course, new { Value = #HttpContext.Current.Request.QueryString["Course"]});
Thanks all
Based on the form code you provided you need to use #Html.HiddenFor(m => m.Course) instead of just #Model.Course. #Model.Course just displays the value as text instead of building a input element that will be sent back to your controller.
If your problem is with a link prior to the view you referenced above, here's what I'd expect to work:
View with link:
#model CourseEnquiryVM
#Html.ActionLink("MyLink","CourseEnquiry","CourseController", new {course = #Model.Course}, null)
CourseController:
public ActionResult CourseEnquiry(string course)
{
// course should have a value at this point
}
In your view, you are only displaying the value of Course.. which isn't able to be submitted. You need to incorporate the value of course with a form input element (textbox, checkbox, textarea, hidden, etc.).
I would highly suggest using EditorFor or Textboxfor, but because your controller action is expecting just a string parameter you could just use Editor or TextBox.
#using (Html.BeginUmbracoForm<CourseEnquiryPageSurfaceController>("PostCourseEnquiryForm", FormMethod.Post, new { id = "checkout__form" }))
{
//#Html.ValidationSummary(false)
#Html.TextBox(Model.Course, null, new { #class = "form-control"});
<input type="submit" value="Submit" />
}
Then you should just be able to do this in your controller:
public ActionResult CourseEnquiry(string course) // parameter variables are camel-case
{
var model = Mapper.Map<CourseEnquiryVM>(CurrentContent);
if(!string.IsNullOrWhiteSpace(course))
model.Course = course;
return model;
}
Let me know if this helps.
I am using ASP.NET MVC5 in my application.
I want to show languages known by an employee using check boxes in my view(check boxes with same name). For this how to write my model, pass them from the controller and display them in the view?
I have those vales stored in an Enum
public enum Language {
English=1,
Telugu=2,
Hindi=3,
Spanish=4
}
It is ok if I have to store them in a table in DB.
You can use the CheckBoxListFor helper:
#Html.CheckBoxListFor(model => model.SelectedOptions, Model.AllOptions)
And your model would look like this:
public class MyModel {
// This property contains the available options
public SelectList AllOptions { get; set; }
// This property contains the selected options
public IEnumerable<string> SelectedOptions { get; set; }
public MyModel() {
AllOptions = new SelectList(
new[] { "Option1", "Option2", "Option3" });
SelectedOptions = new[] { "Option1" };
}
}
In controller you just simply pass your model to the View:
[HttpGet]
[ActionName("Index")]
public ActionResult Index()
{
var model = new MyModel();
return View(model);
}
You can change the AllOptions and SelectedOptions properties as you want (just remove the code from the constructor of MyModel and place it in your controller class).
For more details check this out, there is a note about how to work with Enum: CheckBoxList for Enum types MVC Razor.
My Controller populates my Model with a list with strings that appear in a DropDownList in my View. When the view is posted back to my Controller, that list is suddenly null. Why is it null, and what happened to the list of strings I created?
The list was properly populated and shows up in the View. The remainder of the form elements DO properly post back. For example, selectedName has whatever name the user clicked on. The only thing that is not posting back is nameList.
Here is the relevant part of my model,
public class MyModel
{
[Display(Name = "Selected")]
public string selectedName{ get; set; }
[Display(Name = "Names")]
public List<string> nameList{ get; set; }
}
the relevant Get and Post parts of my Controller,
public class MyController: Controller
{
[HttpGet]
public ActionResult Index()
{
List<string> nameList= getNames();
MyModel model = new MyModel()
model.nameList= nameList;
// Now, model.nameList has a bunch of stuff in it
return View(model);
}
[HttpPost]
public ActionResult Index(MyModel model)
{
if(model.nameList== null)
{
cry();
postOnStackOverflow();
}
return View(model);
}
}
and the relevant part of my View (which is encapsulated inside of a form).
<p>
#Html.LabelFor(c => c.nameList):
#Html.DropDownListFor(c => c.selectedName, new SelectList(Model.nameList), new { onchange = "this.form.submit();" })
</p>
Only the value of the drop down list is posted when you post the form. I assume that your control in question is on a form.
I am not sure why you want to always return to the view you posted from, but you need to repopulate the list:
[HttpPost]
public ActionResult Index(MyModel model)
{
List<string> names = getNames();
model.nameList = names;
return View(model);
}
That is the expected behaviour considering what you have in your view. You need to reload the namelist collection property incase you are returning model to the same view again.
[HttpPost]
public ActionResult Index(MyModel model)
{
if(ModelState.IsValid)
{
// Save and redirect
}
//reload the collection again and return the model to the view
model.nameList=getNames();
return View(model);
}
My Model;
namespace GeneralUtility.Models
{
public class MyModel
{
public int BirthDateYear { get; set; }
public String Details { get; set; }
}
}
My Controller
namespace GeneralUtility.Controllers
{
public class WorkspaceController : Controller
{
public ActionResult MyHelper(MyModel model)
{
if(model.someCondition)
model.Details= "TEST";
else
model.Details= "Some other TEST";
return View(model);
}
}
}
My View
<div data-role="fieldcontain">
#using (Html.BeginForm("MyHelper", "WorkSpace", FormMethod.Post, new { id = "frmMyForm" }))
{
...
<div data-role="fieldcontain">
#Html.EditorFor(x => x.Details )
</div>
...
}
</div>
After I submit my form, I can see the model and any changes I make in MyHelper action method. However, when I make changes and return the model from the controller (I can see the changes in the model while in debug). I get the same value as before in #Html.EditorFor(x => x.Details ) field. What can I do to get the changed Details value of the model to show?
That's because by default ASP.NET MVC returns the model that was passed into a method (using what is stored in the ModelState, if it exists). If it doesn't exist, it uses what you pass to the View.
In order to prevent this, you need to call ModelState.Clear() before returning your view.
Check this blog post for a more detailed explanation.
I want to add a dropdownlist in my form, with 2 values userid and username in my dropdownlist, and also I want to get the value selected by the user when I click the button. I'm new to MVC and so far, I have not worked on dropdownlist, tried few samples but nothing seems to be working the way I want.
I'll jump lots of MVC3 concepts. If you're really new to ASP.NET MVC, you should take a look at some tutorials.
This code should help you:
VIEW
#using (Html.BeginForm("ACTION NAME", "CONTROLLER NAME"))
{
<select name="select">
<option value="username" selected>User name</option>
<option value="userid">User id</option>
</select>
<input type="submit" />
}
ACTION
[HttpPost]
public ActionResult ACTIONNAME(string select)
{
//...
}
Please, note:
ACTION NAME and CONTROLLER NAME at the BeginForm helper. You will have to modify this at your code
The select name ("select") and the name of the argument at the action ("select"). This is not a coincidence, it's a convention. MVC uses the name attr to bind data
The selected attribute at the option will make it the default option
Regards
See one of the ways you can do it is send the list in a model property as the binding and for the value you can bind it to another property like :
public class YourModel
{
public List<UserList> OptionList { get; set; }
public String YourValue{get;set;}
}
public class UserList
{
public String UserName{get;set;}
public String UserId{get;set;}
}
#Html.DropDownListFor(model => model.YourValue, Model.OptionList, "")
In the helper there are overided options which are used to specify the value and text.
And Remember :
This is StackOverflow.
Even the Not working example which you have tried are important for the ones who try to help you since they are spending their precious bandwidths for u.
You don't need create a new model class for each view, just put this on controller:
ViewBag.FieldName = new SelectList(new List<SelectListItem>() {
new SelectListItem { Value = "userid", Text = "User ID" },
new SelectListItem { Value = "username", Text = "User name" }
});
And this on view:
#Html.DropDownList("FieldName")
You need to create a collection of SelectListItem like:
IEnumerable<SelectListItem> selectList =
from c in areaListResponse.Item
select new SelectListItem
{
Text = c.AreaName,
Value = c.Id.ToString()
};
Pass this selectList to your view:
return View(selectList);
In your cshtml:
#model IEnumerable<SelectListItem>
#Html.DropDownListFor(m => m.RequestAreaName, Model)
If you need complecated object, you may need a wrapper class like:
public class RaiseRequestModelWrapper
{
public IEnumerable<SelectListItem> GetModel { get; set; }
public RaiseRequestModel PostModel { get; set; }
}