Html Extension methods are not accessible in MVC Partial Pages - c#

Hi I'm trying to create a declarative Html helper method inside MVC partial page, everything works fine. But when I want to use built-in Html extension methods, I see there are no extension methods. Also I checked my view's webconfig file to add System.Web.Mvc.Html namespace. everything is OK, but I don't know why it's not working.
Any advice will be helpful.
Edit : Here is my code :
#using WebVoter.ViewModel
#using System.Web.Mvc.Html
#helper GetVoteList(IList<VoteQuestionViewModel> voteQuestionList)
{
<div class="head-panel">
#*For example ActionLink is not accessible here*#
#Html.ActionLink(....);
</div>
}

Inside Razor helpers you do not have reference to HtmlHelper. You need to pass it as parameter from the calling code:
#helper GetVoteList(HtmlHelper html, IList<VoteQuestionViewModel> voteQuestionList)
{
<div class="head-panel">
#html.ActionLink(....)
</div>
}
and when you want to call this helper from some view you need to pass the reference:
#GetVoteList(Html, ...)
Personally I've always preferred writing extension methods to the HtmlHelper class instead of using those inline Razor helpers. They would then be used as standard helpers:
#Html.GetVoteList(...)
The advantage is that you are no longer tied to Razor. Extension methods are view engine agnostic and make transition to other view engines less painful because they are C# code. Another benefit is that they can be unit tested.

There is a web.config under Views folder. Can you modify this config too?

Related

Creating partial view with dynamic content

I understand when I create a view, I shouldn't be putting any code in there besides html and the data from the model/controller, which is what I've done so far.
But lets say there is a snipped of dynamically generated html that can be used in multiple views, I'm guessing this would be a partial view that goes in the Shared folder in the project. But since it's a partial view, that has no absolute controller to handle it's propagation of dynamic data (from db), how would I call, and where would I code the propagation of data from the db into the view (or model?), if lets say the partial view was to dynamically render content for table.id=n, etc.
I'm fairly to new and working off a tutorial in .net, trying to figure out how to do this. Anyone know how it's done? Hope the question makes sense.
You can always define a model for the partial.
And you can render the partial from the container view passing a dinamically populated instance of its model:
<!-- index.cshtml -->
<h1>Feed Upload</h1>
<div id="uploader">
#Html.Partial("~/Views/Shared/Controls/_FileUploader.cshtml", new FileUploaderModel() { UploaderClassName = this.Model.UploaderClassName })
</div>
In this simple example I call the partial _FileUploader.cshtml from the index.cshtml using the #Html.Partial() method, passing a new model instance that specifies the UploaderClassName value.
Edit
The this.Model.UploaderClassName refers to the container's model and it is initialized inside the container's controller business. Of course the container's controller can run any data access logic to grab dynamic data from the db and pass them to the partial's model.
Have a look at MSDN, and at this article.
Assuming you are using the razor view engine, you can put an .cshtml file in the App_Code folder with helper functions.
The syntax is like this:
#helper FormatDate(DateTime date)
{
#date.ToShortDateString()
}
You call it like this (assuming the file is Utility.cshtml)
#Utility.FormatDate(Patient.DOB)
Because you can pass parameters to a helper, you can pass any type you need, including complex objects.
I recently published a nuget package to do this very thing. It's called Dynamic MVC.
http://dynamicmvc.com
You can look at the source code on codeplex.
https://dynamicmvc.codeplex.com
The way I did this was to use the ModelMetadata engine built into MVC to allow me get the value for any property in a weakly typed fashion. The ModelMetadata engine originally came from ASP.net Dynamic Data and was ported over to MVC in MVC2. It works great for this kind of situation.

When is it appropriate to create HTML code in the controller (or back end)

I am doing some complex logic involving loads of recursion to create a (complex) piece of HTML.
I started off doing this in the View using functions in Razor because I felt as HTML it belonged there.
But as it started getting more complex I thought I would rather do it in back-end code. Which it currently is.
It still feels a bit smellish though, and I am wondering if I should move it to the View again (which obviously clutters the view)
Which technically is more correct? When is it appropriate to use back-end code to generate HTML?
Thanks for your input.
create more granular partial views and partial models to maintain MVC pattern.
when your correctly select a model for your view, even partial, generating HTML in view is not a problem. you may end up with 20 views and 5 more models, but controller will be just selecting views and populating models which is good.
Don't do it in controller. You can extend the HtmlHelper class and do the stuff there. For example if you are using a paging helper.
Create a static class HtmlHelpers
namespace YourMvcApplication.WebUI.HtmlHelpers
{
public static class PagingHelpers
{
public static MvcHtmlString PageLinks(this HtmlHelper html,int totalPages)
{
StringBuilder result = new StringBuilder();
// do the complex logic to create dynamic html and append to
// String Builder
return MvcHtmlString.Create(result.ToString());
}
}
}
Add reference to this class in all views in web.config.
<namespaces>
<add namespace="YourMvcApplication.WebUI.HtmlHelpers"/>
</namespaces>
Use and resuse this Html Helper methods wherever required.
<div>
#Html.PageLinks(Model.TotalPages)
</div>

