I am trying to convert a webform usercontrol to MVC based usercontrol.
MessageControl.ascx
public abstract class Feedback : System.Web.UI.UserControl
{
public string Message {get;set;}
}
public void ShowError()
{
lblMessage.InnerHtml = Message;
}
So In aspx pages I can call the public method ShowError and pass the error which will be displayed in the usercontrol ascx file.
Aspx pages
protected void Page_Load(object sender, System.EventArgs e)
{
if(isError = true)
{
MessageControl1.Message = "Error Occured";
}
}
How can I achieve the same in MVC 4.0 ?
Any help would be appreciated.
The correct way would be to use the model state for strongly typed models. Another more manual way which I regularly use is to put error messages into Viewbag and then on the view check if the entry in the Viewbag exists and if it does display the error. for example (in asp.net mvc):
<div id="notification_error" class="invalid" <%if (String.IsNullOrEmpty(""+TempData[ "ErrorMsg" ])){ %>style="display: none;"
<%} %>>
<%= TempData["ErrorMsg"] %>
</div>
Related
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.
Previous to MVC, when webforms was the only thing we had, I used to define a container for my controls (A Box you may call) and used this web control (with title, body and a couple of other properties) in other controls.
So if I wanted to change the look of all my visual controls, I just had to make one change in my Box control.
Here's a simplified snippet of what I used to do:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Box.ascx.cs" Inherits="WebApp.Controls.Box" %>
<span class="boxtitle">
<asp:Literal ID="ltrTitle" runat="server"></asp:Literal>
</span>
<div class="boxContent" id="dvBox">
<asp:PlaceHolder runat="server" ID="BoxBodyContainer"/>
</div>
And in code-behind:
public Control BoxBody { get; set; }
public string Title
{
get { return this.ltrTitle.Text; }
set { this.ltrTitle.Text = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
this.BoxBodyContainer.Controls.Add(this.BoxBody);
}
So I could use this along with other controls in my page as follows:
<MyControls:Box ID="Box1" Title="Foo" runat="server">
<boxbody>
<MyControls:CustomControl ID="Bar" runat="server" >
</MyControls:CustomControl>
</boxbody>
</MyControls:Box>
In order to have something alike in MVC 3, I think I should define a Box view and wherever I need the box, use Html.Partial or Html.Action and pass all my data (title, boxbody, etc) to this view and use #Html.Raw in Box view.
But I was wondering if there's any better, more standard way to achieve this.
What's the correct way of achieving this in MVC 3 (Razor)?
You can create an Html Helper extension method to do this. The best tutorial I could find was for MVC2, but it gives you the basics.
What the tutorial doesn't cover is that there are classes in the System.Web.MVC namespace that can help you create html in code.
Note: The following code hasn't been tested.
using System;
using System.Web.Mvc;
public static class HtmlExtensions
{
public static MvcHtmlString Box(this HtmlHelper helper, string title, MvcHtmlString body)
{
TagBuilder span = new TagBuilder("span");
span.AddCssClass("boxtitle");
span.SetInnerText(title.ToString());
TagBuilder div = new TabBuilder("div");
div.AddCssClass("boxContent");
div.InnerHtml = body;
return MvcHtmlString.Create(span.ToString() + div.ToString());
}
}
You can call this method from the razor view like so:
#Html.Box("foo", Html.Partial("bar"))
When I display a session value in a master page (<%: Session["companyname"].ToString() %>) the following information is displayed on the page { CompanyName = TestCompany}. How do I get just the value?
Thanks for your help!
If you can show the code where the value is stored in the session, it's more likely that I could help. I would suggest, though, that you might want to reconsider using the value from the session directly in your view. It would be better, in my opinion, to have a base view model that all of your view models derive from that has the CompanyName property, and any other common properties required by your master page, on it. Your master page, then, could be strongly-typed to the base view model and you could use the values from the model. I've used this pattern with good success on a couple of projects. Couple it with a base controller where the common properties are populated for view results in OnActionExecuted(), it can be very effective in both reducing code duplication and the use of magic strings in your views.
Model:
public abstract class CommonViewModel
{
public string CompanyName { get; set; }
public string UserDisplayName { get; set; }
...
}
Controller:
public abstract class BaseController
{
public override void OnActionExecuted( ActionExecutedContext filterContext )
{
if (filterContext.Result is ViewResult)
{
var model = filterContext.ViewData.Model as CommonViewModel;
if (model != null)
{
model.CompanyName = Session["CompanyName"] as string;
model.UserDisplayName = Session["UserDisplayName"] as string;
}
}
}
}
Master Page:
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<Foo.CommonViewModel>" %>
<!-- ... -->
<div>
<%: Model.CompanyName %>
</div>
<!-- ... -->
<div>
<%: Model.UserDisplayName %>
</div>
Coming from the asp.net background, I really appreciated the concept of 'validationGroup' when adding validation to a page. I've been searching for a corresponding concept within mvc.net and haven't had much luck.
Is this concept available in mvc.net? If not, what alternatives do I have?
Unfortunately no, it doesn't come with anything like that.
I blogged about a workaround a wee while back.
ASP.NET MVC - Validation Summary with 2 Forms & 1 View
The jist of the blog post:
namespace System.Web.Mvc
{
public static class HtmlExtensions
{
public static string ActionValidationSummary(this HtmlHelper html, string action)
{
string currentAction = html.ViewContext.RouteData.Values["action"].ToString();
if (currentAction.ToLower() == action.ToLower())
return html.ValidationSummary();
return string.Empty;
}
}
}
And
<h2>Register</h2>
<%= Html.ActionValidationSummary("Register") %>
<form method="post" id="register-form" action="<%= Html.AttributeEncode(Url.Action("Register")) %>">
... blah ...
</form>
<h2>User Login</h2>
<%= Html.ActionValidationSummary("LogIn") %>
<form method="post" id="login-form" action="<%= Html.AttributeEncode(Url.Action("LogIn")) %>">
... blah ...
</form>
HTHs,
Charles
Expanding on Charlino's answer, and including HtmlAttributes and other ValidationSummary properties:
public static MvcHtmlString ActionValidationSummary(this HtmlHelper html, string action, bool excludePropertyErrors, string message, object htmlAttributes = null)
{
var currentAction = html.ViewContext.RouteData.Values["action"].ToString();
if (currentAction.ToLower() == action.ToLower())
{
return html.ValidationSummary(excludePropertyErrors, message, htmlAttributes);
}
return new MvcHtmlString(string.Empty);
}
Charles's method was the only approach I could find that actually worked for my purposes!
(I.e. two forms on one MVC page -> without doing forms inside partials and ajax loads for the partials. This was no good for me, as I wanted to return differing result sets to be rendered outside the form div, depending on which form was submitted)
I would advise a slight modification to the Html Extension though, because you still want a validation summary to be rendered for the non-matched validation summary so that client side validation works:
namespace System.Web.Mvc
{
public static class HtmlExtensions
{
public static MvcHtmlString ActionValidationSummary(this HtmlHelper html, string action)
{
string currentAction = html.ViewContext.RouteData.Values["action"].ToString();
if (currentAction.ToLower() == action.ToLower())
return html.ValidationSummary();
return new MvcHtmlString("<div class=\"validation-summary-valid\" data-valmsg-summary=\"true\"><ul><li style=\"display:none\"></li></ul></div>");
}
}
}
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();`