Working of HTML/AJAX Helpers in .Net MVC - c#

I think this might be pretty obvious for others but I cannot understand the working of these type of method overloads.
The ones in which we only pass action method name.
How does these #Html.Helpers get the expected controller?
My guesses:
That particular Helper extract it from the HttpContext
this HtmlHelper htmlHelper parameter holds all the information related
to the request.
I am trying to create a custom helper in which user should pass the name of the action method and it magically gets its respective controller.
When I checked the route-values in this HtmlHelper htmlHelper during run-time they showed me this-
{controller, Home}
{action, Index}
{id, null}
When I checked the HttpContext during run-time, it was null.
Expected output should be-
{controller, Device}
{action, Suggest}
{id, null}
If someone can provide behind the curtains working of default #Html.Helpers with examples that will be great.
UPDATE
I did find what I was looking for.
Here is the link to the question and its answer

All Html helpers have access to the HtmlHelper class. That reason is that these helpers are nothing else but extension methods of the HtmlHelper class.
public static class HelperExtensions
{
public static string ControllerName(this HtmlHelper helper)
{
string name = helper.ViewContext.Controller.GetType().Name
return String.Format("<span'>{0}</span>", name);
}
}
}
So everything regarding the current request is there.
View.cshtml
#Html.ControllerName()

Related

How to get syntax highlighting of controllers and actions on HtmlHelper extension method?

