Asp.Net MVC HtmlHelper extension method not showing - asp.net-mvc

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

Related

helper show the html as a string asp.net MVC 5

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

MVC #helper error with HTML.Raw

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.

Is it possible to create a generic #helper method with Razor?

I am trying to write a helper in Razor that looks like the following:
#helper DoSomething<T, U>(Expression<Func<T, U>> expr) where T : class
Unfortunately, the parser thinks that <T is the beginning of an HTML element and I end up with a syntax error. Is it possible to create a helper with Razor that is a generic method? If so, what is the syntax?
This is possible to achieve inside a helper file with the #functions syntax but if you want the razor-style readability you are referring to you will also need to call a regular helper to do the HTML fit and finish.
Note that functions in a Helper file are static so you would still need to pass in the HtmlHelper instance from the page if you were intending to use its methods.
e.g.
Views\MyView.cshtml:
#MyHelper.DoSomething(Html, m=>m.Property1)
#MyHelper.DoSomething(Html, m=>m.Property2)
#MyHelper.DoSomething(Html, m=>m.Property3)
App_Code\MyHelper.cshtml:
#using System.Web.Mvc;
#using System.Web.Mvc.Html;
#using System.Linq.Expressions;
#functions
{
public static HelperResult DoSomething<TModel, TItem>(HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
{
return TheThingToDo(html.LabelFor(expr), html.EditorFor(expr), html.ValidationMessageFor(expr));
}
}
#helper TheThingToDo(MvcHtmlString label, MvcHtmlString textbox, MvcHtmlString validationMessage)
{
<p>
#label
<br />
#textbox
#validationMessage
</p>
}
...
No, this is not currently possible. You could write a normal HTML helper instead.
public static MvcHtmlString DoSomething<T, U>(
this HtmlHelper htmlHelper,
Expression<Func<T, U>> expr
) where T : class
{
...
}
and then:
#(Html.DoSomething<SomeModel, string>(x => x.SomeProperty))
or if you are targeting the model as first generic argument:
public static MvcHtmlString DoSomething<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expr
) where TModel : class
{
...
}
which will allow you to invoke it like this (assuming of course that your view is strongly typed, but that's a safe assumption because all views should be strongly typed anyways :-)):
#Html.DoSomething(x => x.SomeProperty)
In all cases the TModel will be the same (the model declared for the view), and in my case, the TValue was going to be the same, so I was able to declare the Expression argument type:
#helper FormRow(Expression<Func<MyViewModel, MyClass>> expression) {
<div class="form-group">
#(Html.LabelFor(expression, new { #class = "control-label col-sm-6 text-right" }))
<div class="col-sm-6">
#Html.EnumDropDownListFor(expression, new { #class = "form-control" })
</div>
#Html.ValidationMessageFor(expression)
</div>
}
If your model fields are all string, then you can replace MyClass with string.
It might not be bad to define two or three helpers with the TValue defined, but if you have any more that would generate some ugly code, I didn't really find a good solution. I tried wrapping the #helper from a function I put inside the #functions {} block, but I never got it to work down that path.
if your main problem is to get name attribute value for binding using lambda expression seems like the #Html.TextBoxFor(x => x.MyPoperty), and if your component having very complex html tags and should be implemented on razor helper, then why don't just create an extension method of HtmlHelper<TModel> to resolve the binding name:
namespace System.Web.Mvc
{
public static class MyHelpers
{
public static string GetNameForBinding<TModel, TProperty>
(this HtmlHelper<TModel> model,
Expression<Func<TModel, TProperty>> property)
{
return ExpressionHelper.GetExpressionText(property);
}
}
}
your razor helper should be like usual:
#helper MyComponent(string name)
{
<input name="#name" type="text"/>
}
then here you can use it
#TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty))

asp.net mvc Ajax.BeginForm clone