ASP.Net MVC3 - Pass razor markup as a parameter

I have a helper called EditableArea which provides a user with a runtime-editable div (via JS). EditableArea helper checks if an editable area (not related to MVC's Area) with the specified ID exists in the DB, if so then it renders the area's HTML, otherwise it displays the default markup specified as a parameter of the helper:
#Html.EditableArea(someId, "<p>Click to edit contents</p>")
It all works ok, but I'd like to change it so that the default markup is specified not as a string but in razor syntax, something like:
#using (Html.EditableArea(someId))
{
<p>Click to edit contents</p>
}
Or something similar, like the way #sections work in MVC3.
How can I achieve that?
I can make an IDisposable which in its Dispose closes the TagBuilder, etc., but using this approach the markup will still be rendered (I can clear the rendered contents in the Dispose() but the code block would still run unnecessarily, which I'd like to avoid).
Is there some other way to pass a razor block to the helper, which may or may not be actually rendered?
Here's an example I use to render jQuery Template markup by passing in a template Id and razor-style syntax for the template itself:
public static MvcHtmlString jQueryTmpl(this HtmlHelper htmlHelper,
string templateId, Func<object, HelperResult> template)
{
return MvcHtmlString.Create("<script id=\"" + templateId +
"\" type=\"x-jquery-tmpl\">" + template.Invoke(null) + "</script>");
}
and this would be called with
#Html.jQueryTmpl("templateId", #<text>any type of valid razor syntax here</text>)
Basically just use Func<object, HelperResult> as your parameter and template.Invoke(null) (with arguments if necessary) to render it. Obviously you can skip the call to .Invoke() to avoid rendering the "default" markup.
Just to expand on the accepted answer, as it took me quite a while to resolve a similar problem and this is the question which popped up. What I really need was a #helper, which would accept razor text, as the template should contain quite some code. I played around for a long while trying to use several versions of type #helper item(Func<object, HelperResult> input), which I found on the web, with no success. Therefore I went for an approach like:
namespace project.MvcHtmlHelpers
{
public static class HelperExtensions
{
public static MvcHtmlString RazorToMvcString(this HtmlHelper htmlHelper, Func<object, HelperResult> template)
{
return MvcHtmlString.Create(template.Invoke(null).ToString());
}
}
}
and
#project.MvcHtmlHelpers
#helper item(other input, MvcHtmlString content)
{
<div class="item">
...other stuff...
<div class="content">
#content
</div>
</div>
}
and use this via
#item(other input, #Html.RazorToMvcString(#<text>this is a test</text>))
Now I can use the helper template for both Razor input, but I can also drop in partial views, which is handy at some points. As I am no expert there might be better options, but it seems like a flexible approach to me.
In case you're wondering this is how to do it in asp.net core 3.1
#{
void TemplateFunc(Func<object, IHtmlContent> template)
{
<div>#template(null)</div>
}
}
Then in markup you can use it as
<div>
#{TemplateFunc(#<div>123</div>);}
</div>
Taking this further, it is possible to pass the markup directly to a helper, without an extension method.
#helper HelperWithChild(Func<object, HelperResult> renderChild)
{
<div class="wrapper">
#renderChild(this)
</div>
}
#HelperWithChild(#<h1>Hello</h1>)
For multi-line markup <text> is required as well:
#HelperWithChild(#<text>
#AnotherHelper()
<h1>
With more markup
</h1>
</text>)
#helper AnotherHelper()
{
<p>
Another helper
</p>
}
Though I'm not sure how this will play out with Model - my helpers only use their parameters.

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

ideas on building a strongly typed class to manage URLS for asp.net mvc

I want a strongly typed url class that i will reference whenever I need a url for any links, redirecting, etc.
I don't like:
RedirectToActoin("Action", "Controller");
A site with 50+ views means any naming change is going to break allot of things.
I also want a user frienly api so I can do:
MyUrls.ControllnerName.ActionName
How can I achieve this? is it possible?
Have a look at the T4MVC project (part of MvcContrib). It gives you strongly typed helpers for actions, controllers and views.
Excerpt from the project documentation:
T4MVC is a T4 template for ASP.NET MVC apps that creates strongly typed helpers that eliminate the use of literal strings when referring the controllers, actions and views. It helps make your MVC code much more maintainable, and gives you intellisense where you normally would not have any.
Instead of this:
<% Html.RenderPartial("DinnerForm"); %>
You can write this:
<% Html.RenderPartial(MVC.Dinners.Views.DinnerForm); %>
I actually prefer an approach inspired by the MVCContrib folks, which is to utilize a custom method on the controller that uses a Lambda Expression to specify where you are wanting to go.
protected internal RedirectToRouteResult RedirectToAction<TController>(Expression<Func<TController, object>> action) where TController: Controller
{
var controllerName = typeof(TController).Name.Replace("Controller", string.Empty);
var actionName = ((MethodCallExpression)action.Body).Method.Name;
return RedirectToAction(actionName, controllerName);
}
And using it would look like this:
return RedirectToAction<HomeController>(h => h.Index());
With this approach you get the advantages of having easy to read statements and when you refactor your action names these statements get updated as well (or cause build errors if you don't use the rename refactor).

Categories