How can I get Visual Studio to provide controller and action syntax highlighting like it offers for its own object for controller and action strings in the following?
When you use:
Html.Action("Index", "Home");
Visual Studio provides additional highlighting on the controller and action, it even tries to detect which ones are available and will present it as an option.
Notice in the image below how it give me options for the actions based upon the controller that I've entered. This is what I would like to happen for my custom code.
I have perfectly working HtmlHelper extension method similar to the following:
public static MvcHtmlString Action(this HtmlHelper htmlHelper, string actionName, string controllerName, string myAwesomeField) {
return Action(htmlHelper, actionName, controllerName, null /* routeValues */, myAwesomeField);
}
My extension methods are getting picked up by intellisense in the regard that it knows I need a few strings. When you look at Microsoft's code, the parameters for action and controller are strings, but Visual Studio knows whether those controllers and actions exists and provide additional intellisense support. How does the tooling pick up on it?
If you are using Resharper (of course you are) then here's a perfect solution that I had working in 5 minutes:
Follow the instruction here at the Jetbrains blog.
I elected for option 2 of having the annotations source code in a file named jetbrains.Annnotations.cs included in my project. Then simply by adding the parameter attribute decorations as below (in my method WithLink) everything just works.
public void WithLink([AspMvcAction] string action, [AspMvcController] string controller)
Nice
You have to include the namespace where your extension lives either in web.config of Views folder or in the particular view itself (by #using) to get the intellisense support.

HtmlHelper extension to find view name

I'm trying to create a HtmlExtension to retrieve the name of the current view.
However, I don't want to have the requested view (e.g. "LogOn" for "/Account/LogOn") but the actual file that is being processed (e.g. "_Layout").
The closest I could find was html.ViewDataContainer.ToString() which returns {ASP._Page_Views_Shared__Layout_cshtml} for example, but I don't think that parsing this would be a great idea.
Is this information available in the Html Extension?
Thanks in advance.
You can create an extension like this,
public static string ViewName(this WebViewPage page)
{
return Path.GetFileNameWithoutExtension(page.VirtualPath);
}
then from a razor view,
The view name is: #this.ViewName()
or without extension,
The view name is: #Path.GetFileNameWithoutExtension(VirtualPath)

Reducing code duplication using HTML helpers

I have a fairly simple data audit web application written with ASP MVC which effectively has two views of the same model for different purposes.
Agent view - The form filled out by the person validating information the information. Each field on the form in this view has 3 subfields:
a. Original Value - The value from the database before the call was made
b. New Value - The value provided by the person on the phone if it differs from the original.
c. Action - A general indication of what happened
QC View - The form filled out by someone who reviews the work performed in Agent view. Each field on the form in this view has 5 subfields:
a. Original Value - Same as above
b. Agent Value - The value provided in 1b above by the agent.
c. QC Value - The corrected "New Value" if the value specified by the agent is incorrect.
d. Agent Action - Same as above, except read only in this view
e. QC Action - The corrected "New Action" if improperly chosen by the agent.
The only differences between the two views are the subfields available. I'd like to be able to use a single view to represent both views since the overall structure of the pages is identical, and just use HTML helpers to handle the differences in subfields. What I have so far are 2 distinctly separate series of helpers (currently in the same class though could be separated):
// Agent controls
public static MvcHtmlString AuditControl(this HtmlHelper htmlHelper, string id, string fieldLabel, MvcHtmlString editControl, string cssClass)
public static MvcHtmlString AuditControl(this HtmlHelper htmlHelper, string id, string fieldLabel, string editControl, string cssClass)
public static MvcHtmlString AuditControl<COMPLEX>(this HtmlHelper htmlHelper, string id, string fieldLabel, string cssClass) where COMPLEX : AbstractComplex, new()
// QC controls
public static MvcHtmlString ReviewControl(this HtmlHelper htmlHelper, string id, string fieldLabel, MvcHtmlString editControl, string cssClass)
public static MvcHtmlString ReviewControl(this HtmlHelper htmlHelper, string id, string fieldLabel, string editControl, string cssClass)
public static MvcHtmlString ReviewControl<COMPLEX>(this HtmlHelper htmlHelper, string id, string fieldLabel, string cssClass) where COMPLEX : AbstractComplex, new()
Where the third implementations handle the more complex fields composed of multiple pieces of data (like Full name, Address, etc).
One possible solution that I've considered is to separate the different types of controls into different classes which implement a common interface and then pass them as type parameters to more generic HTML helpers. I think this would work but then I'd somehow need to be able to tell the view which implementation it should use to draw the view, which seems problematic because it seems to blur the line between View and Controller.
One less appealing approach that seems obvious is to pass a sort of admin flag from the controller which would be used by a generic (in logic not meaning type generic) factory helper and build the logic in it to know which series of methods to use. This would keep the model and view separate, but feels dirty because then the HTML helper would become responsible for more than just building the HTML.
Is this a reasonable situation to break the separation of concerns as designed by MVC or is there a more appropriate solution?
Since you are using MVC3, I would recommend using child actions for the sub fields:
http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx
Child actions allow you to execute an action on a controller inside of your view, this would be a much cleaner approach.
I was able to implement (my interpretation of) the advice provided by #SoWeLie fairly simply. It involved creating a new Model to house a superset of the possible control properties and a new view to be drawn for each different control set (one for Audit, and one for Review). The problem with it was the resulting View API was ugly:
#Html.RenderAction("DrawControl", new { id = "ID" ... })
// Repeated for all of the overloads of DrawControl
and each Controller action contained something like:
public ActionResult DrawControl(string id, ...)
{
// FieldControl being the name of my Model
var viewModel = new FieldControl() { ID = id, ... };
if (shouldRenderAudit)
return PartialView("AuditControl", viewModel);
else
return PartialView("ReviewControl", viewModel);
I couldn't figure out how to get my generic helper to work in this scenario, and besides, I wanted to remove reduce obvious code duplication so this quickly became:
#functions {
public string DrawControl(string id, ...)
{
return Html.Render("DrawControl", new { id = "ID" });
}
// Repeated for all of the overloads of DrawControl
}
#DrawControl("ID", ...)
With the same controller action. The problem with this (ignoring the fact that the View had functions at all) was that the #functions block had to be included in any view that wanted the benefit of using them (which is currently only 2 but will soon enough balloon to 5 and who knows what my predecessor is going to do with this). I quickly reworked the code again, this time to bring back the helpers (generally keeping the views, model, and controller changes) and finally ended up with this:
View:
#(Html.DrawComplexControl<ProviderName>("id", ...))
#Html.DrawSimpleControl("id", ...)
Controller:
// One common action that is used to determine which control should be drawn
public ActionResult DrawControl(FieldControl model)
{
if (shouldRenderAudit)
return PartialView("AuditControl", model);
else
return PartialView("ReviewControl", model);
}
Helper:
public static MvcHtmlString DrawControl(this HtmlHelper htmlHelper, string id, ...)
{
var model = new FieldControl() { ID = id, ... };
return htmlHelper.Action("DrawControl", model);
}
public static MvcHtmlString DrawSimpleControl(this HtmlHelper htmlHelper, string id, ...)
{
return DrawSimpleControl(htmlHelper, id, ...);
}
public static MvcHtmlString DrawSimpleControl(this HtmlHelper htmlHelper, string id, ...)
{
// Set some defaults to simplify the API
return DrawControl(htmlHelper, id, ...);
}
public static MvcHtmlString DrawComplexControl<T>(this HtmlHelper htmlHelper, string id, ...) where T : AbstractComplex, new()
{
// Build the required controls based on `T`
return DrawControl(htmlHelper, id, ...);
}
Of course there were about a half dozen other iterations between the ones shown to help the situation, and none of them did to the extent necessary. I'm sure there are improvements to be made yet, but this is what I have so far.
Doing it this way provides a very simple API for the View to use without it having to know or care about implementation and it can satisfy all of the requirements of my pre-existing API with only minor modification (in the end at least). I'm not sure if this is what the answer intended as a result but it is functional and provides the simplicity necessary.
Hopefully my headaches will help someone else in the future.

ASP.NET MVC3 Import view content programatically on an extension method

I'm building a state machine which display different action controls (partial views) on the page according to some dynamic value.
I started writing HtmlHelper Extension methods to ouput the proper html for each state. Something like:
#if(Model.state == "NEW") {
Html.RenderActionEdit()
Html.RenderActionDelete()
}
And to do this I was doing simple methods in the form:
return MvcHtmlString.Create("<form><input>..... </form>");
But this is rather cumbersome for large bits of html. So, the question is, would it be possible to write this Html on separate views (cshtml files) and then somehow load them and pass the result to MvcHtmlString? Like
return MvcHtmlString.Create(View.Load("EditAction.csthml"));
I couldn't find a way to load an existing view and then just "include" it on the partial method's output.
Many thanks for any help!
There are a couple of ways to do this:
#Html.RenderPartial("thepartial.cshtml", model); will pass model to the partial view, and render it. There are a couple of other versions too.
#Html.Action("action", "controller", id) (see msdn) will pass id to the specified action method, and render the view it outputs. This is very convenient if you don't have the model object needed for the partial available in your main view.
In an extension method on HtmlHelper, you could use it like this:
public HtmlString YourContent(this HtmlHelper helper)
{
return helper.Action("action", "controller", new { id = 1 });
}
which in your view would be used by calling #Html.YourContent().

