Is there a way by which I can do somethin like this inside a Razor view:
<h1>Normal razor code</h2>
#Html.Action("NormalRazorCode")
#Eval(" #Html.Action(\"RuntimeEval\") ")
Basically a text-to-razor compiler at runtime (that doesnt create a whole new view like RazorEngine does for example).
I think you could assume that the views exist at compile time, and create the actual files at runtime, this way the ViewEngine will work the way it does by default
basically you could create a Html.Eval helper that will create the .cshtml file and after Render it using Html.Action or Html.Partial
I wanted to do something similar; I have model data (under my control) stored in a database, and it would simplify my life if I was able to include HTML helpers in those strings that could be "expanded" when included in a page.
Main motivation was to allow me to re-use existing partial views.
There's no eval function, but you can easily write an extension method that will evaluate methods that you choose to allow in advance. In my case, I want to evaluate calls to #Html.Partial(). The example here is pretty simple - it looks specifically for #Html.Partial("somePartialView") calls and replaces it with the actual partial:
public static IHtmlString ExpandHtmlString(
this HtmlHelper htmlHelper,
String html)
{
if (String.IsNullOrEmpty(html))
return new HtmlString(html);
const String IDENTIFY_PARTIAL = #"#Html.Partial\(""([a-zA-Z0-9\-_]*)""\)";
var partialFinder = new Regex(IDENTIFY_PARTIAL);
var matches = partialFinder.Matches(html);
foreach (Match m in matches) {
var matchedStr = m.Value;
var viewName = m.Groups[1].Value;
var partial = htmlHelper.Partial(viewName);
html = html.Replace(matchedStr, partial.ToHtmlString());
}
return new HtmlString(html);
}
And you call it from your Razor page as so:
#Html.ExpandHtmlString((String)Model.SomeStringField)
You could easily expand on this to to evaluate a set of methods or operators that you decide in advance you will accept.
Related
I would like to use a helper for Submit button in MVC3. Is such a thing available? If not, then does anyone know where I could get some code for this. I would like one that allows me to pass a class attribute.
Isn't it simple just to write
<input type="submit" class="myclassname"/>
In MVC, there are no such things like controls, that carry much application logic. In fact, it is possible but not recommended. What i want to say is that Html helpers are just making Html writing more comfortable and help you not write duplicate code. In your particular case, i think it is simpler to write direct html than that with helper. But anyways, if you want to, it is contained in MVC Futures Library. Method is called SubmitButton
Just add to your project class with such code:
using System.Text;
namespace System.Web.Mvc
{
public static class CustomHtmlHelper
{
public static MvcHtmlString SubmitButton(this HtmlHelper helper, string buttonText, object htmlAttributes = null)
{
StringBuilder html = new StringBuilder();
html.AppendFormat("<input type = 'submit' value = '{0}' ", buttonText);
//{ class = btn btn-default, id = create-button }
var attributes = helper.AttributeEncode(htmlAttributes);
if (!string.IsNullOrEmpty(attributes))
{
attributes = attributes.Trim('{', '}');
var attrValuePairs = attributes.Split(',');
foreach (var attrValuePair in attrValuePairs)
{
var equalIndex = attrValuePair.IndexOf('=');
var attrValue = attrValuePair.Split('=');
html.AppendFormat("{0}='{1}' ", attrValuePair.Substring(0, equalIndex).Trim(), attrValuePair.Substring(equalIndex + 1).Trim());
}
}
html.Append("/>");
return new MvcHtmlString(html.ToString());
}
}
}
And usage example:
#Html.SubmitButton("Save", new { #class= "btn btn-default", id="create-button" })
chk this link out it tells you how to create custom helper method, and there is no builtin submit helper ...
http://stephenwalther.com/blog/archive/2009/03/03/chapter-6-understanding-html-helpers.aspx
and it also include a very basic Submit helper method, hope it helps
There isn't a HTML helper for a button because the HTML helpers reflect over a model's propertys and help you by setting the correct attributes for binding purposes, they also look at the VilidationAttribute metadata for that property (if any) and add any jQuery validation attributes.
Buttons are not part of the model and so do not have any helpers.
You can crate your own HTML helper by following this article http://www.asp.net/mvc/overview/older-versions-1/views/creating-custom-html-helpers-cs or using the TagBuilder class: http://www.asp.net/mvc/overview/older-versions-1/views/using-the-tagbuilder-class-to-build-html-helpers-cs but I prefer to return HTMLString than a string
There isn't one but that shouldn't stop you from building one yourself.
Even though the MVC futures project doesn't appear to be moving forward at all or supported, it's not too hard to download the source, and grab the Submit button helper (and it's supporting code) to roll your own.
It's exactly how we created the base of our SubmitButton helper for a large MVC 4 project.
Here is how I did it.
Speaking of HTML 5 <button> attribute
Create a PartialView - call it like _HTML5Button.vbhtml
#ModelType YourProjectName.ButtonViewModel
<button onclick="location.href = '#Model.URL'" class="btn btn-info">#Model.ButtonText</button>
And create a ButtonViewModel
Public Class ButtonViewModel
Public URL As String
Public ButtonText As String = "Modify Button Text"
'You can add more parameters or do as you please
End Class
Then as you need to create it call you partial like this
#Html.Partial("~/Views/Shared/MiniPartialViews/_HTML5Button.vbhtml", New ButtonViewModel With {.URL = "http://www.goanywhere.com", .ButtonText = "Name That Will Appear On The Button"})
That way if you want to add more parameters later - it is all in that one partial view centralized for you - lets say you want to add an id later on
Well you go to you partial, add an id="#Model.Id" so then in your PartialView Call you just add that parameter - it does not break anything - if you ever need to add a class to that button - add it - in one place rather than searching for all the calls.
Hope that helps - it works super well for me!
I have a large view that needs some conditional logic to decide which of several html chunks to render in the middle of the view. I have a property on my model which can have several different values which determines the html to be output.
I would normally put conditional logic in an html helper, but given that each output is a fair chunk of html, I am not sure that escaping these in a c# file would be great. I could also put the logic in the action and render different views but given that the majority of the view is the same, this does not seem great either. So I am left with multiple if statements in my view (or partial?) which also seems ugly (and is obviously untestable).
What is the best way of doing this?
(I am using MVC3 in case there is something new and funky I can use!)
I usually put separate visual chunks in their own partials. Then my view conditionally calls each partial with Html.Partial. This keeps you main view from bloating.
In general, I try to avoid Html.Helpers that contain more than a single element.
Something like:
#if(Model.HasA)
{
#Html.Partial("widgetdetails-hasa")
}
#if(Model.HasB)
{
#Html.Partial("widgetdetails-hasb")
}
// etc
IMHO logic like this is fine for a view:
#if (Model.ShouldShowSomeSection)
{
... some large chunk of HTML
}
else
{
... some alternative
}
I agree with the answer from #bmancini , but here's what I'd do slightly differently:
I would logically group those 'several html chunks to render' into different partial views :
_partialViewA.cshtml and _partialViewB.cshtml
I then would use extension methods and have my logic in a Helpers folder, then Html sub-folder like this:
using System.Web.Mvc.Html;
public static class SomeViewHelper
{
public static MvcHtmlString OutputHtmlString(this HtmlHelper helper , SomeModel model)
{
if(model.HasA)
{
return helper.Partial("_partialViewA", model)
}
if(model.HasB)
{
return helper.Partial("_partialViewB", model)
}
}
}
This would remove all the logic from the view which would now only have this code:
#Html.OutputHtmlString(Model);
At least this would remove the 'ugliness' and avoid the conditional statements, and also avoid 'escaping the html chinks in C# code'...
Of course I would have to reference the Helpers.Html folder with a #using statement in the view.
I have a partial view that I want to be generic. According to this question, partial views cannot be generic. So I instead made an HtmlHelper extension that handles the pieces for which I want type-safety, then hands off the rest to a real partial view.
Usually my helper is called on page load, which works fine, but sometimes I want to add a row or something through AJAX. When this happens, the controller cannot use my "partial view" since it does not have access to the HtmlHelper.
Apart from having a partial view with a model of type object, is there anything I can do?
I'm using Razor, if that is important.
A simplified version of what I'm doing:
public static MvcHtmlString DoStuff<T>(this HtmlHelper html, IEnumerable<T> data,
Func<T, ViewModelType> StronglyTypedFn, string PartialName)
{
// the pre- and post-processing for the partial view is complex enough I'd like
// to encapsulate it. But I want the encapsulation to include the safety
// benefits that generics give.
var mappedData = data.Select(StronglyTypedFn);
string htmlData = "";
foreach(var model in mappedData){
htmlData += html.Partial(PartialName, model);
}
htmlData += "some boilerplate footer html";
return htmlData;
}
I realize that in this example I have so few lines of code outside the partial view that it seems pointless to have a helper, but in my real example it is more complex.
Now, in an ajax call I want to return Html.DoStuff(). But I can't, because this requires access to the HtmlHelper, and the helper isn't available inside a controller.
You could just have a simple action method that calls the partial for one model instance
public PartialViewResult Single(string partialName) {
return PartialView(partialName);
}
You could use a View with a Dynamic type instead of object.
But... It seems as if there's some misunderstanding here because the Controller shouldn't try to render the view at all. Could you post the Controller code?
The better option is, IMO, returning a JsonResult for your ajax request and adding the row/rows on client side using JS.
Is it possible to detect a route value in a view?
Such as /pages/create/1 and I want to check to see if 1 is there?
Basically, I want to render a different partial view based on this value though I'm fairly sure this is probably not the best way to go about what I'm trying to achieve.
On a side note, instead of doing the above is it possible for me to be able to change what partial views are rendered in a view based on a value from within my controller?
ViewContext.RouteData.Values["whatever"]
You can inspect a RouteData object through ViewPage.ViewContext.RouteData. Then check for values using something like
string areaname = routeData.Values["area"] as string;
string controllername = routeData.Values["controller"] as string;
string actionname = routeData.Values["action"] as string;
string id = routeData.Values["id"] as string;
If you find that you want to inspect these values in the controller instead, you can access them using ControllerBase.ControllerContext.RouteData. Something similar applies for action filters etc.
Other answers are correct, but thought i'd address your last sentence:
On a side note, instead of doing the above is it possible for me to be able to change what partial views are rendered in a view based on a value from within my controller?
Well partial view's are rendered in the View itself (unless calling from JavaScript and binding directly to DOM) with the following code:
<%: Html.RenderPartial("SomePartial") %>
So to prevent "code soup" (if statements) in your view, you use a HTML helper which calls through to RenderPartial after inspecting the ViewContext:
public static string RenderCustomPartial(this HtmlHelper helper, RouteData rd)
{
string partialName;
if (rd.Values["SomeParam"] == 1)
partialName = "PartialOneName";
else
partialName = "PartialTwoName";
return helper.RenderPartial(partialName);
}
And then in the View:
<%: Html.RenderCustomPartial(ViewContext.RouteData) %>
You could make some mods to the above - like access the route data directly in the extension, pass through the model to bind in the partial, etc - but you get the idea.
Alternatively you could do the above IF statement in your controller, and stuff the partial view name in the ViewData, then use that in the regular RenderPartial call in your View.
Whatever floats your boat. :)
The Problem
I have a very nifty menu Html helper written for WebFormViewEngine views. This engine allows your helpers to return void, and still be able to use:
#Html.Theseus
This is great for my helper, because it can then render the menu using HtmlTextWriter, that renders directly to the output stream.
In Razor views, however, the Html helpers are expected to return a value (usually MvcHtmlString) which is what gets added to the output. Small difference, big consequence.
There is a way around this, as pointed out to me by GvS (see ASP.NET MVC 2 to MVC 3: Custom Html Helpers in Razor) as follows:
If the helper returns void, then do the following:
#{Html.Theseus;}
(Essentially, you are just calling the method, rather than rendering into the view).
Whilst still neat, this is not quite the same as #Html.Theseus. So...
My code is complex but works very well, so am loath to go through major edits, ie, replacing the HtmlTextWriter with another writer. A snippet of the code goes like:
writer.AddAttribute(HtmlTextWriterAttribute.Href, n.Url);
writer.AddAttribute(HtmlTextWriterAttribute.Title, n.Description);
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.WriteEncodedText(n.Title);
writer.RenderEndTag();
// Recursion, if any
// Snip off the recursion at this level if specified by depth
// Use a negative value for depth if you want to render the entire sitemap from the starting node
if ((currentDepth < depth) || (depth < 0))
{
if (hasChildNodes)
{
// Recursive building starts here
// Open new ul tag for the child nodes
// "<ul class='ChildNodesContainer {0} Level{1}'>";
writer.AddAttribute(HtmlTextWriterAttribute.Class, "Level" + currentDepth.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
// BuildMenuLevel calls itself here to
// recursively traverse the sitemap hierarchy,
// building the menu as I go.
// Note: this is where I increase the currentDepth variable!
BuildChildMenu(currentDepth + 1, depth, n, writer);
// Close ul tag for the child nodes
writer.RenderEndTag();
}
}
It wouldn't be fun to re write with TagBuilders. As it stands, it renders any type of menu, including the "Incremental Navigation" as described in my 4guysfromrolla article:
Implementing Incremental Navigation with ASP.NET
The Options:
I guess I could return an empty MvcHtmlString, but that is pretty much the definition of a hack...
The only alternative is to head off into the sunset and rewrite the helper using the TagBuilder to build each tag, add that to a StringBuilder, then build the next tag, etc, and then use the StringBuilder instance to create the MvcHtmlString. Seriously ugly, unless I could do something like...
The Question:
Is there a way to:
Stop the HtmlTextWriter rendering to the stream and instead use it like a StringBuilder that at the end of the process I use to create an MvcHtmlString (or HtmlString)?
Sounds unlikely, even as I write...
PS:
The great thing about the HtmlTextWriter is that you can build large quantities of tags, instead of building them one by one as with a TagBuilder.
Contrary to the responses you received for your other question Razor does not require that you return an HtmlString. The problem with your code right now is that you are writing directly to the response stream. Razor executes things inside-out which means that you can mess up the response order (see a similar question).
So in your case you could probably do this (though i haven't tested it):
public static void Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
}
Edit (follow up to address your comments):
Html Helpers are perfectly capable of either returning a HtmlString directly or void and writing to the context writer. For example, both Html.Partial and Html.RenderPartial work fine in Razor. I think what you are confusing is the syntax required to call one version and not the other.
For example, consider an Aspx view:
<%: Html.Partial("Name") %>
<% Html.RenderPartial("Name") %>
You call each method differently. If you flip things around, things will just not work. Similarly in Razor:
#Html.Partial("Name")
#{ Html.RenderPartial("Name"); }
Now it just so happens that the syntax to use a void helper is a lot more verbose in Razor compared to Aspx. However, both work just fine. Unless you meant something else by "the issue is with a html helper not being able to return void".
By the way, if you really want to call your helper using this syntax: #Html.Theseus() you could do this:
public static IHtmlString Theseus(this HtmlHelper html)
{
var writer = new HtmlTextWriter(html.ViewContext.Writer);
...
return new HtmlString("");
}
But that's a bit of a hack.