Common Content Partial View - c#

I have a tag cloud that I'm including on nearly all of the views that I have in my site. I realize that I can put it on a master page. The bigger question is, how do I pass it the data from the database? I'd like to avoid having to fetch the data in every single controller and action. Is there an easy way to do this?

Use #{ Html.RenderAction("TagCloud", "SomeController"); }.
public class SomeController : Controller {
public ActionResult TagCloud() {
var model = // fetch data for tagcloud
return View("~/Views/Shared/Tagcloud.ascx", model);
}
}
It's quite common to load a view that has it's own view model, f.e. because it's shared among a lot of different pages and scenario's, in it's own action.

Related

ASP.NET MVC 5 passing data from home controller to layout via ViewBag

In my HomeController.cs I have some data, which I need to pass to my /Shared/_Layout.cshtml via ViewBag. But I have no idea, how can I do it.
This is my /Shared/_Layout.cshtml
#foreach (var item in ViewBag.Order)
{
<li>#item.Name [ #item.Count ]</li>
}
And here is HomeController.cs
public ActionResult Index()
{
ViewBag.Order = SELECT FROM DB -> ADD TO LIST
}
You don't have access to the Viewbag in the Layout View unfortunately.
You could however use:
PartialView call to a method in a base controller
Simply define a base controller that is a parent for all your controllers. This can also be handy for some error handling by the way.
And in your layout use #Html.Partial("ViewName") to call your base controller.
Use Ajax call
Use javascript in your layout view to execute a controller function that returns the data you need.
Use Session variables instead
since session variables are accesable in layout views.
There are probably more answers too, but I believe these will probably be the most common solutions to your problem.
(If you need any help implementing one of these solutions please give me a sign and I'll explain it more deeply how to do it.)
A fairly common method of passing content to the layout, is to let all ViewModel classes implement a base class (or interface), which will then be the #model of your _Layout view.
Assuming you want to pass on some text MyText to all of your pages:
// My base view model class
public class ViewModelBase {
public string MyText { get; set; }
}
// Some other view model
public class MyOtherViewModel : ViewModelBase {
// other properties
}
// In the _Layout view, implement the base class
#model ViewModelBase
...
#Html.DisplayFor(m => m.MyText)
...
#RenderBody()
...
This way, your _Layout view can work with all the properties of the ViewModelbase class, while whatever view is rendered after that still will have the properties of their child view model - here MyOtherViewModel - available.
Hope that helps!
On a side note, I would not recommend an extended use of ViewBags for passing data to your view in MVC, simply because it has a very low maintainability compared to other methods, due to it not being strongly typed. In my opition, ViewBags does not have any real benefits compared to - for instance - using viewmodels.
As an alternative, you can create a base controller inheriting from the controller and assigning the ViewBag variable to your list in the base controller constructor, like so
public class BaseController : Controller {
public BaseController(){
ViewBag.Order = SELECT FROM DB -> ADD TO LIST
}
}
Then in other controllers, inherit from the base controller like so
public class HomeController : BaseController {
}
This then allows you to assign the view bag for each controller request and the view bag variable will be available in the Layout View.
I myself have used this technique to show a list of car makes that is shown throughout the website.
As a suggestion though, I would implement some form of caching, to save constant calls to the back end.
However if you want to keep the functionality in the home controller you will need to restructure it so that it can be used via an Ajax call using jQuery/Javascript as #counterflux has suggested in their second point

MVC 5 - Returning a set of objects to the page to render

I'm still learning the MVC way of doing web development.
I have a partial view that renders information for a single photo (picture, username, f-stop, other info)
I have several pages where I want to display lists of photos. For example, on my homepage, I want to display the most recent photos to be added to the site.
My current approach to doing this is that I have added a GetNewestPhotos() function to my PhotoController that goes to the database to get the most recent photo records, and, for each one, renders the partialview and concatenates it to the result string (using the nasty-looking RenderPartialViewToString found here). Then client side, I request this string via AJAX and populate a div with the result.
I'm almost sure that this is wrong. How should I do it?
In your controller method, return a partial view and inject your compound object into the view
public class CompoundType
{
public List<Photo> Photos { get; set; }
}
public ActionResult GetNewestPhotos()
{
CompoundType model = provider.GetPhotosFromDbAsCompoundObject();
return PartialView("ViewName", model);
}
In your view, specify that your view's model should be your compound object.
#model CompoundType
At that point, simply iterate over the properties and/or collections in your object to render them into html.
#foreach (var photo in Model.Photos)
{
#Html.Raw(photo.Name).
...
}
There are a number of reasons why this is preferable over your current approach.
Razor gives you strong typing. You can see what your types are and you get vastly more useful runtime exceptions, allowing you to troubleshoot issues more easily.
In your current paradigm, you are actually doing work twice. You are creating the partial views, but then you are taking them and splicing them together on the client. This is redundant work.
Maintainability. Other devs expect to see the pattern I've outlined. By being consistent, you'll find more useful information online and be able to solve problems more quickly when you encounter them. In addition, you can more easily hand over your project with less knowledge transfer.
You have a view model for the page that contains a list of the photo view models. This page view model contains a list of viewmodels for the photos.
In the View for the main page call:
#Html.DisplayFor(m => m.PhotosView)
This will render each view model using the default view you defined.
edit
class MainPageController
{
ActionResult Index()
{
var model = new MainPageViewModel
{
Photos = GetListOfPhotoViewModelsOrderedByAge(SomeDataSource),
}
return View(model)
}
class MainPageViewModel
{
// various other properties
IList<PhotoViewModels> Photos {get; set;}
}
class PhotoViewModel
{
// properties to display about the photo (including hte path to the actual image)
}
The Razor views (mainpage)
#model MainPageViewModel
#Html.DisplayFor(m =>m.Photos)
#* other things on the page *#
Photo view (in the shared/display directory)
#model PhotoViewModel
<img url="#Model.PathToImage" />
I haven't tried this and it's mostly from ther top of my head, there may be slight syntax errors.

Reusing a view in another view with parameters in ASP.NET MVC

Being rather new to ASP.NET MVC, I am already seeing some benefits of it for code reuse and maintainability. When asking a previous question on routing, I had another idea for my example, but unsure how to implement it.
Question: How can I reuse my issue view and controller in separate pages, as well as having parameters for certain things (like how many issues to display)?
Example: Say in my web application I want to show a list of issues. These issues are grouped by projects. So if you go to www.example.com/projectname/issues, you would see a list of issues for that project, and if you went to www.example.com/issues, you would see all issues for all projects. What I would like to do is that if you go to www.example.com/projectname, you would see some info about that project, including the last 10 issues submitted.
How can I reuse this issue code? I see I have the option for Partial View, but when I implemented it, I was unsure how to route any code to it. In Web Forms, you could make a ASP.net control, set some parameters in the code behind, and then embed this control elsewhere.
Some of the examples I have found so far either lack a complete implementation (goiing beyond just adding some HTMl in other pages), look like older MVC code that doesn't seem to work for me in ASP.NET MVC 3, and lack allowing me to set paramaters and showing this type of reuse.
My terminology may not be entirely correct here. If anything, I am trying to find the best (read MVC) way to replicate something like ASP.net Web Forms User Controls. As in, reusing my 'issues' code (HTML and C#) on both a 'master' issues list, as well as an issues 'widget' if you will
Skip the temptation write code in the view that goes out and accesses data on it's own. That includes using built-in functions like RenderAction. Even though RenderAction "goes back" to execute another controller it doesn't mean the view isn't taking an action on its own, which arguably breaks the MVC approach where views are supposed to do nothing and the model is supposed to contain everything the view needs.
Instead what you could do is send back a model for your issue list page(s) which contains a property containing the issues list:
public class IssueListModel {
public List<Issue> Issues { get; set; }
}
Populate it in your issue list action:
public ActionResult IssueList(string projectName) // projectName may be null
{
var issueListModel = new IssueListModel();
issueListModel.Issues = SomeRepository.GetIssues(projectName); // whatever you need to send...
return View(issueListModel);
}
Then on your list pages you could loop through it:
#foreach (var issue in Model.Issues) {
<div>#issue.IssueName</div>
}
Or you could send the Issues collection down to a partial view:
#Html.RenderPartial("IssueList", Model.Issues)
You can type your partial view to expect List as the model:
#model List<MyProject.Models.Issue>
... and then loop through it in the partial view, this time doing a foreach on the model itself:
#foreach (var issue in Model) {
<div>#issue.IssueName</div>
}
Then what you can do is make a separate model for your project detail view which also contains a property containing Issues:
public class ProjectDetailModel {
public Project Project { get; set; }
public List<Issue> Issues { get; set; }
public string Whatever { get; set; }
}
In the controller you can populate this List using the same function that you would populate in your lists controller:
public ActionResult ProjectDetail(string projectName)
{
var projectDetailModel = new ProjectDetailModel();
projectDetailModel.Issues = SomeRepository.GetIssues(projectName, 10); // whatever you need to send
return View(projectDetailModel);
}
Then you can re-use the same exact partial view on your ProjectDetail view:
#Html.RenderPartial("IssueList", Model.Issues)
A long answer but I hope this is what you were looking for!
If you want to re-use presentation logic only, you can use partial view. If you want to re-use also controller's logic, you have to use child action combined with partial view.
Create a controller
public class IssuesController : Controller
{
[ChildActionOnly]
public PartialViewResult List(string projectName, int issueCount = 0)
{
IEnumerable<Issue> issueList = new List<Issue>();
// Here load appropriate issues into issueList
return PartialView(issueList);
}
}
Do not forget also to create appropriate partial view named List within the folder Issues.
Finally use this line within your project view
#{ Html.RenderAction("List", "Issues", new { projectName = "Px", issueCount = 10 } ); }
and this line within your issue list view
#{ Html.RenderAction("List", "Issues", new { projectName = "Px" } ); }
In your controller method return the view as named rather than just View()
ie...
public ViewResult IssueView1()
{ return View("Issue");}
public ViewResult IssueView2()
{return View("Issue");}

MVC3 Partial Views

Still Learning MVC3, EF. For now I am connecting to MySql but I believe that will not be relevant. For simplicity, I decided to use one database for my test application and I have included a category to differentiate the data. For eg I have a news, events,info and pages categories. Now when it comes to listing contents in views for example at the homepage, I want to list latest 5 news items(news category), latest 5 events(events category), welcome text(info category). i have been able to create partialViews to list these at the different sections of the homepage. But I feel am doing this wrongly since in each of these PartialViews I am querying the same table over and over and just filtering with where cat=....in the LINQ query.
Can you please confirm if that should be the practice or there is a better way to do this.
You could do the following:
Controller:
public ActionResult Home()
{
IEnumerable<MyDateRecords> myData = LinqQueryToGetAllTheDataUnFiltered();
ViewData.Model = new MyViewData { MyData = myData; }
return View();
}
ViewModel class:
public class MyViewData
{
List<MyDataRecords> MyData { get; set; }
List<MyDataRecords> News { get { return MyData.Where(m => m.Category = "News"); } }
List<MyDataRecords> Events { get { return MyData.Where(m => m.Category = "Events"); } }
}
View:
#model MyViewModel
#Html.Partial("NewsPartial", Model.News)
#Html.Partial("EventsPartial", Model.Events)
Partial:
#model IEnumerable<MyDataRecord>
This way we only queried for the data once and just passed a different set to each partial
For an uncomplicated way of presenting this type of data, what you are doing is fine. You should look at the OutputCacheAttribute for any PartialView Method you use on your Controller.
This is pretty inefficient. But it's good that you noticed this because querying that database is often the bottleneck in any given request.
For starters you should retrieve that data into a dictionary or model and then pass it to the partial views to render similar to what Bassam outlined. Ideally, this should be taken care of in the Controller to stick to the MVC paradigm and then passed to the main view, which would then pass the appropriate data to the partial views.
Once you get more advanced/familiar with ASP.NET MVC, you can start looking into caching. I'd stay away from caching for right now since it get somewhat tricky if you have data that is rapidly changing since you need to start worrying about updating/synchronizing/etc.

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