Get referring view in an Action - MVC2 - asp.net-mvc

This may be more of a best practice question.
I have three views (create/details/edit) that all link to their own results view (createResults/detailsResults/editResults). Each results view shares a partial with a results table on it.
When a user submits one of the three (c/d/e) views, should each results view have its own action, even tho the action will quite literally do the exact same thing (search on the information on the c/d/e view)? I'd rather not duplicate this code if not necessary.
Should I have one action, and pass in something that tells the action which results view to direct to? Is there an easy way to get the referring view in the action?

If you have 3 actions you don't need to duplicate code. Why not refactor the common code into a single private method on the controller, or perhaps even move it into an action filter?

I would make a single action with a string parameter containing the view name.
You can play with the routing table to make the URLs prettier.

Related

When should we implement a custom MVC ActionFilter?

Should we move logic that supposes to be in Controller (like the data to render the partial view) to ActionFilter?
For example, I'm making a CMS web site. There should be a advertisement block to be rendered on several pages but not all the pages. Should I make an ActionFilter attribute like [ShowAd(categoryId)] and decorate the action methods with this attribute?
The implementation of this controller would include service calls to retrieve information from database, buildup view models and put in the ViewData. There would be a HtmlHelper to render the partial view using the data in ViewData if it exists.
That just seems yucky to me.
When I'm trying to figure out whether I need an ActionFilter, the first question I have is, Is this a cross-cutting concern?. Your particular use-case doesn't fit this, at first blush. The reason is, is that an ad is just another thing to render on a page. There's nothing special about it that makes it cross-cutting. If you replaced the word 'Ad' with 'Product' in your question, all the same facts would be true.
So there's that, and then there's the separation of concerns and testability. How testable are your controllers once you have this ActionFilter in place? It's something else you've got to mock out when testing, and what's worse is that you have to mock out those dependencies with every controller you add the ActionFilter to.
The second question I ask is, "How can I do this in a way that seems most idiomatic in the platform I'm using?"
For this particular problem, it sounds like a RenderAction and an AdController is the way to go.
Here's why:
An Ad is its own resource; it normally isn't closely tied to anything else on the page; it exists in its own little world, as it were.
It has its own data-access strategy
You don't really want to repeat the code to generate an Ad in every place you could use it (which is where a RenderPartial approach would take you)
So here's what such a beast would look like:
public AdController : Controller
{
//DI'd in
private AdRepository AdRepository;
[ChildActionOnly]
public ActionResult ShowAd(int categoryId)
{
Ad ad = Adrepository.GetAdByCategory(categoryId);
AdViewModel avm = new AdViewModel(ad);
return View(avm);
}
}
Then you could have a custom partial view that is set up around this, and there's no need to put a filter on every action (or every controller), and you don't have try to fit a square peg (an action filter) in a round hole (a dynamic view).
Adding an Ad to an existing page then becomes really easy:
<% Html.RenderAction("ShowAd", "Ad" new { categoryId = Model.CategoryId }); %>
If your ad system is simple enough, there is no reason you could/should not use an action filter to insert enough info into the view data to generate the ad in your view code.
For a simple ad system, say.. a single ad of a specific category shows up in the same place in the layout on every page and that's it, then there is no real argument of a better way except to prepare for future changes to the system. While those concerns may be legitimate, you may also have it on good authority that requirement will never change. But, even if requirements do change, having wrapped all the code that generates ads in one place is the most important aspect and will save you much more time up front than a more robust solution might. Obviously there are more than a few ways to wrap this code in a single place.
As for the way you are choosing to do it, I would keep your action filter cleaner to only have it insert the category into the view data and have all the magic happen inside your html helper which would take the category in as a parameter. Building up view models to shove into the view data is going to require a bit of extra work, and put code all over the place when it doesn't need to be there. Keep it simple and do all of the html generation inside of the html helper which is responsible for...building html.

How can I find out the name of the controller action that called my view in MVC3?

