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.
Related
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");}
I'm quite newbie to mvc and as I'm designing some blog-like web app I want to know one thing from more experianced. The problem is that I have a view of a blog article and box for typing comments for it. The perfect, abstract solution would be to type this view strongly with a class of both article and comment, but as we know, it's not possible.
What will be the best way to handle this case? As it's a View of article, this should typed with article class. On the other hand - it will be easier to type it with comment class and pass article fields with ViewBag. I don't find it right as logically it shouldn't be typed with a class, that isn't directly represented here (article is the main one).
What are other possible solution to add and validate comments easily and do it in a clear way?
Thanks in advance.
Edit: main "problem" is not to handle list of existing comments, but to add new one - can I use with ViewModel FormCollection in this case easily on the controller side?
You would use a ViewModel in this instance. Something like this:
public class Article
{
public string Text { get; set; }
}
public class Comment
{
public string Text { get; set; }
}
public class BlogPostViewModel
{
public Article Article { get; set; }
public IEnumerable<Comment> Comments { get; set; }
}
ViewModel: the connecting object between a domain model (or models) [in your case Comment and Article] and a View [in your case, your blog post view].
Your View would look something like this:
#model BlogPostViewModel
#* you can reference Model.Article, as well as Model.Comments here *#
Here is a good working example of ASP.NET MVC View Model Patterns.
Edit: and then for an Action method to receive your POSTed data:
[HttpPost]
public ActionResult YourBlogView(BlogPostViewModel bpvm)
{
// here you have access to the binded model
// i.e. bpvm.Article
}
The perfect, abstract solution would be to type this view strongly with a class of both article and comment, but as we know, it's not possible.
But it is possible. And it's called a ViewModel.
You seem to be fixated a little on the CRUD model. It looks mainstream but that's because it's the only approach that lends itself to code-generation and templating.
To get a better perspective, take a look at the AccountModels in the standard MVC templates.
Use a new model:
public class ArticleAndCommentsModel {
public ArticleModel Article { get; set; }
public IEnumerable<CommentModel> Comments { get; set; }
}
An your view will inherit ViewPage<ArticleAndCommentsModel>.
I Can see two options
Create a viewmodel with the article and a comments collection
Render the comment section as a separate view (on the same page)
An alternative to the ViewModel approach suggested by the others would be to put the Article in the ViewBag and reference it from there and let the Comment be the page's model. This assumes the user doesn't modify the article.
Let's say I have a theoretical MVC framework that uses a ViewData object to pass data from the controller to the view. In my controller, let's say I have some code like this (in pseudocode):
function GetClientInfo()
{
// grab a bunch of data from the database
var client = Database.GetClient();
var clientOrders = Database.GetClientOrders();
var clientWishList = Database.GetClientWishList();
// set a bunch of variables in the ViewData object
ViewData.set("client", client);
ViewData.set("clientOrders", clientOrders);
ViewData.set("clientWishList", clientWishList);
showView("ClientHomePage");
}
And then in my ClientHomePage view, I display the data like so:
<p>Welcome back, [ViewData.get("client").FirstName]!</p>
<p>Your order history:</p>
<ul>
[Html.ToList(ViewData.get("clientOrders")]
</ul>
<p>Your wishlist:</p>
<ul>
[Html.ToList(ViewData.get("clientWishList")]
</ul>
This is what I understand MVC to be like (please correct me if I'm wrong). The issue I'm having here is those magic strings in the view. How does the view know what objects it can pull out of the ViewData object unless it has knowledge of what the controller is putting in there in the first place? What if someone does a refactor on one of the magic strings in the controller, but forgets to change it in the view, and gets a runtime bug instead of a compile-time error? This seems like a pretty big violation of separation of concerns to me.
This is where I'm thinking that a ViewModel might come in handy:
class ClientInfo
{
Client client;
List clientOrders;
List clientWishList;
}
Then the controller creates an instance of ClientInfo and passes it to the view. The ViewModel becomes the binding contract between the controller and the view, and the view does not need to know what the controller is doing, as long as it assumes that the controller is populating the ViewModel properly. At first, I thought this was MVVM, but reading more about it, it seems like what I have in mind is more MVC-VM, since in MVVM, the controller does not exist.
My question is, what am I not understanding here about MVC vs. MVVM? Is referring to variables in the ViewData by magic strings really not that bad of an idea? And how does one insure that changes made in the controller won't adversely affect the view?
Your understanding of MVC is wrong, it stands for Model View Controller but you are missing the Model in your example. This is the typed entity that gets passed back to the View to do the rendering. In ASP.Net MVC you would use typed Views that also type the Model within the View so it is checked at compile time. This eliminates the need for magic strings (second part of your question).
In MVVM you have Model View ViewModel. This is a way of binding a ViewModel directly to the UI layer via a View which is used a lot in WPF. It replaces the need for a controller and it's generally a 1-to-1 mapping with the UI. It's just an alternative mechanism that solves the same problem (of abstraction and seperation of concerns) but better suited to the technology.
Theres some useful info here which might help understand the difference.
Best approach to use strongly typed views
Models:
public class ContentPage
{
public string Title { get; set; }
public string Description { get; set; }
}
public class ContentPagesModel
{
public ContentPage GetAboutPage()
{
var page = new ContentPage();
page.Title = "About us";
page.Description = "This page introduces us";
return page;
}
}
Controller:
public ActionResult About()
{
var model = new ContentPagesModel();
var page = model.GetAboutPage();
return View(page);
}
View:
#model Experiments.AspNetMvc3NewFeatures.Razor.Models.ContentPage
#{
View.Title = Model.Title;
}
<h2>About</h2>
<p>
#Model.Description
</p>
for more detail check out here
I case of using string as keys of ViewData - yes, it will be a lot of exceptions if someone will refactor code.
It sounds like your understanding of MVC is very old, probably based on MVC 1. Things have changed tremendously in the last couple of years.
Now we have strongly typed view models, and we have the ability to use expressions in the view, which by default aren't compile-time validated, but they can be for debug purposes (though it slows down the build a great deal).
What's more, we don't pass model data through ViewDate anymore (well, not directly anyways.. it's still passed that way but the framework hides it).
For instance I have a model X with properties Title(string) and Valid(bool). I need to show same model on two separate pages with different field labels and input controls.
E.g. "Title" for title and "Valid" for valid on one form while "Destination" for title and "Returning" for valid on the other.
I guess the easiest way would be to have two different views for the same model. But is it really a MVC way to go?
Thanks
Well, let's say you have some View-folder called List, and one called Details - and displaying the Model in the two should be different.
You can create a DisplayTemplates folder within each of the two folders, and create a PartialControl with the same name as your Model, and also strongly type it to your Model.
In your different views you can then do <%= Html.DisplayFor( your model) %> or you can also use the regular <% Html.RenderParital("NameOfPartial", ModelX); %>
Edit
To try and approach the original question, maybe this could help you in some way (I posted this as an answer to a different question How to change [DisplayName“xxx”] in Controller?)
public class MyDisplayName : DisplayNameAttribute
{
public int DbId { get; set; }
public MyDisplayName(int DbId)
{
this.DbId = DbId;
}
public override string DisplayName
{
get
{
// Do some db-lookup to retrieve the name
return "Some string from DBLookup";
}
}
}
public class TestModel
{
[MyDisplayName(2)]
public string MyTextField { get; set; }
}
Maybe you could rewrite the custom-attribute to do some sort of logic-based Name-selection, and that way use the same PartialView for both model-variations?
Yes, two different Views is appropriate, as you are providing two different VIEWS of your MODEL.
However, are you sure you aren't shoehorning your data into a single model, when in fact it represents a different entity in each case?
Is it really the same model?
If they're two different entities with similar properties then I would create two separate view models. Any commonality could be put in an abstract base class or interface.
If it's the same model but just a different input screen then sure, reuse the model.
I would imagine the first case is probably the one that is relevant here.
I have a contoller that renders 3 different views. But I also have a common part (div) for every view. I thought that I can create an UserControl with own controller and include that control on my views (New controller and view as controll).
How should I use that UserControl? Should it be a partial view?
Or different approach - can I have multiple partial views on one page?
I've been searching the web for the last view days and haven't found working solution that suits me. Also I want to use Strongly Typed views/data.
Cheers
You should use a partial view. Then you call <% Html.PartialRender("MyCommonControl", Model); %> in the 3-4 views to render the common section (like a menu or something).
This way you can strongly type the partial view and pass the model (like in the above example) or part of the model to it that is relevant.
UserControls are a ASP.NET Forms paradigm really, you should use partial views because they use the same MVC View Engine.
Update
If you place the PartialView in /Views/Home it'll only be accessible to the HomeController You want to put it in /Views/Common to make it accessible to ALL controllers.
You should also make a Generic ViewModel for The data that control needs, and make it a sub-component of the models for each Controller:
Eg:
class CommonSectionViewModel
{
public string Data { get; set; } // Just Example Data
public int Count { get; set; }
}
class ProductsModel
{
public CommonSectionViewModel CommonData { get; set; }
// Other properties for a products models
}
class CompaniesModel
{
public CommonSectionViewModel CommonData { get; set; }
// Other properties for a company model
}
Then in your Views for your controllers you call the partial render like this:
<% Html.PartialView("MyCommonControl", Model.CommonData); %>
Note: You can override the control as well
Having the following files:
/Views/Common/MyCommonControl.ascx
/Views/Products/MyCommonControl.ascx
When you call .RenderPartial("MyCommonControl") from ProductsController #2 is used, and from any other controller, #1 is used. So you can override functionality for some controllers if you wish.