What is the ASP.NET MVC equivalent of displaying a Label conditionally? - asp.net-mvc

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();`

Related

ASP.NET MVC: handling logic & variables in the _Layout page

In my _layout.cshtml page, I've got some elements that need to be hidden on some pages. I know the pages on which we won't display some parts. For a single page, I could just do this:
#if (ViewContext.RouteData.Values["action"].ToString() != "LogIn") {
<div> .... <div>
}
But that gets messy and long with multiple pages. Someplace, ideally not in the _Layout page, I could build a list of actions, and if the current action is any of them, set a boolean variable (ShowStuff) to false. Then just do this on _Layout:
#if (ShowStuff== true) {
<div> .... <div>
}
I'm just not sure where would be the best-practice way to examine that list of actions and set the boolean. Can the _Layout page have it's own model and controller like a normal view?
Similarly to MikeSW answer, I'd use an action filter, but I would populate ViewData with a specific ViewModel. When you want to display it simply DisplayFor the value, if it's populated the template is used by whatever type the model is, if it's null nothing is displayed. (examples below from memory, may not be exactly correct.)
public BlahModelAttribute : ActionFilterAttribute
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
BlahModel model = Db.GetModel();
filterContext.Controller.ViewData.Set(model);
}
}
ViewData extensions:
public static ViewDataExtensions
{
private static string GetName<T>()
: where T : class
{
return typeof(T).FullName;
}
public static void Set<T>(this ViewDataDictionary viewData, T value)
: where T : class
{
var name = GetName<T>();
viewData[name] = value;
}
public static T Get<T>(this ViewDataDictionary viewData)
: where T : class
{
var name = GetName<T>();
return viewData[name] as T;
}
}
In your view:
#{var blahModel = ViewData.Get<BlahModel>() }
#Html.DisplayFor(m => blahModel)
If devs would stop looking for the 'best way' for every problem they have, that would be great. No best way here, just opinionated solutions. Here's mine: You can create an action filter [ShowNav] and decorate any controller/action you need. That filter will put a boolean into HttpContext.Items . Create then a HtmlHelper which checks for the boolean. Then in _layout, if (Html.CanShowNavig()) { <nav> } . That's the easiest solution that comes to my mind.

MVC Dynamic View Data and Dynamic Views

Traditionally, I have built MVC applications using view models with Data Annotations attributes, and I dynamically render the views using editor templates. Everything works great, and it really cuts down on the time it takes me to build new views. My requirements have recently changed. Now, I can't define the view model at design time. The properties that will be rendered on the view are decided at run time based on business rules. Also, the validation rules for those properties may be decided at run time as well. (A field that is not required in my domain model, may be required in my view based on business rules). Also, the set of properties that will be rendered is not known until run time - User A may edit 6 properties from the model, while user B may edit 9 properties.
I am wondering if it is possible to create a model metadata provider that will supply my own metadata from business rules for an untyped view model like a collection of property names and values. Has anyone solved this problem?
I solved a similar problem by creating a more complex model, and using a custom editor template to make the model be rendered to look like a typical editor, but using the dynamic field information:
public class SingleRowFieldAnswerForm
{
/// <summary>
/// The fields answers to display.
/// This is a collection because we ask the MVC to bind parameters to it,
/// and it could cause issues if the underlying objects were being recreated
/// each time it got iterated over.
/// </summary>
public ICollection<IFieldAnswerModel> FieldAnswers { get; set; }
}
public interface IFieldAnswerModel
{
int FieldId { get; set; }
string FieldTitle { get; set; }
bool DisplayAsInput { get; }
bool IsRequired { get; }
bool HideSurroundingHtml { get; }
}
// sample implementation of IFieldAnswerModel
public class TextAreaFieldAnswer : FieldAnswerModelBase<TextAreaDisplayerOptions>
{
public string Answer { get; set; }
}
EditorTemplates/SingleRowFieldAnswerForm.cshtml:
#helper DisplayerOrEditor(IFieldAnswerModel answer)
{
var templateName = "FieldAnswers/" + answer.GetType().Name;
var htmlFieldName = string.Format("Answers[{0}]", answer.FieldId);
if (answer.DisplayAsInput)
{
#Html.EditorFor(m => answer, templateName, htmlFieldName)
// This will display validation messages that apply to the entire answer.
// This typically means that the input got past client-side validation and
// was caught on the server instead.
// Each answer's view must also produce a validation message for
// its individual properties if you want client-side validation to be
// enabled.
#Html.ValidationMessage(htmlFieldName)
}
else
{
#Html.DisplayFor(m => answer, templateName, htmlFieldName)
}
}
<div class="form-section">
<table class="form-table">
<tbody>
#{
foreach (var answer in Model.FieldAnswers)
{
if (answer.HideSurroundingHtml)
{
#DisplayerOrEditor(answer)
}
else
{
var labelClass = answer.IsRequired ? "form-label required" : "form-label";
<tr>
<td class="#labelClass">
#answer.FieldTitle:
</td>
<td class="form-field">
<div>
#DisplayerOrEditor(answer)
</div>
</td>
</tr>
}
}
}
</tbody>
</table>
</div>
So I populate my SingleRowFieldAnswerForm with a series of answer models. Each answer model type has its own editor template, allowing me to customize how different types of dynamic "properties" should be displayed. For example:
// EditorTemplates/FieldAnswers/TextAreaFieldAnswer.cshtml
#model TextAreaFieldAnswer
#{
var htmlAttributes = Html.GetUnobtrusiveValidationAttributes("Answer", ViewData.ModelMetadata);
// add custom classes that you want to apply to your inputs.
htmlAttributes.Add("class", "multi-line input-field");
}
#Html.TextAreaFor(m => m.Answer, Model.Options.Rows, 0, htmlAttributes)
#Html.ValidationMessage("Answer")
The next tricky part is that when you send this information to the server, it doesn't inherently know which type of IFieldAnswerModel to construct, so you can't just bind the SingleRowAnswerForm in your arguments list. Instead, you have to do something like this:
public ActionResult SaveForm(int formId)
{
SingleRowAnswerForm form = GetForm(formId);
foreach (var fieldAnswerModel in form.FieldAnswers.Where(a => a.DisplayAsInput))
{
// Updating this as a dynamic makes sure all the properties are bound regardless
// of the runtime type (since UpdateModel relies on the generic type normally).
this.TryUpdateModel((dynamic) fieldAnswerModel,
string.Format("Answers[{1}]", fieldAnswerModel.FieldId));
}
...
Since you provided MVC with each dynamic "property" value to bind to, it can bind each of the properties on each answer type without any difficulty.
Obviously I've omitted a lot of details, like how to produce the answer models in the first place, but hopefully this puts you on the right track.
You can use The ViewData Property in your ViewModel, View and Controller, it is dynamic, so it can be resolved at runtime.

How to dynamically display a list of both name and value of a String/boolean pair on MVC in ASP.NET MVC

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...

ASP.NET MVC - strongly typed view with partial views (view and partial views should also have access to some global data)

Consider the following scenario:
Action Edit() is forwarded to Edit.aspx view to render the view.
Edit.aspx consists of textbox1 and two partial views (aka view user controls):
part1.ascx (which has textbox2, textbox3)
and part2.ascx (which has checkbox1 and checkbox2)
You want to have a strongly typed view for Edit.aspx, say, you use EditViewData class.
You also need Edit.aspx, part1.ascx and part2.ascx have access to some global information such as currentUserID, currentUserLanguage, currentUserTimezone.
Questions:
How do you go about structuring the EditViewData class?
How do you pass the view data to the view and partial views so that the object gets populated automatically when you submit the form and return to the Edit() http.post action?
What do you pass to the Edit() http.post action?
Your viewdata should look like this:
public class EditViewData
{
public int currentUserID { get; set; }
public string currentUserLanguage { get; set; }
public string currentUserTimezone { get; set; }
// ... other stuff
}
After you strongly type your aspx, you also need to strongly type your ascxs. Then in your aspx, when you call RenderPartial, just call like usual:
<% using (Html.BeginForm()) %>
<% Html.RenderPartial("part1.ascx" ); %>
<% Html.RenderPartial("part2.ascx" ); %>
<%}%>
It should automatically inherit the Model in the control. Just remember that your BeginForm should be surrounding both of your controls (ascxs).

asp.net mvc radio button state

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

Resources