I have one page that will contain a lot data.
Outline of the page is on the image.
For output I have used "razor" engine.
Now I wonder will I get something if I refactor my code so each section
which data comes from database will be partial view?
On this page I need about 20 value objects to display all needed data.
I have made one "ViewModel" class that contains all data I need and set that
viewModel class as razor page model. Is this the right way how I should do this or
there is some smarter way?
public class MyViewModelClass{
public ValueObjectx x;
public ValueObjecty y;
public ValueObjectz z;
public List<ValueObjectT> tList;
public List<ValueObjectG> gList;
public List<ValueObjectS> sList;
}
In a scenario like this I would typically have each section as a partial view. Each section is responsible for it's own layout and ViewModel. This prevents the tendency to have a super view which has to know about too many things in order to render.
I would not use one ViewModel for all the data. I would only use the ViewModel for the data that might actually be posted back to the server. For all the other data, like lists, I would put it on the ViewBag. Like:
public ViewResult SomeAction()
{
var model = GetYourModelData();
ViewBag.TList = GetTList();
ViewBag.GList = GetGList();
ViewBag.SList = SList();
return View(model);
}
You could put all the lists in a separate helper object if you use them a lot.
Note that this is not a "this is how you should do it answer", it is more a description of what works for me. But as I see it the model of the view is one thing and the actual data, as data in drop downs etc., on the view is another thing and should be put in the ViewData property, but the ViewBag is just a dynamic wrapper around the ViewData property. You can then use the properties on the ViewBag as input to your partial views that should make up each of your sections.
Related
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.
If i have a controller action "Create" that returns a view with the following as the Model type:
public class PaymentModel
{
public Model.SummaryInformation SummaryInformation;
public Model.CardDetail CardDetail;
}
If there is a button on this view that POST's to an action "New" and I want that action to recieve a different object e.g.
public class PaymentNewModel
{
public Model.CardDetail CardDetail;
}
Is this possible? I do not want to use the same Model when the view is rendered to the Model that is POSTed
I'm not aware of anything that would prevent this. The action binder doesn't really care, as long as it can figure it out.
I assume the SummaryInformation object is only used for presentation? (it does not affect the input form?) In that case, you could pass it via ViewData and just bind the view directly against the CardDetail. This is closer to the MVC philosophy, but probably not a huge deal one way or the other.
I'm just wondering where people are creating their SelectList - in the action or the view.
I have seen examples of both and the one that makes the most sense to me is doing it in the action and have the view model have a property of type SelectList.
On the other hand, I have seen examples where people have the view model have a property of SelectList and the SelectList is populated within the view model (either in the constructor or via lazy loading). I like this idea as it means there is less code in my actions...
In short I was just wondering what people are doing atm.
Cheers Anthony
Create your SelectList in the controller (by looking up your list of items from your model repository), and pass it to the view as either a ViewData object, or as part of your strongly-typed ViewModel.
It´s a presentation-specific aspect, so I prefer to do it in the View, using an Html helper. So I pass a collection to the View and use a html helper method to map the items to SelectListItems. The method could look very much like this:
public static IList<SelectListItem> MapToSelectItems<T>(this IEnumerable<T> itemsToMap, Func<T, string> textProperty, Func<T, string> valueProperty, Predicate<T> isSelected)
{
var result = new List<SelectListItem>();
foreach (var item in itemsToMap)
{
result.Add(new SelectListItem
{
Value = valueProperty(item),
Text = textProperty(item),
Selected = isSelected(item)
});
}
return result;
}
Regards.
I typically create my SelectList in the action or service layer and pass it to my view via ViewData. I've also made it a part of a view model and a strongly typed view. Both ways create it in the action or service layer though.
I have the SelectList exposed as a property in the view model and populate it in the action using the necessary repository. I think that the code that interacts directly with the repositories should be the one that is also responsible with populating, be it the controller actions or service layer or whatever else.
I don't think that populating the list directly from the view model is a good idea, because it would require the view model to have a repository dependency and do database interactions and the view model should not be responsible for this kind of things.
You could also create a separate special object, called Initializer or something like that, that does all the populating and initializations, if you have multiple SelectList fields and want to keep your actions code cleaner.
Neither, create it in a separate class, look here How to map View Model back to Domain Model in a POST action?
I use an IBuilder interface in the controller and do all the building of entities/viewmodels in the implementation of this Builder
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.
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