I'm using asp.net mvc ajax.
The partial view is using Ajax.BeginForm (just an example):
<div id="divPlaceholder">
<% using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "divPlaceholder" })) { %>
... asp.net mvc controls and validation messages
<input type="submit" value="Save" />
<% } %>
</div>
After update, if validation fails, the html is:
<div id="divPlaceholder">
<div id="divPlaceholder">
...form
</div>
</div>
I don't like that the returned html is inserted, instead it should replace original div.
Probably on POST I should not render <div> around form in partial view or render the div without id.
What else can I do in this situation?
I was thinking that maybe I should write a helper, something like Ajax.DivBeginForm, which will render form inside div on GET and hide the div on POST.
Can somebody provide a good advice how to write such helper (Ajax.DivBeginForm)?
I'd like it to work with using keyword:
<% using (Ajax.DivBeginForm(new AjaxOptions { UpdateTargetId = "myId" })) { ... }%>
My solution. Please comment if something is wrong.
public class DivMvcForm : MvcForm
{
private bool _disposed;
private MvcForm mvcForm;
private ViewContext viewContext;
public DivMvcForm(MvcForm mvcForm, ViewContext viewContext) : base(viewContext)
{
this.mvcForm = mvcForm;
this.viewContext = viewContext;
}
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
mvcForm.EndForm();
viewContext.Writer.Write("</div>");
}
}
}
Helper
public static class AjaxHelperExtensions
{
public static MvcForm DivBeginForm(this AjaxHelper ajaxHelper, AjaxOptions ajaxOptions)
{
var tagBuilder = new TagBuilder("div");
if (ajaxHelper.ViewContext.HttpContext.Request.RequestType == "GET"
&& string.IsNullOrWhiteSpace(ajaxOptions.UpdateTargetId) != true)
{
tagBuilder.MergeAttribute("id", ajaxOptions.UpdateTargetId);
}
ajaxHelper.ViewContext.Writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
var theForm = ajaxHelper.BeginForm(ajaxOptions);
return new DivMvcForm(theForm, ajaxHelper.ViewContext);
}
}
And how it works
<% using (Ajax.DivBeginForm(new AjaxOptions { UpdateTargetId = "divPlaceholder" })) { %>
... controls
<% } %>
Result - when ModelState is invalid the partial view returns div without id.
I'm going to take a little different approach here, rather than getting your original solution to work, I'd recommend using the pattern normally followed in this scenario and not using a helper. I realize this is a bit later than the original post, but for future use by anyone : )
If your partial view has a form, then you will keep posting, and returning a form in a form in a form in a form, etc. so you want to have your PARENT contain BeginForm, the div, and renderpartial
using (Ajax.BeginForm("Index", "ProjectManager", new AjaxOptions() ....
<div id="divPlaceholder">
Html.RenderPartial(....)
</div>
If you want to encapsulate this logic in say, an "Order" partial view that is displayed on a Customer screen, then you have two options.
1. Include the BeginForm on the parent Customer view (which reduces code reusability as any view that wants to include the "Order" partial view must include the ajax wiring.
Or
2. You have two partial views for order. One is OrderIndex.ascx (or cshtml if razor) and one is OrderIndexDetail.ascx (or whatever naming convention you decide)
OrderIndex contains your Ajax.beginform and OrderIndexDetail has no form, only the partial view details.
Option 2 is more code (ok, literally about 30 more seconds of coding to move the ajax.beginform into another view) but increases code reusability.
You can handle submit form yourself:
<div id="divPlaceholder">
<% using (Html.BeginForm("action", "controller", FormMethod.Post, new { id = "submitForm"})) { %>
... asp.net mvc controls and validation messages
<input type="submit" value="Save" />
<% } %>
</div>
and write some javascript as:
$('#submitForm').submit(function() {
$.post('post-to-this-url',
data: { foo: formvalue1, bar: formvalue2},
function(data) {
// update html here
});
return false;
})

DRY in the MVC View

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.

Resources