I'm wanting to create a page that allows the users to select how they would like to view their data - i.e. summary (which supports grouping), grid (which supports grouping), table (which supports grouping), map, time line, xml, json etc.
Now each layout would probably have different use a different view model, which inherit from a common base class/view model. The reason being that each layout needs the object structure that it deals with to be different (some need hierarchical others a flatter structure).
Each layout would call the same repository method and each layout would support the same functionality, i.e. searching and filtering (hence these controls would be shared between layouts). The main exception to this would be sorting which only grid and table views would need to support.
Now my question is given this what do people think is the best approach.
Using DisplayFor to handle the rendering of the different types?
Also how do I work this with the actions... I would imagine that I would use the one action, and pass in the layout types, but then how does this support the grouping required for the summary, grid and table views.
Do i treat each grouping as just a layout type
Also how would this work from a URL point of view - what do people think is the template to support this layout functionality
Cheers
Anthony
Conceptually, the View is the part of an MVC web app that is responsible for handling the displaying of data. So, if you want to display data in different ways, it seems most logical that each "display" has its own corresponding aspx View.
All of the View Models can inherit from the same base model. So, for example, we might have four models and three views:
public abstract class BaseViewModel {}
public class GridViewModel : BaseViewModel {}
public class TableViewModel : BaseViewModel {}
public class SummaryViewModel : BaseViewModel {}
GridViewPage<GridViewModel>
TableViewPage<TableViewModel>
SummaryViewPage<SummaryViewModel>
Each of the views can have different stylehsheets and javascript files attached, so you should be able to use DisplayFor if you'd like, or you can create the layout by hand.
As for the controller, you can create one action method that returns any of the three views, or you could create three separate ActionResults, one for each view. Here's the "monolithic" ActionResult:
public ActionResult PageViewResult(string pageType)
{
switch (pageType)
{
//define your cases, return your views and your models
//make sure to set a default
}
}
You can format the routes however you see fit. For example, with the above "monolithic" ActionResult, we could create the following route in our Global.asax file:
routes.MapRoute(
"FormattedViewPage", // Route name
"View/Page/{pageType}", // URL with parameters
new { controller = "ViewPageController", action = "PageViewResult", pageType = "grid" } // Parameter defaults
);
Hope this helps. Let me know if you have any questions.
First of if it's the same data then I would try to use the same model for all the views but either use different css or different aspx pages/controls for rendering the model depending on how the user wants to view the data.
Also you may want to look into how much of this can be done in JavaScript, it all depends on how rich you want the user experience to be.
Related
when do you think is it better to use ViewData over a view model?
I have the same exact partial view in a couple of main views. I'd like to control how a partial view is rendered but I'd also prefer the partial view to accept only a view model which is a collection of records, just a pure IEnumerable<> object. I'd rather avoid to send the full view model object from a main view because it also contains a lot of different properties, objects, that control paging, sorting, filtering etc.
Therefore the question is if I should create another view model for the partial view or is it ok to use ViewData? I've read soemwhere that using ViewData is a very bad practice.
With View Data, I could simply pass require details like this:
#{
ViewDataDictionary vd = new ViewDataDictionary
{
new KeyValuePair<string,object>("WithDelete", Model.WithDelete),
new KeyValuePair<string,object>("WithRadarCode", Model.WithCode)
};
}
// ...
#if (Model != null) {
Html.RenderPartial("_ListOfRecordingsToRender", Model.recordingViewModel, vd);
}
At the moment, it would be sorted out.
My worry is that currently this *.recordingViewModel has plenty of different variations in my project, because of different models for creating / editting, listing, shoing details of a record etc. I feel like it may start to be too messy in my project if I make view model for each action.
What do you think. Please could you advice on that particular problem. Thanks
I think you should stick to using a ViewModel, your ViewModel is the class that defines your requirements for the view.
My reason behind this is that in the long run, it will be a lot more maintainable. When using ViewBag it's a dynamic class so in your views you should be checking if the ViewBag property exists (And can lead to silly mistakes like typo's) e.g.:
if(ViewBag.PropertyName != null)
{
// ViewBag.PropertyName is a different property to ViewBag.propertyName
}
This type of code can make your View's quite messy. If you use a strongly typed model, you should be able to put most of the logic in your controllers and keep the View as clean as possible which is a massive plus in my books.
You also will also end up (if you use ViewBag) attempting to maintain it at some point and struggle. You are removing one great thing about C#, it's a strongly typed language! ViewBag is not strongly typed, you may think you are passing in a List<T> but you could just be passing a string.
One last point, you also will lose out on any intellisense features in Visual Studio.
I feel like it may start to be too messy in my project if I make view model for each action.
Wont it just be as messy in your controllers assigning everything to a ViewBag? If it was a ViewModel you could send it off to a 'Mapping' class to map your DTO to your View.
Instead of this:
// ViewModel
var model = new CustomViewModel()
{
PropertyOne = result.FirstResult,
PropertyTwo = result.SecondResult,
}
//ViewBag
ViewBag.PropertyOne = result.FirstResult;
ViewBag.PropertyTwo = result.SecondResult;
You could do this:
var mapper = new Map();
var model = mapper.MapToViewModel(result);
*You would obviously need to provide an implimentation to the mapping class, look at something like Automapper
I'd also prefer the partial view to accept only a view model which is a collection of records, just a pure IEnumerable<> object. I'd rather avoid to send the full view model object from a main view because it also contains a lot of different properties, objects, that control paging, sorting, filtering etc.
That is fine, just create a view model that has a property of IEnumerable<T>. In my opinion you should try and use a strongly typed ViewModel in all of your scenarios.
I have two views that show roughly the same data, but one is by client while the other is by project. Normally this would be great, as the same display template gets re-used across both views. However, I need the display of those items to be different when they list by client vs by project. However, they already have display templates defined. Is there some way for me to have two display templates for a single type?
edit
Ok, I forgot one important detail that makes this more complicated. While there are individual models (view models) that hold the items for each view, the items themselves are of mixed types (common base class). The display templates are for each of the types of items that can be in the list, so I can't use an attribute on the model.
I suppose I could make individual sub-models to wrap or replace the classes, but that's more duplication and work than I'd prefer.
Does each view have it's own strongly typed view? If so create two different templates, then in each model reference them with the [UIHint] annotation.
Example:
public class ClientModel
{
[UIHint("ClientDisplay")]
public SharedDataModel sharedData { get; set;}
//Other fields below
}
Then do the same thing for Project model. If you're currently using the same model between the two you could wrap them in separate new models and do the same thing.
From what you've asked I believe this is what you're trying to do, I had a little trouble following your question.
I have a View strongly typed to an Item class. In my controller I need to send two different List. Is there an easier way to do this other than creating a new class with two List.
What I am ultimately trying to do is have on my homepage 10 items ordered by Date, and 10 items ordered by Popularity.
WHAT I DID
I actually went with a combination of the two answers. I strongly-typed my View to the new class I created with the two lists. I then strongly-typed two partial views to the each one of the lists. May seem like overkill, but I like how it turned out.
"creating a new class with two Lists" is the way to go. It's called a view model and once you embrace it, the power of strongly-typed views really opens up. It can be this simple:
public class IndexViewModel
{
public List<Item> Newest { get; set; }
public List<Item> Popular { get; set; }
}
There are two general philosophies to this. The first is to take the approach John Sheehan stanted. You create a custom view model with both lists and pass that to your strongly typed view.
The second is to consider the lists as "ancillary" data and put them in ViewData like jeef3 stated. But, when you render the lists, you use a strongly typed partial.
ViewData["Newest"] = Newest;
ViewData["Popular"] = Popular
By that I mean in your main view, you'd call RenderPartial(...) but pass in the view data key you used.
And your partial would look like:
<%# ViewUserControl Inherits="System.Web.Mvc.ViewUserControl<List<Item>>" %>
...
This gives you strongly typed access to that view data from within your partial.
It's what John suggested or not having a strongly typed view and adding them to the ViewData:
ViewData["Newest"] = Newest;
ViewData["Popular"] = Popular
Another option would be Strongly Typed Partial views.
You should make a model that includes both lists specifically for the view.
Typically in the little MVC I have done, I have made a model for each view even if they just passed along identically data that was served up for by data or business layer just to keep the separation between the two parts very strict. This setup is a little more work and not needed in many simple cases but it does keep things cleaner in my opinion.
My MVC application contains a parent model, which will contain 1 or more child models.
I have set up the main view to display properties from the parent model, and then loop through a collection of my child models (of various types, but all inheriting from the same base type). Each of the child models have a corresponding partial view.
My "parent" view iterates over the child models like this:
foreach (ChildBase child in ViewData.Model.Children)
{
Html.RenderPartial("Partials/"+child.ChildType.ToString()+"Child",
section);
}
My application has the appropriate /Partials/ChildType1.ascx, ChildType2.ascx, etc. Everything works great.
Is this an appropriate way to use Partial Views? It feels slightly off-kilter due to the dynamic names, but I don't know another way to perform dynamic selection of the proper view without resorting to a big switch statement.
Is it advisable to use the same view for multiple "modes" of the same model? I would like to use the same .ascx for displaying the "read only" view of the model, as well as an edit form, based on which Controller Action is used to return the view.
Iconic,
It's difficult to answer the questions without knowing exactly what you're trying to achieve.
I'll have a go though:
If you're familiar with web forms, think of your partial view as a webforms usercontrol for the moment and think of the part of your model that is relevant to your partial views as a 'fragment of information' that want to pass across to the partial view.
Natural choices for using a partial view would be for elements used in many views across your site.
So ... in answer:
1.Although what you are doing is valid, it doesn't seem quite correct. An example of a partial view I have used might be a row in a grid of data where you'd call the partial view passing in the row object as its model:
foreach (MyObject o in Model.objects)
{
Html.RenderPartial("Shared/gridRowForObject.ascx", o, ViewData);
}
You can strongly type your views also to expect a specific type to be passed through as the Model object.
Again another use might be a login box or a 'contact me form' etc.
2._Ultimately this is a personal design decision but I'd go for the option that requires the least application/presentation logic and the cleanest code in your view. I'd tend to avoid writing to many conditional calls in your view for example and by inferring a base type to pass across to all of your partial views as in your example, may well tie you down.
When learning the MVC framework I found the Oxite code to be useful.
I hope that helps.
The 'RenderPartial()' method in ASP.NET MVC offeres a very low level of functionality. It does not provide, nor attempt to provide a true 'sub-controller' model *.
I have an increasing number of controls being rendered via 'RenderPartial()'. They fall into 3 main categories :
1) Controls that are direct
descendants of a specific page that
use that page's model
2) Controls that are direct
descendants of a specific page that
use that page's model with an
additional key of some type.
Think implementation of
'DataRepeater'.
3) Controls that represent unrelated
functionality to the page they appear
on. This could be anything from a
banner rotator, to a feedback form,
store locator, mailing list signup.
The key point being it doesn't care
what page it is put on.
Because of the way the ViewData model works there only exists one model object per request - thats to say anything the subcontrols need must be present in the page model.
Ultimately the MVC team will hopefully come out with a true 'subcontroller' model, but until then I'm just adding anything to the main page model that the child controls also need.
In the case of (3) above this means my model for 'ProductModel' may have to contain a field for 'MailingListSignup' model. Obviously that is not ideal, but i've accepted this at the best compromise with the current framework - and least likely to 'close any doors' to a future subcontroller model.
The controller should be responsible for getting the data for a model because the model should really just be a dumb data structure that doesn't know where it gets its data from. But I don't want the controller to have to create the model in several different places.
What I have begun doing is creating a factory to create me the model. This factory is called by the controller (the model doesn't know about the factory).
public static class JoinMailingListModelFactory {
public static JoinMailingListModel CreateJoinMailingListModel() {
return new JoinMailingListModel()
{
MailingLists = MailingListCache.GetPartnerMailingLists();
};
}
}
So my actual question is how are other people with this same issue actually creating the models. What is going to be the best approach for future compatibility with new MVC features?
NB: There are issues with RenderAction() that I won't go into here - not least that its only in MVCContrib and not going to be in the RTM version of ASP.NET-MVC. Other issues caused sufficent problems that I elected not to use it. So lets pretend for now that only RenderPartial() exists - or at least that thats what I've decided to use.
Instead of adding things like MailingListSignup as a property of your ProductModel, encapsulate both at the same level in a class like ProductViewModel that looks like:
public class ProductViewModel() {
public ProductModel productModel;
public MailingListSignup signup;
}
Then get your View to be strongly-typed to the ProductViewModel class. You can access the ProductModel by calling Model.productModel, and you can access the signup class using Model.signup.
This is a loose interpretation of Fowler's 'Presentation Model' (http://martinfowler.com/eaaDev/PresentationModel.html), but I've seen it used by some Microsoft devs, such as Rob Conery and Stephen Walther.
One approach I've seen for this scenario is to use an action-filter to populate the data for the partial view - i.e. subclass ActionFilterAttribute. In the OnActionExecuting, add the data into the ViewData. Then you just have to decorate the different actions that use that partial view with the filter.
There's a RenderPartial overload I use that let's you specify a new ViewData and Model:
RenderPartial code
If you look at the previous link of the MVC source code, as well as the following (look for RenderPartialInternal method):
RenderPartialInternal code
you can see that if basically copies the viewdata you pass creating a new Dictionary and sets the Model to be used in the control. So the page can have a Model, but then pass a different Model to the sub-control.
If the sub-controls aren't referred directly from the main View Model, you could do the trick Marc Gravell mentions to add your custom logic.
One method I tried was to use a strongly typed partial view with an interface. In most situations an agregated ViewModel is the better way, but I still want to share this.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IMailingListSignup>" %>
The Viewmodel implements the interface
public class ProductViewModel:IMailingListSignup
Thats not perfect at all but solves some issues: You can still easily map properties from your route to the model. I am not shure if you can have a route parameter map to the properties of MailingListSignup otherwise.
You still have the problem of filling the Model. If its not to late I prefer to do it in OnActionExecuted. I dont see how you can fill a Model in OnActionExecuting.