ASP.NET MVC : Good Replacement for User Control? - asp.net-mvc

I found user controls to be incredibly useful when working with ASP.NET webforms. By encapsulating the code required for displaying a control with the markup, creation of reusable components was very straightforward and very, very useful.
While MVC provides convenient separation of concerns, this seems to break encapsulation (ie, you can add a control without adding or using its supporting code, leading to runtime errors). Having to modify a controller every time I add a control to a view seems to me to integrate concerns, not separate them. I'd rather break the purist MVC ideology than give up the benefits of reusable, packaged controls.
I need to be able to include components similar to webforms user controls throughout a site, but not for the entire site, and not at a level that belongs in a master page. These components should have their own code not just markup (to interact with the business layer), and it would be great if the page controller didn't need to know about the control. Since MVC user controls don't have codebehind, I can't see a good way to do this.
Update
FINALLY, a good (and, in retrospect, obvious) way to accomplish this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace K.ObjectModel.Controls
{
public class TestControl : ViewUserControl
{
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
writer.Write("Hello World");
base.Render(writer);
}
}
}
Create a new class which inherits ViewUserControl
Override the .Render() method as shown above.
Register the control via its associated ASCX as you would in a webForm:
<%# Register TagName="tn" TagPrefix="k" Src="~/Views/Navigation/LeftBar.ascx"%>
Use the corresponding tag in whatever view or master page that you need:
<k:tn runat="server"/>
Make sure your .ascx inherits your new control:
<%# Control Language="C#" Inherits="K.ObjectModel.Controls.TestControl" %>
Voila, you're up and running. This is tested with ASP.NET MVC 2, VS 2010 and .NET 4.0.
Your custom tag references the ascx partial view, which inherits from the TestControl class. The control then overrides the Render() method, which is called to render the view, giving you complete control over the process from tag to output.
The difference between using this approach and calling Html.RenderPartial() or `Html.RenderAction()' is adding the control to a view is done with a webforms-like tag, which is not only more comfortable for designers, but keeps them from having to be aware of controller names and methods. The name of the control class is isolated to the ASCX, also making it easier to drop these in an assembly and reuse them across separate projects.
Some may say that this violates SoC, but I believe that this approach is functionally equivalent to tying a partial view and a controller together while maintaining clean markup. It should be clear, however, that it is still up to the developer to keep only presentation-related logic in the control Business and data access logic still belong in their respective layers.

I'm a little confused here.
First of all, the .NET MVC equivalent to User Controls is Partial Views. Partial Views are a convenient way of encapsulating common View functionality in a single location. You can then call a Partial View from inside another View.
Second of all, modifying a View shouldn't mean also modifying a controller. If you are required to make a change to both just because your View changed (and not the underlying data), then there's a code issue somewhere along the line.

At first glance its easy to dismiss MVC as not having the capabilities for reusable components.
Once you get the know ASP.NET MVC you'll find there are several techniques for creating rich controls and components and encapsulating aspects of MVC follow along the same pathways as encapsulating a WebForms application.
I think what you're doing is only looking at the View aspects of MVC and not how all the underlying M and C can be encapsulated and tied together. Partial Views, Render Action/Partial are just small pieces of the underlying component capabilities of MVC. There is much more richness under the covers.

a user control is just some stuff that renders html, in mvc you have html helpers and partial views and normal views (you can render them with renderaction )
Html.Helper("someStuff")
Html.RenderPartial("viewname")
Html.RenderAction<Controller>(o => o.Action());
so basically it's just the helpers
you can actually easily substitute a call to
Html.TextBoxFor(o => o.Name);
with
Html.RenderPartial("textbox", Model.Name);

Consider the following example:
My view (CustomerDetail.ascx) binds to ICustomerDetail view-model which looks like:
interface ICustomerDetail
{
string Name { get; }
Address CurrentAddress { get; }
}
I can create a partial view Address.ascx which binds to IAddress view-model
When I am creating the CustomerDetail.ascx, I can place the Address.ascx on the same surface & bind it to the oCustomerDetail.Address field
IMO - we should be composing views from multiple such smaller partial views in MVC & this is where you will see the re-usability & the power of user controls (partial views)
Now if my controller returns ICustomerDetail, I will be able to re-use the Address.ascx without any problems
HTH.

Let's take a registration page for an e-commerce site, as an example. You prompt the user for their name, password, postal information, favorite dog breed, etc. Somewhere else in the application, you also need to collect a billing address and a shipping address. To enforce DRY, you create a user control that manages the entry of the address information.
So, to further illustrate, your address class looks something like this:
public class Address
{
public string StreetAddress { get; set; }
public string City { get; set; }
...
}
Your registration class:
public class UserReg
{
public string UserName { get; set; }
public Address MailingAddress { get; set; }
...
}
Your billing and shipping addresses may descend from the Address class:
public class BillingAddress : Address
{
...
}
public class ShippingAddress : Address
{
...
}
For the following examples, I am assuming that you have added System.Web.Mvc to the namespaces section of web.config. Based on this class hierarchy, your user control will have a control tag that refers only to the Address class:
<%# Control Language="C#" Inherits="ViewUserControl<Address>" %>
Since you've done this, you simply need to pass the appropriate model reference from the page. In the User Registration page:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<UserReg>" %>
...
<% Html.RenderPartial("AddressControl", Model.MailingAddress); %>
In the billing address page:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<BillingAddress>" %>
...
<% Html.RenderPartial("AddressControl", Model); %>
In the shipping address page:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<ShippingAddress>" %>
...
<% Html.RenderPartial("AddressControl", Model); %>
I can pass the model directly from the billing and shipping page, because the class directly descends from Address. As long as the logic is in place to process the addresses correctly, you're not going to have to make many changes to the controller, if any.

Partial views are certainly not close to WebUserControls.
In WebUserControls, you can write the HTML, and its own event handlers and is also connected to the parent page. For e.g. I can create a Sign In Web user control and use it anywhere in the web application. As an independent control, it can handle all the verifications and signing and allows redirections.
You don't get that in Partial views.

Related

What is a good way to build sub views with logic in ASP.NET MVC 3

We're building a business application using Microsoft ASP.NET MVC 3.
Some views are now becoming so complex that it seems reasonable to divide them into two or more separate views, with separate controllers and models. (Reuse is another reason for wanting to build separate views.)
Which method do you recommend for achieving such separation in ASP.NET MVC?
Partial Views seems like the obvious answer, but as I see it, partial views "out of the box" has little support for separate controllers.
The best solution we've found so far is this, using the
Html.RenderAction("<view initialization action method>")
method as explained here: http://www.primaryobjects.com/CMS/Article129.aspx
This post also mentions the RenderAction method: ASP .NET MVC correct UserControl architecture
Do you see any weaknesses with this approach?
Do you recommend any better, easier ways of achieving this?
Take a "Post Detail" view. A composite view that displays both "Post Summary" and "Post Comments".
Taking the partial approach, you'd end up with:
public class PostDetailModel
{
PostSummaryModel Summary { get;set; }
PostCommentsModel Comments { get;set; }
}
and view:
<div id="post_detail">
#Html.Partial("Summary", Model.Summary)
<ul class="comment-list">
#foreach(var comment in Model.Comments)
{
<li>#Html.Partial("Comment", comment)</li>
}
</ul>
</div>
This means that the PostController.Detail method would be responsible for constructing a PostSummaryModel, constructing a PostCommentsModel and selecting which partials to use to render each.
If you then also had the following model:
public class PostListModel
{
ICollection<PostSummaryModel> Posts { get;set; }
}
You would have two actions responsible for constructing a PostSummaryModel and knowing which partial to use. If your application isn't properly structured, this might lead to duplicate data access/model mapping code. But if you delegate and abstract model construction into re-usable model factories (that could be called by both actions) you minimise this risk.
One the other hand, taking the Html.Action approach your model simply becomes:
public class PostDetailModel
{
int PostId { get;set; }
}
and view:
<div id="post_detail">
#Html.Action("Summary", new { Model.PostId })
#Html.Action("Comments", new { Model.PostId })
</div>
It can then be left up the "Summary" and "Comments" actions to construct their own model and select a view.
There is however an ever so slight performance hit in choosing the Html.Action approach because ASP.NET MVC has to go through the whole process of model-binding, executing action filters, validating, etc so you probably wouldn't use Html.Action to display items in a sufficiently long list view. But for creating a composite view it can be a really clean way to stitch together half a dozen or so existing views.
I think you should go with Action or RenderAction as Darin points out if you want to have logic in your view. However, there is a "hacky" solution that you could use and that is to implement your own IViewPageActivator. Then you could have your views implement a custom WebViewPage and inject services in this WebViewPage and they will be available to your views. To summarize:
Use Partial or RenderPartial if you only need to inject the model
Use Action or RenderAction if you have some logic in your partial view
Implement IViewPageActivator and some other stuff if you really have to
UPDATE: Also, in simple scenarios helpers might do the job for you depending on what you are trying to achieve.
Take a look at client site MVC javascript framework for controlling complex UI.
In combination with jquery templating very powerfull and an option to keep an eye on ...
Backbone.js
http://documentcloud.github.com/backbone/
Kockout.js
http://knockoutjs.com/
Knockout.js vs. Backbone.js – Part 1
http://ifandelse.com/?p=61
Knockout.js vs. Backbone.js – Part 2
http://ifandelse.com/?p=70
Html.Action and Html.RenderAction are the best way to externalize some repeating complex logic in the views that requires its own controllers and models.

Problem with strongly typed partial view

I've a problem with Partial View.
I am developing a blog in asp.net mvc and I would make in my masterpage a list of categories, last post, last comments.
I think that the best solution is to use strongly typed partial view, and in each partial view pass the necessary model.
MY problem is that the model in View.. in any view (connected to the masterpage's contentplaceholder) enter in conflict with the models in partial views and I get an error like this:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Blog.Models.Articoli]' but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Blog.Models.Categorie]'.
I found on web a dirty solution that consist of to pass togheter the model of any view, some viewdata, one for every model to pass in partial view. But this solution don't respect DRY Principle..because you must repeat this code for each action!
So, my question is: Can I create a model that contain also partial view's model?
If, yes, in that way?
It Exist another solution more simple?
Thanks for help
How about the View Model Pattern?
I've created wrapper classes that are passed to my views rather than whatever object I would normally use
public class MyCreateUserView
{
public User CreatingUser { get; set; }
public MyPartialViewObject Blah { get; set; }
}
In your view write:
public ActionResult CreateUser()
{
MyCreateUserView createUser = new MyCreateUserView()
{
CreatingUser = GetUserFromSomewhere(),
Blah = GetPartialViewObject();
}
return View(createUser);
}
Then your page header looks like so:
<%# Page Language="C#" Inherits="ViewPage<MyCreateUserView>" %>
and when you render your partial write:
<% Html.RenderPartial("../MyPartialViewObject ", Model.Blah); %>
Instead of solving that with the pattern you describe (which is generally a great pattern), I solve that with calls to RenderAction and have it return a partial view. That way the code is in one place as each call to each view does not have to worry about marshalling all the data you need. If you want to see a short discussion on how to use it, I would check Haack's blog here: http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx. You can also check out the discussion on this other post here on SO: ASP.NET MVC Master Page Data

Is there an equivalent to Monorail view components for the ASP.Net MVC Framework?

I make heavy use of View Components in some of the larger applications I've built in Monorail - What is the equivalent approach in ASP.Net MVC for a view component, that can support sections etc.?
Actually you have several options to create the equivalent of a ViewComponent in ASP.NET MVC, depending in the complexity of your component. I use these two approaches which are the more mvc-ish of the options I am aware of.
1:
The simplest thing is to create a ViewUserControl and display it using Html.RenderPartial with the helper. The ViewUserControl is a simple piece of markup with no backing controller (I think you can put a codebehind file if you want).
Optionally, you can pass a model object or the entire ViewData dictionary to the view when calling RenderPartial, like this:
<% Html.RenderPartial("TopBar", model); %>
"TopBar" is an ascx page. This works anywhere, in master pages and in normal views.
2:
If you want your component to have more complicated logic or to access datasources, IoC, etc, then you can use Html.RenderAction which is an extension method found in the Microsoft.Web.Mvc assembly. I am using this out of the mvccontrib distribution. It works like this, you need to create a normal controller with all the logic you need, then create some views and all of these things become your component, for example:
public class AboutComponentController : Controller {
public IRepository Repository{ get; set; }
public ActionResult Detail() {
var lastEvent = Repository.FindAll<Auditoria>().FirstOrDefault();
return View(lastEvent);
}
}
Notice how I have a reference to an IRepository which is going to be injected with IoC (Windsor in my case) and I can do anything a normal controller would do.
Now, in any page (master or normal) where you want to use your component, import Microsoft.Web.Mvc and call Html.RenderAction with the appropriate parameters. This will create a mini mvc pipeline that creates the controller, resolves the view, etc., just like a Monorail ViewComponent. I prefer to use the lambda based variation of the method, like this:
<% Html.RenderAction<AboutComponentController>(x => x.Detail("a message"));%>
Unfortunately, the only way to pass parameters is to use the method call itself, which in turn must be unique in the controller. Still needs some work to resemble a ViewComponent.
I don't use masterpages or layouts in the views of my components since they are composition elements themselves.
Remember that when using the Webforms view engine, you can have strongly typed views if you like to have intellisense when using the Model variable in code blocks.
The beauty of this is that you can mix view engines with these approaches, I usually create the components in nvelocity and display them in aspx pages, etc.
I now there can be issues with caching of the partial views but I haven't run into any so far. I am sure there are other options (like subcontrollers in mvccontrib) but this is usually enough for simple cases. Of course you can use normal ASP.net components in your aspx view pages but that would be cheating right? hehe. I hope it helps.
Phil Haack blogged about creating areas to group controllers into sub-folders/sections similar to MonoRails.

Function in ASP.NET MVC

A function returns only one view.
what if I want to return multiple views in a function?
For example, I have this code:
Function Index() As ActionResult
Dim _news As DataTable = News.newsSelect()
Dim _announcement As DataTable = Announcement.SelectAnnouncement()
Return View()
End Function
I want to return _news and _announcement to be used in the aspx page. How would I do this?
Are you trying to show both sets at the same time? News and Announcements?
If so then why not implement either a PartialView or two PartialViews?
Then in your main view you can render them and pass the collection to the PartialViews?
There are heaps of samples on this and the one I recommend is in NerdDinner if you haven't already seen it.
I hope this helps. If you want sample code then let me know.
One simple way is just to have those two datasets sent in a ViewData element, which you can access in a field.
example:
ViewData["Elements"] = new SelectList(aElements, "Guid", "Name");
is consumed as:
<%= Html.DropDownList("Elements","Pick an element")%>
Also, I think that if you read between the lines of this blog post here you will find an elegant way of achieving what you want ;) but its a bit more involved..(only because you mentioned Views instead of just variables..
Quote:
We need to create our own implementation of IViewFactory. This
is responsible for locating and
creating an instance of an IView
(which both ViewPage and
ViewUserControl implement).
To “inject” (all you DI fans excuse me borrowing the term without
using a DI framework) our new View
Factory into every Controller we are
going to create our own
IControllerFactory implementation.
We need to configure the framework to use our new Controller
Factory.
Finally we can create two Views – an AJAX version and a pure
HTML version.
Building on that should be all you need
Good luck!
Ric
Assuming what you are trying to do is use both of those DataTables to populate some View, then my recommendation would be to create a wrapper object and then a strongly typed view based on this object.
The wrapper object would contain properties for all of the data elements that you need in order to render your view properly. In your case, it is 2 DataTable objects. I do not really know VB, so all my examples will be in C#. Here is an example of the data wrapper class...
public class IndexViewData
{
public DataTable News { get; set; }
public DataTable Announcement { get; set; }
}
You then might update the Index action in your controller as follows:
public ActionResult Index()
{
var viewData = new IndexViewData();
viewData.News = News.newsSelect();
viewData.Announcement = Announcement.SelectAnouncement();
return View(viewData);
}
Finally, you would need to create/update your view to be strongly typed. This is done by having your page inherit from the generic System.Web.Mvc.ViewPage<T> class. Just substitute the view data wrapper created earlier for T. To do this, you would set the inherits attribute of the <%# Page %> element. In your case, if we assume your root namespace is called "Foo", you might have the following page declaration in your Index.aspx view (added extra line breaks for readability):
<%# Page Title=""
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Foo.Models.MyModelType.IndexViewData>"
%>
Once you have a strongly typed view for your view data wrapper, you can access the wrapper object in your view using the Model property. Here is an example of something you could do in your Index.aspx view
<%-- Output some random data (bad example, for demonstration only) --%>
<%= Model.News[0]["title"] %><br/>
<%= Model.Anouncement[0]["body"] %>
In reality you're probably going to do something like iterate over each row of the data table. Regardless, once you create the strongly typed view, your model object, which was passed to the view in the Index method of the controller, is available in the Model property within the view.
You can find detailed tutorials at the ASP.NET MVC site

asp.net mvc partialview

It seems that my understanding of the use of partial views is not quite right.
I am trying to add a partial view which builds from a database and use the partial view within a master page.
Using the NerdDinner project (wich is great for mvc) I have added the following:
Views\Shared\dinners.ascx" :
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<NerdDinner.Models.Dinner>>" %>
<ul>
<% foreach (var dinner in Model) { %>
<li>
<%= Html.Encode(dinner.Title) %>
</li>
<% } %>
</ul>
Within "Views\Shared\Site.Master" :
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
...
<% Html.RenderPartial("dinners"); %>
The above will only currently function on the pages where the dinners object is currently availabe as a list (eg site/Dinners). Is there a way to do this in an elegant and mvc way or is this something that requires a clever compromise?
Why do you have it in your master? I would add another place holder to your master and then add it where it makes sense. I feel that master pages should be as agnostic to specifics. If you want it be on all dinner pages, just make nested master of the original one.
This is, IMO, one of the biggest limitations of ASP.NET MVC - managing shared data across multiple views (next to rendering partial ascx views to strings!). If you google or search on stackoverflow for something like 'managing shared view data asp.net mvc' you'll get a ton of results with various options, none of which are really perfect. The MVC team at Microsoft have acknowledged this as a problem and will hopefully include a standard solution in a future release.
Depending on how you manage data access, the easiest way may be to create a base Controller class and retrieve the data you need for the partial either inside the constructor or inside OnActionExecuting().
The option that I have chosen is to use the Html.RenderAction() helper method inside the MvcContrib project. It basically enables you to call an action method from your view and render the response. This isn't great because it requires your view to have yet more knowledge about controllers, but it gives an easy short-term solution that doesn't require hooking up any extra code on your part.
This tutorial on stephan Walther's site deal with this issue. If you use an abstract base class where the dinners object is populated and inherit from that, it will always be available, but you'll have to be aware that it's there always even when you don't need it ;).
One method I use is to create a helper method and use it in your Master Page.
public static void RenderDinners(this HtmlHelper helper)
{
helper.RenderAction<DinnersController>(c => c.Dinners());
}
<%# Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
...
<% Html.RenderDinners(); %>
As you can see the helper calls the Dinners Action method on the DinnersController.
public ActionResult Dinners()
{
...get dinners and put in the View
return Dinners(view);
}
partialview seems to me to be inherently flawed. It creates module coupling and breaks cohesion intentionally by definition.
I agree with Daniel, even if your control does not shows on every page, it shows on some of them, you should create your master as template only

Resources