Is it applicable to place methods inside ASP.NET MVC views?

The current code of one of the views in my project is a large, monolithic one and I just want to organize it. Is it just fine to place methods inside my ASP.NET MVC view?
I would say definitely not. The purpose of MVC is to separate the concerns i.e. Model - View - Controller. In your case you are mixing Model/Controller logic with your View.
If you need to pass complex content into your view you should create custom ViewData classes, populate them in your controller and pass them into your View.
If your methods are more relating to generating View markup you should look at splitting it up into partial views or as already suggested using helper extension methods.
It's possible but if you need methods inside a view, perhaps you should instead consider extending the Html object with an extension method, and then use these methods from inside the view.
I like to separate my extensions by functionality to keep the view readable.
For example:
public static MySpecialDateHelper SpecialDateHelper(this HtmlHelper helper)
{
return new MySpecialDateHelper(helper);
}
public class MySpecialDateHelper
{
// Fields
private HtmlHelper m_helper;
private StringBuilder m_sb = new StringBuilder();
// Methods
internal MySpecialDateHelper(HtmlHelper helper)
{
this.m_helper = helper;
}
// Print date prettily
public public string PrettyDate(datetime target)
{
// format however
return string.format("pretty {0}", target.ToString());
}
// Print date prettily
public public string PrettyDateInATextbox(datetime target)
{
return m_helper.TextBox("prettyid",PrettyDate(target));
}
// etc...
}
Then in your view, you just have to reference the extension you just created
<%= Html.SpecialDateHelper.PrettyDateInATextbox(Now); %>
Of course, your separation of methods into extensions may vary, you can also extend the HtmlHelper object with the methods directly, but i prefer this method
No.
Move it the controller, if necessary introduce view models. After the that, define your own Html helpers, considering those should be for html like stuff, not for any logic.
You could have something in a view that's v. v. specific to it, and its just not possible that bit of code would do anything outside of it. But even then, you can just define it in a helper, as long as the name is very clear on what it does, you are better than putting it in the view. You can unit test helpers, so there is a clear advantage when doing that.

Categories