I have the following code in a Razor Helper file
#helper MakeNoteBlank(string content)
{
string msg = "";
if(content == null)
{
msg = " ";
}
else
{
msg = content;
}
<div class="note">
<p>
#Html.Raw(msg)
</p>
</div>
}
The code fails at execution with the #Html.Raw(..) statement, stating that
"Object reference not set to an instance of an object."
If I remove the #Html.Raw(..) and output 'msg' directly then there is no problem.
What am I doing wrong?
use #(new HtmlString()) instead of #Html.Raw()
The best approach I can think of would possibly be creating an extension method for HtmlHelper. You need to create a class like this:
using System.Web;
using System.Web.Mvc;
namespace MyApplication.Extensions
{
public static class HtmlHelperExtension
{
public static IHtmlString DisplayMessage<T>(this HtmlHelper<T> htmlHelper, string content)
{
return htmlHelper.Raw($#"
<div class=""note"">
<p>
{content ?? " "}
</p>
</div>
");
}
}
}
Then in your cshtml file, simply use it like this:
#using MyApplication.Extensions;
#Html.DisplayMessage("Your content here")
Hope this helps.
Related
I am working on asp.net MVC 5, I created a helper for boolean.
I wrote this:
public class CustomHelper
{
public static string Boolean(bool? value)
{
if(value == true )
return string.Format("<span class='glyphicon glyphicon-ok green'></span>");
else
return string.Format("<span class='glyphicon glyphicon-remove red'></span>");
}
}
and the Razor is:
<td>
#CustomHelper.Boolean(item.Deleted)
</td>
but the result is the same as the photo. the html appears as a string
how to fix it? so I can show the symbol instead?
thank you
By default, the # symbol encodes the output.
You should create your custom check as an extension method to the HtmlHelper class as below
public static class BooleanExtensions
{
public static MvcHtmlString Boolean(this HtmlHelper helper, bool value)
{
var spanTag = new TagBuilder("span");
if(value)
spanTag.MergeAttribute("class", glyphicon glyphicon-ok green");
else
spanTag.MergeAttribute("class", glyphicon glyphicon-remove red");
return MvcHtmlString.Create(spanTag.ToString(TagRenderMode.EndTag));
}
}
Next, in your Razor view, call it as:
#Html.Boolean(item.Deleted)
Don't forget to add the #using for the namespace that has the class in the beginning of the view
For some reason my ViewData outputs HTML code even though I don't want it do.
This is what it
<br />2015-04-01
<br />2015-04-02
<br />2015-04-07
<br />2015-04-08
<br />2015-04-09
<br />2015-04-10
but I just want it to look like this
Du har ej rapporterat tid följande dagar:
2015-04-01
2015-04-02
2015-04-07
2015-04-08
2015-04-09
2015-04-10
This is part of my controller:
var missingdays = new DatabaseLayer().GetConsultantMissingDays(Constants.CurrentUser(User.Identity.Name));
if (missingdays.Count == 0)
{
ViewData["missingDays"] = "";
}
else
ViewData["missingDays"] = "Du har ej rapporterat tid följande dagar:<br />" +
string.Join("<br />", missingdays.Select(x => x.ToMissingDateJavascript()));
ViewData.Model = projectData;
return View();
}
And this is from my view:
<div>
#ViewData["missingDays"]
#Html.ValidationSummary()
</div>
and my Extensions
public static string ToMissingDateJavascript(this DateTime value) {
string dateString = value.ToString("yyyy-MM-dd");
return "" + dateString + "";
}
public static bool IsWeekend(this DateTime value) {
return value.DayOfWeek == DayOfWeek.Saturday || value.DayOfWeek == DayOfWeek.Sunday;
}
But I see the HTML code in the browser
You can wrap the call in Html.Raw(), like this:
#Html.Raw(ViewData["missingDays"])
However, it is better to pass in an array rather than HTML (or even passing it in the view model). You should avoid using any HTML in your controller as much as possible. For example:
#foreach(var date in (List<DateTime>)ViewData["stuff"])
{
<a href="javascript:SetDate('#date.ToString("yyyy-MM-dd")');">
#date.ToString("yyyy-MM-dd")
</a>
<br/>
}
Noet: I would also suggest not using br tags here and format with CSS.
In MVC the View is supposed to execute all work related to generating HTML, not the Controller. You can rewrite and simplify both the view and the controller as follows:
View:
#foreach(var date in ViewBag.MissingDays){
var isoDate=date.ToString("yyyy-MM-dd");
<br/>#isoDate
}
Controller:
//Assuming that missingdays is a List<DateTime> or other IEnumerable<DateTime>
ViewBag.MissingDays=missingdays;
You'll need to convert the string to HTML:
#{
string missingDays = ViewData["missingDays"]
}
#MvcHtmlString.Create(missingDays)
OK, I'm trying to implement the Repeater extension methods to the HtmlHelper as explained in Phil Haack's blog here http://haacked.com/archive/2008/05/03/code-based-repeater-for-asp.net-mvc.aspx
However, when I try to use it in my View I get a Compilation error 'System.Web.Mvc.HtmlHelper' does not contain a definition for 'Repeater'.
Here is my extension class:
namespace MyAwesomeBlog.Helpers {
public static class HtmlHelpers {
public static void Repeater<T>(this HtmlHelper html
, IEnumerable<T> items
, Action<T> render
, Action<T> renderAlt) {
// Implementation irrelevant
});
}
public static void Repeater<T>(this HtmlHelper html
, Action<T> render
, Action<T> renderAlt) {
// Implementation irrelevant
});
}
public static void Repeater<T>(this HtmlHelper html
, string viewDataKey
, Action<T> render
, Action<T> renderAlt) {
// Implementation irrelevant
});
}
public static void Repeater<T>(this HtmlHelper html
, IEnumerable<T> items
, string className
, string classNameAlt
, Action<T, string> render) {
// Implementation irrelevant
});
}
}
}
I have included this in my Web.Config:
<add namespace="MyAwesomeBlog.Helpers"/>
This is my use of the extension method in my view:
<% HtmlHelper.Repeater<Post>(Model, "post", "post-alt", (post, cssClassName) => { %>
<div class="<%=cssClassName %>">
<h1><%= post.Title %></h1>
<p>
<%= post.Body %>
</p>
</div>
<% }); %>
Still, the compiler gives me squiggly lines under ".Repeater" saying that HtmlHelper does not have such a method.
What have I missed?
Regarding my comment in my other answer, I have just checked and I am almost sure that is your problem. You can't have extension methods on static classes (or add static extension methods), so you need an instance of HtmlHelper to call Repeater.
Did you add this to the Web.Config in the Views folder or the root web.config?
It needs to go in 'Views/web.config'.
Try changing it to:
<% Html.Repeater<Post>(Model, "post", "post-alt", (post, cssClassName) => { %>
<div class="<%=cssClassName %>">
<h1><%= post.Title %></h1>
<p>
<%= post.Body %>
</p>
</div>
<% }); %>
The Html property on your View is an HtmlHelper.
Extend the IHtmlHelper interface rather than HtmlHelper class.
public static void Repeater<T>(this IHtmlHelper html
, IEnumerable<T> items
, string className
, string classNameAlt
, Action<T, string> render) {
I came across this very problem today and in my case closing and reopening the page with the html on it seemed to do the trick (and compiling the project obviously).
In Asp.net MVC3 when you write below code , it generates wrapping html itself
#using (Html.BeginForm()) {
#Html.ValidationMessageFor(model => model.Text)
}
It generates codes in below format,
<form method="post" action="/Feeds">
<!-- Fields Here -->
</form>
My question in #using (Html.BeginForm()) automatically adds <form> tag at beginning and end, how can i create something like that of my own.
I am looking for some thing like below
#using (Html.BeginMYCUSTOMDIV())
{
I am text inside div
}
Expected Generated Output
<div class="customDivClass">
I am text inside div
</div>
Something along the lines:
public class MyDiv : IDisposable
{
private readonly TextWriter _writer;
public MyDiv(TextWriter writer)
{
_writer = writer;
}
public void Dispose()
{
_writer.WriteLine("</div>");
}
}
public static class MyExtensions
{
public static MyDiv BeginMYCUSTOMDIV(this HtmlHelper htmlHelper)
{
var div = new TagBuilder("div");
div.AddCssClass("customDivClass");
htmlHelper.ViewContext.Writer.WriteLine(div.ToString(TagRenderMode.StartTag));
return new MyDiv(htmlHelper.ViewContext.Writer);
}
}
and in the view:
#using (Html.BeginMYCUSTOMDIV())
{
<span>Hello</span>
}
generates:
<div class="customDivClass">
<span>Hello</span>
</div>
If I'm not mistaken, Html.BeginForm() returns an IDisposable object. When used in the using block, the object's Disposemethod is called, which is the responsible to write the closing tag to the output.
how does using HtmlHelper.BeginForm() work?
Html.BeginForm() type of extension
I've been working a lot with asp.net web forms and one think that I like about the is the consistency with the generated markup e.g. if you create a composite control for a TextField you can control the generated markup in a single class like and don't break the SRP:
<form:textfield id="firstName" runat="server" required="true" label="First Name" />
I you're your going to generate the markup by hand it might look like this:
<label for="firstName" id="lbl_firstName">Name <span class="required">*</span></label>
<input id="firstName" name="firstName" type="text" value="" />
The problem is when would like to change something for example add a wrapping div or move the span. In worst case you have to edit thousands of views.
That's why I really like the MVC Contrib FluentHtml.
<%= this.TextBox(x => x.Message.PostedBy).Class("required").Label("Name") %>
My question is what do you think is the best way to add a wrapping div for the code line above? I think hand writing is not an option because of the arguments above? Perhaps extending the TextBox : MvcContrib.FluentHtml.Elements.TextInput?
have you checked InputBuilder in MvcContrib project? it is used in Codecampserver as well. have a look and i think u will like it.
Honestly, I don't think the example case you've given applies to real world. A textbox is a textbox. If you need one, you render one.
If you need a more "complex" control like a textbox wrapped in a div tag, then you can have a partial view for that.
For example, Model :
public class CustomControlModel {
public string Name { get; set; }
public string Value { get; set; }
public string Class { get; set; }
public bool WrapInDivTag { get; set; }
//you get the idea
}
Custom Control :
<%# Control Inherits="System.Web.Mvc.ViewUserControl<CustomControlModel>" %>
<%if (Model.WrapInDivTag) {%> <div> <% } %>
<%=Html.TextBox(Model.Name, Model.Value, new { #class = Model.Class })%>
<%if (Model.WrapInDivTag) {%> </div> <% } %>
And when rendering :
<%Html.RenderPartial("CustomControl",
new CustomControlModel { Name = "name", WrapInDivTag = true }); %>
That's a very simple example but I hope it explains why I suggested partial views. Don't forget that you can expose another property to get which tag to render etc.
InputBuilders are one option. With FluentHtml you could create a custom element, something like this:
public class TextBoxInContainer : TextInput<TextBox>
{
public TextBoxInContainer (string name) : base(HtmlInputType.Text, name) { }
public TextBoxInContainer (string name, MemberExpression forMember, IEnumerable<IBehaviorMarker> behaviors) : base(HtmlInputType.Text, name, forMember, behaviors) { }
protected override ToString()
{
divBuilder = new TagBuilder(HtmlTag.Div);
divBuilder.InnerHtml = ToString();
return divBuilder.ToString(TagRenderMode.SelfClosing);
}
}
To use this from your view you would extend IViewModelContainer something like this:
public static MyTextBox TextBoxInContainer <T>(this IViewModelContainer<T> view, Expression<Func<T, object>> expression) where T : class
{
return new TextBoxInContainer (expression.GetNameFor(view), expression.GetMemberExpression(), view.Behaviors)
.Value(expression.GetValueFrom(view.ViewModel));
}
Then if you want to change your container to a span sitewide, you change the ToString method of TextBoxInContainer.