How to convert this code to AspNetCore
public static MvcHtmlString ChqLabelFor<TModel, TValue>(this HtmlHelper<TModel> html,
Expression<Func<TModel, TValue>> expression, object htmlAttributes)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
string resolvedLabelText = metadata.DisplayName ?? metadata.PropertyName;
if (metadata.IsRequired)
{
resolvedLabelText += "*";
}
return LabelExtensions.LabelFor<TModel, TValue>(html, expression, resolvedLabelText, htmlAttributes);
}
I know that I can use now instead of MvcHtmlString just HtmlString
What to do with
ModelMetadata.FromLambdaExpression
I couldn't find any alternative ...
Those helpers still exist, but they are buried a little.
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, htmlHelper.ViewData, htmlHelper.MetadataProvider);
You can then access the metadata with
modelExplorer.Metadata
and the model itself with
modelExplorer.Model
I've got some example code here that uses it for PowerBI Embedded report rendering.
https://blogs.endjin.com/2016/09/how-to-use-power-bi-embedded-with-aspnetcore/
From the #willDaBeast comment on the other answer, what worked for me was the following code that I provided as a separate answer for better formatting. Although it might not be the recommended practice.
ModelExpressionProvider expressionProvider = new ModelExpressionProvider(htmlHelper.MetadataProvider);
var metadata = expressionProvider.CreateModelExpression(htmlHelper.ViewData,expression);
In netcore 3.0:
Get an instance of ModelExpressionProvider from DI
Use CreateModelExpression method
var metadata = _modelExpressionProvider.CreateModelExpression(ViewData, expression).Metadata;
P.S
In order to extend HtmlHelper, I would suggest another approach:
public class CustomHtmlHelper : HtmlHelper, ICustomHtmlHelper
{
// add your extension methods here and in ICustomHtmlHelper
// _modelExpressionProvider will be part of constructor
// register implementation in DI
}
and use the new helper in views:
#inject ICustomHtmlHelper Html
Related
is there any helper in asp.net MVC3
Go to Google
?
Not for an action but to a static link
I don't believe there is, but I'm not sure why you would want one. You'd actually end up with more code:
Go to Google
<%: Html.Link("http://www.google.com/", "Go to Google") %>
#Html.Link("http://www.google.com/", "Go to Google")
Update: If you want to create a Link() helper like that above, you would use an extension method:
public static class LinkExtensions
{
public static MvcHtmlString Link(this HtmlHelper helper, string href, string text)
{
var builder = new TagBuilder("a");
builder.MergeAttribute("href", href);
builder.SetInnerText(text);
return MvcHtmlString.Create(builder.ToString(TagRenderMode.Normal));
}
}
public static string MakeAnchor(string text)
{
string pattern = #"((?<Protocol>\w+):\/\/(?<Domain>[\w#][\w.:#]+)\/?[\w\.?=%&=\-#/$,]*)";
Regex r = new Regex(pattern, RegexOptions.Multiline);
var anchor = r.Replace(text, "$1");
return anchor;
}
This method returns <a href="http://google.com/" target="_blank">http://google.com/</a> if I pass it some text containing http://google.com/
instead of <a href="http://google.com/" target="_blank">http://google.com/</a>
I'm using razor views with MVC 3. I'm stumped. Is there some sort of new view encoding in MVC 3 that I'm unaware of?
If you use #: (in Razor) or <%: (in the WebForm View Engine) the text will be automatically html encoded. Try #= or <%=
Make your method return an IHtmlString to indicate to the framework that your output is already encoded.
The simplest implementation of that is return new HtmlString(anchor);
IHtmlString is implemented in .NET 4, so if you're in .NET 3.5, you'll have to use the #= or <%= syntax (as #Andrew mentions) to avoid encoding.
I found this blog post (http://www.akngo.com/) that shows how to "wrap" Htmlstring for convenience sake. Usage is RawView.SomeString
I have been using the Razor engine for
a few days now, and I am liking it a
lot. I noticed one big problem right
from the start, all strings are HTML
encoded. For our uses, this becomes
quite an annoying “feature” when we
deal with JSON strings. For example,
given a simple JSON string, ["1", "2",
"3", "4"]
The actual HTML encoded output of this
became, ["1", "2",
"3", "4"]
The quotes have been escaped, which is
not the desired output. If we need to
send HTML back out, or XML, we would
have to then individually escape
those. Overall, I find this quite
annoying as we would have to wrap it
inside of an HtmlString as shown in
this StackOverflow post.
In our scenario, we are generating
JSON/XML from C#, so most of the time,
our serialized data is inside of the
ViewModel. Which would result in us
doing things like, #(new
HtmlString(View.Foo)) #(new
HtmlString(View.Bar))
As you can see, this can become quite
cumbersome. So I decided to
plagiarized part of MVC to create a
RawView. In the end, the usage will
be, #RawView.Foo #RawView.Bar
I also went ahead to allow the usage
of the index operator as well in case
there’s a need to loop over it with a
list of keys. // Assuming we have Foo,
Bar, and Baz in our ViewModel #{
List keys = new List { "Foo", "Bar", "Baz" };
foreach (var key in keys) {
#RawView[key]
} }
To achieve this, I would need to
duplicate DynamicViewDataDictionary.
Naturally, I named it
DynamicRawViewDataDictionary, public
class DynamicRawViewDataDictionary :
DynamicObject
{
private readonly Func viewThunk;
public DynamicRawViewDataDictionary(Func
viewThunk)
{
this.viewThunk = viewThunk;
}
private ViewDataDictionary ViewData
{
get
{
ViewDataDictionary viewData = this.viewThunk();
return viewData;
}
}
public object this[string key]
{
set { setData(key, value); }
get { return getData(key); }
}
public override IEnumerable<string>
GetDynamicMemberNames()
{
return ViewData.Keys;
}
public override bool TryGetMember(GetMemberBinder binder,
out object result)
{
result = getData(binder.Name);
return true;
}
public override bool TrySetMember(SetMemberBinder binder,
object value)
{
setData(binder.Name, value);
return true;
}
private object getData(string key)
{
object value = ViewData[key];
if (value is string)
{
return new HtmlString(value.ToString());
}
return value;
}
private void setData(string key, object value)
{
ViewData[key] = value;
}
}
The reason for the duplication is
because the DynamicViewDataDictionary
is an internal sealed class. There was
no way I could get around this
restriction other than copying and
pasting. The obvious change I made to
this class is the getData method. I
checked to see if the object is a
string, if it is, I wrapped it with an
HtmlString().
Now I need to use this inside of a
WebViewPage, since all views , I have
to have a customized version of a
WebViewPage as well. Good thing that
MS didn’t make this class internal
sealed, so I can easily inherit from
it. I needed to make a generic and a
non-generic version, I just plopped
both of this inside of one file.
public abstract class
CustomWebViewPage :
WebViewPage
{
private DynamicRawViewDataDictionary _rawData;
public dynamic RawView
{
get
{
if (_rawData == null)
{
_rawData = new DynamicRawViewDataDictionary(() =>
ViewData);
}
return _rawData;
}
}
}
public abstract class CustomWebViewPage : WebViewPage
{
private DynamicRawViewDataDictionary _rawData;
public dynamic RawView
{
get
{
if (_rawData == null)
{
_rawData = new DynamicRawViewDataDictionary(() =>
ViewData);
}
return _rawData;
}
}
}
For the final touch to actually make
this work, at the top of the cshtml
file, you should have something like,
#inherits
Namespace.To.CustomWebViewPage
//or #inherits
Namespace.To.CustomWebViewPage
Now that the cshtml file inherits from
the new CustomWebViewPage, it should
have access to RawView, which will
wrap all strings with an HtmlString.
Once you have this in place, you’ll
still need to make sure that all of
your new views inherits from
CustomWebViewPage. I highly recommend
using custom code templates in your
MVC project to generate the view. This
will help with automating a lot of the
mundane tasks.
Htmlstring is the answer.
#(new HtmlString(SomeString))
I have the following HtmlHelper method that I want to create a button that does a redirect with JavaScript:
public static string JavaScriptButton(this HtmlHelper helper, string value,
string action, string controller, object routeValues = null, object htmlAttributes = null)
{
var a = (new UrlHelper(helper.ViewContext.RequestContext))
.Action(action, controller, routeValues);
var builder = new TagBuilder("input");
builder.Attributes.Add("type", "submit");
builder.Attributes.Add("value", value);
builder.Attributes.Add("class", "button");
builder.Attributes.Add("onclick", string.Format("javascript:location.href='{0}'", a));
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing)).ToString();
}
The problem is that the line that creates the onclick handler is getting escaped by the tagbuilder, the resulting html is:
<input class="button" onclick="javascript:location.href=''" type="submit" value="Return to All Audits" />
Is there anyway I can stop this behaviour?
This is actually an issue with .NET 4.0. To fix it you need to override the attribute encoding process.
public class HtmlAttributeNoEncoding : System.Web.Util.HttpEncoder
{
protected override void HtmlAttributeEncode(string value, System.IO.TextWriter output)
{
output.Write(value);
}
}
Then put this in your web.config file under the <system.web> element:
<httpRuntime encoderType="HtmlAttributeNoEncoding"/>
I found this here.
While the solution provided by Ryan may work, it is a little like using a hammer to swat a fly.
The issue is that TagBuilder encodes strings during calls to MergeAttributes(). For the required Javascript in a button link this affects single quotes and spaces.
The last step of the required extension method is the return of an MvcHtmlString (which receives no further encoding), so it is perfectly reasonable to make some simple text corrections to the string (to undo the encoding) before creating that object.
e.g.
return new MvcHtmlString(tb.ToString(TagRenderMode.Normal).Replace("'", "\'").Replace(" "," "));
A complete ActionLinkButton helper is shown below:
public static class ActionLinkButtonHelper
{
public static MvcHtmlString ActionLinkButton(this HtmlHelper htmlHelper, string buttonText, string actionName, object routeValuesObject = null, object htmlAttributes = null)
{
return ActionLinkButton(htmlHelper, buttonText, actionName, "", routeValuesObject, htmlAttributes);
}
public static MvcHtmlString ActionLinkButton(this HtmlHelper htmlHelper, string buttonText, string actionName, string controllerName, object routeValuesObject = null, object htmlAttributes = null)
{
if (string.IsNullOrEmpty(controllerName))
{
controllerName = HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString();
}
RouteValueDictionary routeValues = new RouteValueDictionary(routeValuesObject);
RouteValueDictionary htmlAttr = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
TagBuilder tb = new TagBuilder("button");
tb.MergeAttributes(htmlAttr, false);
string href = UrlHelper.GenerateUrl("default", actionName, controllerName, routeValues, RouteTable.Routes, htmlHelper.ViewContext.RequestContext, false);
tb.MergeAttribute("type", "button");
tb.SetInnerText(buttonText);
tb.MergeAttribute("value", buttonText);
tb.MergeAttribute("onclick", "location.href=\'"+ href +"\';return false;");
return new MvcHtmlString(tb.ToString(TagRenderMode.Normal).Replace("'", "\'").Replace(" "," "));
}
}
This does everything you need to add button links, has the most useful overloads you use with ActionLink and does not potentially cause unexpected application-wide changes by changing the attribute encoding process.
The TagBuilder does not encode the string during MergeAttribute as directly manipulating the Attributes property and then getting the string representation with ToString() is also escaping the characters. Most probably the encoding happens during ToString().
The class below solves the problem with JavaScript:
public class JavaScriptTagBuilder : TagBuilder
{
public string OnClick { get; set; }
public JavaScriptTagBuilder(string tagName)
: base(tagName)
{
}
public override string ToString()
{
string openingTag = "<" + TagName;
return base.ToString().Replace(openingTag, string.Format("{0} onclick=\"{1}\"", openingTag, OnClick));
}
}
I'm a little late to the disco but you could decode the HTML before returning it:
return new MvcHtmlString(System.Web.HttpUtility.HtmlDecode(tb.ToString(TagRenderMode.Normal));
I have an HTMLHelper extension method that outputs HTML to Response.Write. How best to unit test this?
I'm considering mocking the HtmlHelper passed into the method, but am unsure as to how I should verify the HTML sent to Response.Write.
Thanks
If you are using an HTML helper to output text to the browser why not have it return a string and in your view do something like ...
<%=Html.YourExtension() %>
It makes it a great deal more testable.
Kindness,
Dan
EDIT:
Modification would be a change of signature
public static void YourExtension(this HtmlHelper html)
{
...
Response.Write(outputSting);
}
to
public static string YourExtension(this HtmlHelper html)
{
...
return outputSting;
}
I use the following code to test and validate html helpers. If you are doing anything complex, like disposable based helpers like beginform or helpers with dependencies you need a better test framework then just looking at the string of a single helper.
Validation is a another example.
Try the following:
var sb = new StringBuilder();
var context = new ViewContext();
context.ViewData = new ViewDataDictionary(_testModel);
context.Writer = new StringWriter(sb);
var page = new ViewPage<TestModel>();
var helper = new HtmlHelper<TestModel>(context, page);
//Do your stuff here to exercise your helper
//Get the results of all helpers
var result = sb.ToString();
//Asserts and string tests here for emitted HTML
Assert.IsNotNullOrEmpty(result);
This works if the method "YourExtension" is simply using HtmlHelper methods that return string or HtmlString. But methods like "BeginForm" return MvcForm object and also the form tag is written directly on TextWriter that HtmlHelper has reference to.
I'm writing a helper method for ASP.NET MVC and I need to call Url.Content to get an appropriate URL for the context. However, in order to create a new UrlHelper() I need to get the current RequestContext (System.Web.Routing.RequestContext to be precise) and I'm not sure how to grab it. Anyone know?
If the current IHttpHandler is MvcHandler, you can use
((MvcHandler)HttpContext.Current.Handler).RequestContext
Noticed this was still unanswered. As of MVC 1.0 you can do:
public static string NewHelperMethod(this HtmlHelper helper)
{
UrlHelper url = new UrlHelper(helper.ViewContext.RequestContext);
You may have found an answer elsewhere, but here goes;
In a controller action, you can get to the current RequestContext like so:
public ActionResult SomeAction(){
var helper = new UrlHelper(this.ControllerContext.RequestContext);
...
}
As mentioned above, just extend the HtmlHelper and the context is exposed in that way. For example:
public static string ExtensionMethodName(this HtmlHelper html,object o)
{
html.ViewContext.HttpContext.Request.Uri ... etc
}
Don't create a new one. Just extend the existing UrlHelper, just like you'd extend HtmlHelper:
public static string IdLink(this UrlHelper helper, Guid id)
{ //...
If you must use both HtmlHelper and UrlHelper, pass one of them as a regular (non-"this") argument.