I would like to code some logic into my views that depends on the name of the controller action used to call the view. Is there a way I can find out this name.
Hope somebody can help me with that. Please note that it's MVC3 I am using.
Get the name of the controller
#ViewContext.Controller.ValueProvider.GetValue("controller").RawValue
Get the name of the action
#ViewContext.Controller.ValueProvider.GetValue("action").RawValue
I found that here.
#ViewContext.RouteData.Values["Controller"]
#ViewContext.RouteData.Values["Action"]
While this works, I'd suggest it's a little inelegant. Personally I'd add these options as flags to a ViewModel and pass that to my View.
ViewContext.RouteData.Values["action"] may be used, but it is bad choise to let view decide such things. You could use display and editor templates to generate different views and then let action choose its view. Views should be very simple and rely on data that that receive via ViewData or their model. Best to let controller decide such things as differenciate some views with action

with mvc.net is it possible to share a view between multiple actions

This is a noob question, but I will ask it anyway...
I'm wanting to create a page that will do basic CRUD operations on a list of items:
-display the list
-edit an item
-create an item
-delete an item
It is looking like I will need an action for each of this operations. This is good and understandable. My question is regarding the views for interacting with the user.
I want to have in-place editing, so the user clicks on edit and they can edit the details of the item in the list. In my current understanding, i will have to duplicate a great deal of the view between 'display the list' and 'edit an item'. however, this seems to be unnecessary redundancy and will make future updates more time-consuming as I will have to update each view.
Is there an easier way? Am I on the right/wrong track? Any other comments?
Yes, absolutely. You'll want to use the overload of View() that takes a string. The string is the name of the view to render:
public ActionResult MyAction()
{
return View("MyViewName");
}
The View() method can take the name of a view as a parameter, so you can render the same view from several actions. By default (if you don't specify a view name) the framework uses a view named as the current action. See here for details.
I believe what you should be looking into is rendering partial views, which are .ascx pages similar to UserControls in WebForms. They're basically shared partial views that you can use for the same purposes across many views.
If you look in the default project template you can find examples for items such as the Login control.
Edit: And as others have noted, you can also share views between actions. Had my own noob moment there too. :)

ASP.NET MVC - switching the VIew to be used dynamically

What is the correct way to dynamically change the View (the view aspx) that a controller method uses versus using the standard naming convention.
I'm guessing it has something to do with the ViewResult and ViewName, but what is the correct syntax?
Update:
One thing I forgot... is there a way to do this without having the "action" or method name not be part of the resulting URL?
For example,
If I wanted to have a list of all the states in the USA.
http://localhost/list/states
(displays a simple list of state names)
And If I wanted to have a list of the 50 largest cities in the USA.
http://localhost/list/largest-cities
(displays the city and the population - two column grid)
So I'd like to be able to pull in different "formatters" depending on the list name.
Should I do that in a single action / multiple views?
Could I then use Routes to hide the View name in the URL?
What is the best way to approach this?
Just use the method that takes the name of the view to choose. Be careful, though. Most times what you probably want to do is redirect to a different action instead. Returning a different view won't change the url like redirecting will.
string name = ...figure out which view you want...
return View( name );
If you need to render a different view from a controller action dynamically, you can simply supply a value to the base.View() method (or the ViewResult constructor). The location of the view will always be (for the web forms view engine):
/Views/{Controller}/{View}.aspx
Edit: (Thanks to Ithi) It could also be in:
/Views/Shared/{View}.aspx

ASP.Net MVC and RenderPartial w/ relative paths

