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

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.

Related

Calling controller action method directly from Razor View

I looked around and couldn't find an easy solution.
I've tried #GetUserName which doesn't work.
I've tried # { GetUserName which doesn't work.
There has to be an easy way to call a method from the razor view engine.
It is within a foreach loop.
I need GetUserName(item.userID)
The below code is in my controller:
[ChildActionOnly]
public string GetUserName(int userID)
{
ProPit_User user = db.ProPit_User.Find(userID);
return user.username;
}
Trying to call a controller action method directly from your view is usually a sign of bad design.
You have a few options, depending on what you are trying to do:
Avoid calling the method, instead put the data in your model, and render the model
Use Html.RenderAction
Put the method in another class and use normal function syntax.
(1) is usually my default approach, often incorporating DisplayTemplates and EditorTemplates
For (3), e.g.
public static class Util
{
public string MyUtilMethod(int blah)
}
And the view:
#Util.MyUtilMethod(1)
Although you can obtain the controller instance from your view, doing so is plain wrong as it violates the whole MVC (and MVVM) paradigm, as the view should not be aware of its controller.
(The only possible reason I can think of where this would be useful would perhaps be for testing the controller from a mocked view, although even here, it should be possible to test the exposed controller functionality directly from unit tests):
#{
var controller = ViewContext.Controller as MyController;
var userName = controller.GetUserName(123);
}
The better way to have arrived at this result is for the controller to pre-populate, and pass all the data needed by the View, such as the userName, to a custom ViewModel (as typed by the #model directive at the top of the Razor page), or to the ViewBag dynamic.

Custom type-dependent template loader

As you might know, ASP.NET MVC has support for custom view overrides for model fields within views. There are special folders in the Views folder called Views\Shared\EditorTemplates, Views\Shared\DisplayTemplates and so on, and these folders can contain files like Views\Shared\EditorTemplates\String.cshtml, which will override the default view used when calling #Html.EditorFor in a view with a model with a String field.
What I want to do is to use this functionality for a custom kind of templates. I want to have a folder like Views\Shared\GroupTemplates that may contain e.g. Views\Shared\GroupTemplates\String.cshtml and Views\Shared\GroupTemplates\Object.cshtml, and I want to create a HtmlHelper method that allows me to call for example Html.GroupFor(foo => foo.Bar), which will load the template in String.cshtml if Bar is a String property, and the template in Object.cshtml otherwise.
Full example of the expected behavior; if Views\Shared\GroupTemplates\String.cshtml contains this:
#model String
This is the string template
... and Views\Shared\GroupTemplates\Object.cshtml contains this:
#model Object
This is the object template
I have a model like:
class Foo
{
public bool Bar { get; set; }
public String Baz { get; set; }
}
And a view in Views\Foo\Create.cshtml like:
#model Foo
#Html.GroupFor(m => m.Bar)
#Html.GroupFor(m => m.Baz)
When I render the view Create.cshtml, the result should be this:
This is the object template
This is the string template
How should GroupFor be implemented?
The thing is that you can easily specify your view location like that
html.Partial("~/Views/Shared/GroupTemplates/YourViewName.cshtml");
or even override default behaviour by implementing custom view engine, for an example see this blog A Custom View Engine with Dynamic View Location
But you also want to reuse the logic which determines the view name based on its model type. So that if a view with String name doesn't exist an Object view is picked up. Which means going through parent classes.
I've had a look how EditorFor is implemented:
public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
return html.TemplateFor<TModel, TValue>(expression, null, null, DataBoundControlMode.Edit, null);
}
It uses TemplateFor method which is internal and you can't just reuse it.
So I can only see of two options:
Implement you custom logic by checking if a view file with a correct name exists by trying model type name and its parent classes. And if you find a proper view just use Partial extension in your helper.
Try to use reflection to call internal method. But this approach is more like a hack than a solution.
Hope it helps!

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

Passing data to Master Page in ASP.NET MVC

What is your way of passing data to Master Page (using ASP.NET MVC) without breaking MVC rules?
Personally, I prefer to code abstract controller (base controller) or base class which is passed to all views.
If you prefer your views to have strongly typed view data classes this might work for you. Other solutions are probably more correct but this is a nice balance between design and practicality IMHO.
The master page takes a strongly typed view data class containing only information relevant to it:
public class MasterViewData
{
public ICollection<string> Navigation { get; set; }
}
Each view using that master page takes a strongly typed view data class containing its information and deriving from the master pages view data:
public class IndexViewData : MasterViewData
{
public string Name { get; set; }
public float Price { get; set; }
}
Since I don't want individual controllers to know anything about putting together the master pages data I encapsulate that logic into a factory which is passed to each controller:
public interface IViewDataFactory
{
T Create<T>()
where T : MasterViewData, new()
}
public class ProductController : Controller
{
public ProductController(IViewDataFactory viewDataFactory)
...
public ActionResult Index()
{
var viewData = viewDataFactory.Create<ProductViewData>();
viewData.Name = "My product";
viewData.Price = 9.95;
return View("Index", viewData);
}
}
Inheritance matches the master to view relationship well but when it comes to rendering partials / user controls I will compose their view data into the pages view data, e.g.
public class IndexViewData : MasterViewData
{
public string Name { get; set; }
public float Price { get; set; }
public SubViewData SubViewData { get; set; }
}
<% Html.RenderPartial("Sub", Model.SubViewData); %>
This is example code only and is not intended to compile as is. Designed for ASP.Net MVC 1.0.
I prefer breaking off the data-driven pieces of the master view into partials and rendering them using Html.RenderAction. This has several distinct advantages over the popular view model inheritance approach:
Master view data is completely decoupled from "regular" view models. This is composition over inheritance and results in a more loosely coupled system that's easier to change.
Master view models are built up by a completely separate controller action. "Regular" actions don't need to worry about this, and there's no need for a view data factory, which seems overly complicated for my tastes.
If you happen to use a tool like AutoMapper to map your domain to your view models, you'll find it easier to configure because your view models will more closely resemble your domain models when they don't inherit master view data.
With separate action methods for master data, you can easily apply output caching to certain regions of the page. Typically master views contain data that changes less frequently than the main page content.
EDIT
Generic Error has provided a better answer below. Please read it!
Original Answer
Microsoft has actually posted an entry on the "official" way to handle this. This provides a step-by-step walk-through with an explanation of their reasoning.
In short, they recommend using an abstract controller class, but see for yourself.
Abstract controllers are a good idea, and I haven't found a better way. I'm interested to see what other people have done, as well.
I did some research and came across these two sites. Maybe they could help.
ASP.NET MVC Tip #31 – Passing Data to Master Pages and User Controls
Passing Data to Master Pages with ASP.NET MVC
I find that a common parent for all model objects you pass to the view is exceptionally useful.
There will always tend to be some common model properties between pages anyway.
The Request.Params object is mutable. It's pretty easy to add scalar values to it as part of the request processing cycle. From the view's perspective, that information could have been provided in the QueryString or FORM POST. hth
I thing that another good way could be to create Interface for view with some Property like ParentView of some interface, so you can use it both for controls which need a reference to the page(parent control) and for master views which should be accessed from views.
The other solutions lack elegance and take too long. I apologize for doing this very sad and impoverished thing almost an entire year later:
<script runat="server" type="text/C#">
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
MasterModel = SiteMasterViewData.Get(this.Context);
}
protected SiteMasterViewData MasterModel;
</script>
So clearly I have this static method Get() on SiteMasterViewData that returns SiteMasterViewData.

Categories