I'm trying out asp.net mvc for a new project, and I ran across something odd. When I use the MVC UI helpers for textboxes, the values get persisted between calls. But, when I use a series of radio buttons, the checked state doesn't get persisted.
Here's an example from my view.
<li>
<%=Html.RadioButton("providerType","1")%><label>Hospital</label>
<%=Html.RadioButton("providerType","2")%><label>Facility</label>
<%=Html.RadioButton("providerType","3")%><label>Physician</label>
</li>
When the form gets posted back, I build up an object with "ProviderType" as one of it's properties. The value on the object is getting set, and then I RedirectToAction with the provider as a argument. All is well, and I end up at a URL like "http://localhost/Provider/List?ProviderType=1" with ProviderType showing. The value gets persisted to the URL, but the UI helper isn't picking up the checked state.
I'm having this problem with listbox, dropdownlist, and radiobutton. Textboxes pick up the values just fine. Do you see something I'm doing wrong? I'm assuming that the helpers will do this for me, but maybe I'll just have to take care of this on my own. I'm just feeling my way through this, so your input is appreciated.
Edit: I just found the override for the SelectList constructor that takes a selected value. That took care of my dropdown issue I mentioned above.
Edit #2: I found something that works, but it pains me to do it this way. I feel like this should be inferred.
<li>
<%=Html.RadioButton("ProviderType","1",Request["ProviderType"]=="1")%><label>Hospital</label>
<%=Html.RadioButton("ProviderType", "2", Request["ProviderType"] == "2")%><label>Facility</label>
<%=Html.RadioButton("ProviderType", "3", Request["ProviderType"] == "3")%><label>Physician</label>
</li>
Hopefully someone will come up with another way.
If you give the radio buttons the same name as the property on your model, then MVC will automatically set the checked attribute on the appropriate button.
I think this relies on having a strongly typed Model.
What you need is something like this in your view:
<% foreach(var provider in (IEnumerable<Provider>)ViewData["Providers"]) { %>
<%=Html.RadioButton("ProviderType", provider.ID.ToString(), provider.IsSelected)%><label><%=provider.Name%></label>
<% } %>
And then in your controller have this:
var providers = GetProviders();
int selectedId = (int) Request["ProviderType"]; // TODO: Use Int32.TryParse() instead
foreach(var p in providers)
{
if (p.ID == selectedId)
{
p.IsSelected = true;
break;
}
}
ViewData["Providers"] = providers;
return View();
The Provider class will be something like this:
public class Provider
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
The form shouldn't be posting to the querystring, unless you forgot to specify the form as method="POST". How are you specifying the form? Are you using ASP.NET MVC Beta?
I'm using vs2010 now, it works like:
<%=Html.RadioButton("ProviderType","1",Model.ProviderType==1)%><label>Hospital</label>
looks better?
Well logically it would not persist, there is no session state. Think of it as an entirely new page. In order to get your radio buttons to populate you need to persist back something like ViewData["ProviderType"] = 3 to have the radiobutton repopulate with its data.
I've made this HTML Helper extension:
<Extension()> _
Public Function RadioButtonList(ByVal helper As HtmlHelper, ByVal name As String, ByVal Items As IEnumerable(Of String)) As String
Dim selectList = New SelectList(Items)
Return helper.RadioButtonList(name, selectList)
End Function
<Extension()> _
Public Function RadioButtonList(ByVal helper As HtmlHelper, ByVal Name As String, ByVal Items As IEnumerable(Of SelectListItem)) As String
Dim sb As New StringBuilder
sb.Append("<table class=""radiobuttonlist"">")
For Each item In Items
sb.AppendFormat("<tr><td><input id=""{0}_{1}"" name=""{0}"" type=""radio"" value=""{1}"" {2} /><label for=""{0}_{1}"" id=""{0}_{1}_Label"">{3}</label></td><tr>", Name, item.Value, If(item.Selected, "selected", ""), item.Text)
Next
sb.Append("</table>")
Return sb.ToString()
End Function
Then in the view:
<%= Html.RadioButtonList("ProviderType", Model.ProviderTypeSelectList) %>
In the controller the option is mapped automagically using the standard:
UpdateModel(Provider)
Works like a charm. If you are tablephobic, change the markup generated.
View:
<%=Html.RadioButton("providerType","1")%><label>Hospital</label>
<%=Html.RadioButton("providerType","2")%><label>Facility</label>
<%=Html.RadioButton("providerType","3")%><label>Physician</label>
Controller:
public ActionResult GetType(FormCollection collection)
{
string type=collection.Get("providerType");
if(type=="1")
//code
else if(type=="2")
//code
else
//code
return View();
}
Related
I need to dynamically display a list of both name and value of string/boolean pair on MVC view (*.cshtml) based on user selection. Specifically, both name and value of a string and boolean pair are different in each list. There are more one list that user can select. For example:
FruitName: Apple (string:string)
IsRipen: true (string:boolean)
BookName: C#
IsSold: false
One list type is defined as one report type. A list can be retrieved from report programmatically.
Possible Solution 1
Since the data type of name and value in the list are fixed (string, boolean), one idea is to build a collection as a MVC model, and pass that model to MVC razor view. The question is that how to display the name on the view,
#Html.LabelFor(model => model.Names[0]) //how to display it as 'Fruit Name'
Possible Solution 2
In ASP.NET web form, there is user control whihch can be loaded dynamically. There is partial view in ASP.NET MVC. Can the partial view do what I want? Or is there better solution.
I am new to MVC, any ideal or example would be very much appreicated.
If I understand you correctly, what you want to do is create a Partial View and call it using an action in your controller.
First, do something like this in your controller
// partial
public ActionResult report(string reportName)
{
return View(reportModel.Name);
}
Then, make sure there is a partial view that shares the name of your report.
You can then call the partial view like this:
#{Html.RenderAction("report", "Home", new{ reportName="report" });}
The line above will render the partial view Report.cshtml into the parent view or master layout.
edit
Ok. so every report has a list of name value pairs right?
Assuming that, we can write an action that passes that list to your view.
public ActionResult DisplayPairs()
{
NameValueCollection pairs = new System.Collections.Specialized.NameValueCollection();
pairs.Add("Name", "Value");
pairs.Add("Name2", "Value2");
pairs.Add("Name3", "Value3");
pairs.Add("Name4", "Value4");
return View(pairs);
}
Then we have the DisplayPairs View:
#model System.Collections.Specialized.NameValueCollection
#{
ViewBag.Title = "DisplayPairs";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>DisplayPairs</h2>
<table>
#foreach(string key in Model.AllKeys){
<tr><th>#key</th><td>#Model[key]</td></tr>
}
</table>
Which displays:
Name Value
Name2 Value2
Name3 Value3
Name4 Value4
I hope this helps
Why don't you just create a wrapper class that contains all the data you need?
public class ReportViewModel
{
IEnumerable<KeyValuePair<object, object>> Items { get; set; }
public ReportViewModel()
{ Items = new List<KeyValuePair<object, object>>() }
}
You can then create your model like so:
var model = new ReportViewModel();
model.Items.Add("BookName", "C#");
model.Items.Add("IsSold", false);
return View(model);
In your view, you just iterate over the KeyValuePairs, and print the key and value:
<ul>
#foreach(var kvp in Model.Items)
{
<li>#kvp.Key: #kvp.Value</li>
}
</ul>
(Excuse me if my razor syntax is buggy - I've not worked very much with it as of yet...)
Also, you might have to add calls to ToSting() if you have odd types of objects in your list. I think the framework does that for you if it needs to, but I'm not sure...
I'm not sure what I am doing wrong here, not even sure if I am on the right track. I have a view model and I create a drop down list from it. Here is my view model:
public class ApplicationViewModel
{
public Application Application { get; private set; }
public SelectList AccountTypes { get; private set; }
public ApplicationViewModel(Application application, IEnumerable<AccountType> accountTypes)
{
Application = application;
AccountTypes = new SelectList(accountTypes, "AccountTypeID", "AccountTypeName", application.AccountTypeID);
}
}
Here is my Create (get) action:
public ActionResult Create()
{
var viewModel = new ApplicationViewModel(new Application(), db.AccountTypes);
return View(viewModel);
}
And my view code:
<%: Html.DropDownListFor(???, Model.AccountTypes, "-- Select --") %>
<%: Html.ValidationMessageFor(???) %>
In the code above, I'm not exactly sure what must come in ??? The initial value is "-- Select --". If the user clicks on the submit button and the dropdown's value is still "-- Select --" then it must display a message.
I am also using EF4. Please can someone advise as to what to do. Code samples would be appreciated.
Thanks.
If your View is strongly typed ie:
Inherits="System.Web.Mvc.ViewUserControl<Model.NameSpace.ApplicationViewModel>"
Then the ??? in your view code should be a lambda expression referring to the items in your ViewModel. (I assume your ViewModel's Application object has a property that is going to be assigned a value based on the drop down list?)
I've assumed your application object has an AccountType property, For example:
??? should be something like:
<%= Html.DropDownListFor(x => x.Application.AccountType, Model.AccountTypes) %>
The value from the drop down list will populate the AccountType property on your Application model and will be populated with the AccountTypes from your ViewModel.
Hope this helps.
-- EDIT --
On your Application model, use the namespace:
using System.ComponentModel.DataAnnotations;
Above your AccountTypes property, add
[Required(ErrorMessage="Account Type Required")]
I think this should work.
I am trying to work with an HTML.DropDownList in MVC and am not getting the expected return values. Here is my implementation for the selectList to bind to the drop down -
IEnumerable<status> stat = _provider.GetAllStatuses();
Statuses = new SelectList(stat.ToList(), "id", "name", i.status.id);
And here is my view -
<%= Html.DropDownList("Status",Model.Statuses) %>
I am getting an error when trying to run updatemodel in my controller. I then tried to individually set each object. It turns out that I am not getting a single int from the formvalue as I would expect to. Instead, I am getting a value like "5,10,2,3". I think this is coming from how I set up my selectlist, but I'm not exactly sure. Can anyone see an error in the way I am setting up this dd?
Thanks for any help, and let me know if I can clarify anything.
What does the signature of the post method look like? It (or the model) should have a Status property that's defined as an int. I suspect that you've got more code than you're showing us that is listing all the potential statuses on the page (hidden fields?) and that's what you are seeing posted back as an array of ints.
It should look something like:
public ActionResult PostAction( int status, .... )
{
... status will contain the selected value from the dropdown ...
}
This is how I am doing it:
var stat = _provider.GetAllStatuses();
myViewDataObject.Statuses = new SelectList(stat, "id", "name", i.status.id);
stat is an IEnumerable. Statuses is of type SelectList. You don't need ToList() if you are returning an IEnumerable or IQueryable from your provider.
My view inherits from
System.Web.Mvc.Viewpage<MyProject.Models.MyViewDataClass>
which looks like this:
class MyViewDataClass
{
public int StatusID { get; set; }
public SelectList Statuses { get; set; }
}
In the controller, I am accepting a FormsCollection object, and using the model binder to update it:
public ActionResult Edit(FormCollection collection)
{
var myViewDataObject = new MyViewDataClass();
UpdateModel(myViewDataObject);
}
More info at http://nerddinnerbook.s3.amazonaws.com/Part6.htm
I'm currently porting an ASP.NET WebForms application to ASP.NET MVC.
In one of the pages there is an ASP.NET Label control which is displayed conditionally based on a variable in the codebehind. So, something to the effect of
<asp:Label runat="server" Visible="<%# ShowLabel%>">
...
</asp:Label>
Where ShowLabel is a Boolean value in the codebehind. The contents of the label are generated at runtime and will be different pretty much every time.
There's better ways to do this even in ASP.NET, but what would be the best way to do this in ASP.NET MVC? How are you even supposed to render dynamic text in ASP.NET MVC in a way similar to how the ASP.NET Label object worked?
I believe in the Thunderdome principle of having one ViewModel class for each View (unless it is a very simple view).
So I would have a ViewModel class like the following:
public class IndexViewModel
{
public bool labelIsVisible { get; set; }
public String labelText { get; set; }
public IndexViewModel(bool labelIsVisible, String labelText)
{
this.labelIsVisible = labelIsVisible;
this.labelText = labelText;
}
}
In your controller, do something like,
public ActionResult Index()
{
// Set label to be visible in the ViewModel instance
IndexViewModel viewData = new IndexViewData(true, "Simucal rocks!");
return View(viewData);
}
Where Index is a strongly typed view of type IndexViewModel.
Then, in your view simply do something like:
<% if (Model.labelIsVisible) { %>
<%= Model.labelText %>
<% } %>
The main idea in MVC is NOT to pass the strings you want to display; you should pass the relevant objects to your View, and the View, in turn, would decide wether to display that label or not (and this is using a simple if, like in Simucal's sample).
So, instead of doing
if (Model.labelIsVisible) {
One would do
if (Model.Comments == 0) {
For example, if the label would be to show a prompt for a user to comment on an article.
Take your element in and set on hide() function like that:
<div id="label">
#Html.Label("myLabel", "text")
</div>
$("#label").hide();`
As we all know RenderAction() is either part of:
Asp.net MVC 1 Futures or
Asp.net MVC 2 Beta 2
and allows us to render action result inside another view or partial view.
Description
You have a view with multiple partial views displayed using RenderAction() helper.
At least two partial views render a <form> probably by using Html.BeginForm() that postback to original view.
Postback is not performed via Ajax call
Postback data is validated upon form POST.
Problem
When one of the forms gets posted back the other one renders as invalid.
Has anyone used this pattern and solved it? We should somehow know which form performed postback and only that one should validate its POST data. Others should either ignore validation or perform regular HttpVerb.Get action processing.
Have a hidden field in the form to indicate which one. Or, have a prefix passed to the partial and appended to each element in the form.
About prefixes. First way is to have two properties, calling Html.RenderPartial("partial", Model.Data1/2.WithPrefix("data1")).
public class FormModel
{
public string Prefix { get; set; }
}
public class FormData
{
public FormModel Data1 { get; set; }
public FormModel Data2 { get; set; }
}
public ActionResult HandlePost(FormData data)
{
if (data.Data1 != null) {} else {}
}
Second way is the same but use two action parameters.
public ActionResult HandlePost(FormModel data1, FormModel data2)
{
if (data1 != null) {} else {}
}
In the partial view you do
<%= Html.TextBox(Model.Prefix + ".FormModelField", Model.FormModelField) %>
that is, you set field name with the prefix passed in the model.
Of course you may vary this in details.