I've been playing around with ASP.NET MVC and had a question. Or maybe its a concern that I am doing this wrong. Just working on a lame site to stretch my wings a bit. I am sorry this question is not at all concise.
Ok, here's the scenario. When the user visits home/index, the page should show a list of products and a list of articles. The file layout is such (DAL is my data access layer):
Controllers
Home
Index
Views
Home
Index inherits from ViewPage
Product
List inherits from ViewUserControl<IEnumerable<DAL.Product>>
Single inherits from ViewUserControl<DAL.Product>
Article
List inherits from ViewUserControl<IEnumerable<DAL.Article>>
Single inherits from ViewUserControl<DAL.Article>
Controllers.HomeController.Index produces a View whose ViewData contains two entries, a IEnumerable<DAL.Product> and a IEnumerable<DAL.Article>.
View.Home.Index will use those view entries to call:
Html.RenderPartial("~/Views/Product/List.ascx", ViewData["ProductList"])
and Html.RenderPartial("~/Views/Article/List.ascx", ViewData["ArticleList"])
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("Single", product);
View.Article.List does something similar to View.Product.List
This approach fails however. The approach makes sense to me, but maybe someone with more experience with these MVC platforms will recognize a better way.
The above produces an error inside View.Product.List. The call to Html.RenderPartial("Single",...) complains that "Single" view was not found. The error indicates:
The partial view 'Single' could not be found. The following locations were searched:
~/Views/Home/Single.aspx
~/Views/Home/Single.ascx
~/Views/Shared/Single.aspx
~/Views/Shared/Single.ascx
Because I was calling RenderAction() from a view in Product, I expected the runtime to look for the "Single" view within Views\Product. It seems however the lookup is relative the controller which invoked the original view (/Controller/Home invoked /Views/Product) rather than the current view.
So I am able to fix this by changing Views\Product, such that:
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("~/Views/Product/Single.ascx", product);
instead of
View.Product.List will call
foreach(Product product in View.Model)
Html.RenderPartial("Single", product);
This fix works but.. I do not understand why I needed to specify the full path of the view. It would make sense to me for the relative name to be interpreted relative to the current view's path rather than the original controller's view path. I cannot think of any useful case where interpreting the name relative to the controller's view instead of the current view is useful (except in the typical case where they are the same).
Around this time I should have a question mark? To emphasis this actually is a question.
Because I was calling RenderAction()
from a view in Product
...
I do not understand why I needed
to specify the full path of the view.
It would make sense to me for the
relative name to be interpreted
relative to the current view's path
rather than the original controller's
view path
The part I think you're misunderstanding is the "execution location" for lack of a better or official term. Paths are not relative to your view, not even your "controller's view" as you put it. They are relative to your request URL, which defines a controller context. I may not be saying it very well, but if you spent a little time in Reflector looking at how URLs and routes are resolved, I think this would all fall into place in your head.
[edit:
I was thinking, you have 2 cases:
the Home controller is the only one that ever references Product / Articles List user control
the user controls are shared by several controllers
In the first case, the view user controls really belong to the home controller and it makes sense to put them in the home controller folder. In the second case, it makes sense to place them in the shared folder since they will be shared by controllers.
In either case, maybe you can place them in a sub folder. Like Views/Home/Products and then try RendarPartial("Product/Single") and see what happens? I don't know if it would try to resolve it to: Home/Product/Single and then Shared/Product/Single or not. If sub folders work, it seems to allow the logical seperation of Product and Article, while showing that they are still members of either the Home controller or Shared by all controllers.
]
Check out this blog entry by Steve Sanderson:
http://blog.codeville.net/2008/10/14/partial-requests-in-aspnet-mvc/
What you are doing isn't wrong, but it does seem to sort of go against the convention of View/Controller folder names. That said, it makes sense to want to define controller-agnostic view user controls and nesting them seems valid. So I dunno!
Anyways, the link just describes a method of instead of using RenderPartial to render a use control, it defines a method of RenderPartialRequest that renders the return value (in your case a user control) of a controller action. So you could add a Product and Articles controller with an Action List that returns your user control, and then call those two actions from the Home/Index view. This seems more intuitive to me, but just an opinion.
He also mentions subcontrollers from MVC Contrib, and I'm pretty sure there is desire for something like this to be a part of ASP.NET MVC release.
From looking at the MVCStoreFront sample this is how they have everything structured for calling RenderPartial
Views
Shared
ProductSingle
ProductList
ArticleSingle
ArticleList
Then render them via:
<% Html.RenderPartial("ProductSingle", ViewData["ProductList"]); %>
<% Html.RenderPartial("ProductList", product); %>
<% Html.RenderPartial("ArticleSingle", article); %>
<% Html.RenderPartial("ArticleList", ViewData["ArticleList"]); %>

Resources