in webforms I would always use my masterpage to set page titles and meta description based on the current url. I was thinking of doing the same for my Asp.net Mvc projects but I ain't sure where to start. It would be nice to be able to set the title/description based on the controller and/or action with some default values incase I don't specify any info. The reason why I do this is because I like everything to be in one place because that makes it easy to spot mistakes.
Edit:
After reading the answers and googling some I was thinking it might be cool to get the info from an xml file. With Xml looking something like this:
<website title="default title for webpage">
<controller name="HomeController" title="Default title for home controller">
<action name="Index" title="title for index action" />
</controller>
</website>
I am new to Asp.net Mvc so I am not sure where to initialize this.
I suggest the following strategy:
Create an hierarchy of models:
abstract class MasterModel
{
public string PageTitle { get; set; }
}
abstract class HomeBaseModel : MasterModel
{
PageTitle = "Home";
}
abstract class UsersBaseModel : MasterModel
{
PageTitle = "Users";
}
/************************************/
class HomeNewsModel : HomeBaseModel
{
PageTitle = "News";
}
class UsersProfileModel : UsersBaseModel
{
PageTitle = "Profile";
}
You define a master model to hold the page title and you create base models to hold default titles for a controller. This way, you can define the title in each action explicitly or leave it out so that the default title for this controller will be used.
Then in your master view you just write once:
<title><%= Model.PageTitle %></title>
and it's done.
You could simply pull that data from the master page's Model, then let that Model have some reasonable defaults.
So after a few days of trying stuff I ended up with making a custom filter that reads from a XML file.
I added the code to copypastecode.com
http://www.copypastecode.com/9797/
http://www.copypastecode.com/9809/
http://www.copypastecode.com/9805/
I am very novice at Asp.net Mvc and "real" C# coding so if you see strange stuff please forgive me. If somebody wants to optimize it or has a better solution feel free to post it as an answer.
Next thing I am gonna try is to make it without a filter so it's activated on all controllers. Not sure where to hook up the logic though. So if anybody can push me into the right direction let me know.
Related
I have a pretty big ASP.NET MVC site with 100 controllers and thousands of actions. Previously the header image that was defined on the Site.Master page was hardcoded and I want to make it dynamic.
To do so, I added this line to my Site.Master file:
<%= Html.Action("GetHeaderTitle", "Home")%>
which just returns some HTML for the header title such as:
<span style='font-size:15px;'>My Header Title</span>
The issue is that <title> also had this same hard coded value. I could obviously create another HTML.Action to have it show the dynamic valid in the title, but now I am going back to the server twice for essentially the same information (not the exact same HTML as I don't want the span information, but the same logic on the server to get the data).
Is there a way to have an Html.Action return multiple snippets of HTML that I can updates in different places on my master page?
I think you're looking at it wrong - if retrieving of the title is a long operation then just cache the results and write different actions anyway.
// Controller
public string GetTitle()
{
var title = (string)ControllerContext.HttpContext.Items["CachedTitle"];
if (string.IsNullOrEmpty(title))
{
title = "some lengthy retrieval";
ControllerContext.HttpContext.Items["CachedTitle"] = title;
}
return title;
}
public ActionResult GetTitleForTitle()
{
return Content(GetTitle());
}
public ActionResult GetHeaderTitle()
{
return Content("<span>"+ GetTitle() + "<span>");
}
Alternatively, you can cache it directly on the view, which is kind of evil (the simpler view the better):
<%
ViewBag.CachedTitle = Html.Action("GetHeaderTitle", "Home");
%>
...
<%= ViewBag.CachedTitle %>
...
<%= ViewBag.CachedTitle %>
Are you using <asp:ContentPlaceHolder ID="TitleContent" runat="server" /> ? in your site.master? Or have you considered using it? This would allow you to set the title from within a view based on your model.
Maybe you should also consider to introduce ViewModels, which allows you to combine view related data into a ViewModel and return it from a controller. This would allow you to batch queries and save round trips. And use a data repository which gets injected into your controller class (if you are not already doing). Sorry I'm guessing here because you do not show any controller code.
Or you should take advantage of client side code (JavaScript) and load parts of the UI via ajax? And have the UI update itself (could also be reactive).
Unfortunately I have the feeling that the thing your are facing today is not about the title but more about the hundreds of controllers with thousands of actions.
So researching how to organize controllers and building up an opinion based on your use cases would be my best bet based on what I can see and guess from your question.
Hope that helps...
I'll explain a generic scenario. To explain my problem, I'd request some patience until I explain how I've implemented my Search page. I've a [Users] table and an [Address] table based on which I've derived a VIEW in SQL - [vw_User_Addr]. I'm using L2S so that I'll get a strongly typed vw_User_Addr class.
Now I've a search page so apparently I'll derive it from this class like:
<%# Page Title="User Search" ... Inherits="System.Web.Mvc.ViewPage<MyDAL.vw_User_Addr>" %>
My List action returns a List which I use in the View to form the Grid table. Everything is fine till here. Now I want to implement search on this page. So this is what I found -
In Controller during the List Action I create ViewData["Usr"] = new vw_User_Addr();
In my View I do:
<% MyDAL.vw_User_Addr usr =
((MyDAL.vw_User_Addr)(ViewData["Usr"]));
%>
Then I use this "usr" object to bind my search control like:
<%= Html.TextBox("FirstName",
usr.FirstName)%>
Finally in my HttpPost handler action I object vw_User_Addr like:
[HttpPost] public ActionResult
List(vw_User_Addr searchObj){...}
I use this searchObj to extract the values user might have populated in the search controls and then I perform search.
So, I hope I explained well. This is how I do my search in MVC2. Here're my concerns/questions:
When I click on the search image-button I get a postback but it fires a ModelValidation which gives error. Not with all but atleast those which are int (i.e. like Roles dropdown search control) - how to handle that?
After a lot of R&D I've settled that I've to either do a ModelState.Clear(); or a more complex way to handle this. Is there a better option?
Is there a better way to achieve the search-implementation explained above? Am I using the standard\best way to do search in MVC?
At core-level, can I make L2S understand that this is an SQL-View so its Readonly and all the fields can be NULL - so that the Mr.DefaultModelValidator doesn't perform such illogical validations?
If 4. is viable can I set the properties of all the fields in the L2S view to be readonly to give it a try?
Thank you for your precious time and review. Pls guide me if I'm not on the track. I believe this is the simplest search-scenario and so it has to be easy. Just need to find the missing links.
I implemented an ActionFilterAttribute [SkipModelValidation] which avoids/eliminates unwanted model validation for search pages.
Ref: How to disable validation in a HttpPost action in ASP.NET MVC 3?
public class SkipModelValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Get ModelState
ModelStateDictionary dict = ((Controller)filterContext.Controller).ModelState;
if (dict != null && !dict.IsValid)
dict.Clear();
base.OnActionExecuting(filterContext);
}
}
I am developing a web application using asp.net mvc.
I have come across the need to Temporarily create a class in the ascx/aspx file.
This class will replace the Model during the development of the page.
It will also hold some test data for the user to have the chance to see some results.
Once we are happy with the layout on the screen, I will inherit the correct Model class through the Control tag.
Can you please advise if this is possible and how to do it?
This does not work:
<%
class Modelo
{
public Guid Guid { get; set; }
public string Name { get; set; }
}
%>
Edit:
I have found that when I am sitting with the user and we are discussing the specifics of a control on the screen, in many cases when he/she sees the results and we discuss them he/she asks to change something. Moving between the controller, the model and the fake data repository consumes time. Even though the user is quit intelligent he/she can not follow what I am doing and feels less involved. If every thing was in one place I could explain to them what I am doing , they would at least feel that they know what is happening and we spend more time working and less time waiting for me to switch between screens. After he/she leaves I can sit down quietly and implement the agreed solution quietly. By keeping a class with fake data representing the model on the actual control all I will need to do is some housekeeping to meet the MVC way of doing things.
Hope it is more clear now.
Thanks in advance, Be happy - Julian
An alternative approach that would meet your needs could be as follows:
You base your view on a ViewModel as you would normally, however you copy the view model into a 'DisplayModel' local variable that you use for display purposes. The values in this can be set locally during the development demo stage - if no values have been supplied by the controller.
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MyViewModel>" %>
<%
MyViewModel displayModel = MyViewModel;
if (displayModel == null)
{
displayModel = new MyViewModel {
Property1 = "Test Value 1",
Property2 = 100.00,
Property3 = true
};
}
%>
<%= displayModel.Property1 %>
<%= displayModel.Property2 %>
<%= displayModel.Property3 %>
Why don't you directly hardcode the values in the view? Another possibility is to use the real model class but use a dummy repository inside the controller which will fill the model with temporary data. Once the final repository is ready all you have to do is instruct your DI framework to pass the real repository to the controller.
IMHO creating temporary classes inside the views wouldn't bring much value nor you will gain time.
Well, you can't really achieve what you want, as the view processor is not exactly like a code behind.
Your best bet would be to create a temporary class in your controller, and then pass that to the view. At least it takes some steps out of the way, and it shouldn't take too long to switch between only two windows.
I've a problem with Partial View.
I am developing a blog in asp.net mvc and I would make in my masterpage a list of categories, last post, last comments.
I think that the best solution is to use strongly typed partial view, and in each partial view pass the necessary model.
MY problem is that the model in View.. in any view (connected to the masterpage's contentplaceholder) enter in conflict with the models in partial views and I get an error like this:
The model item passed into the dictionary is of type 'System.Collections.Generic.List`1[Blog.Models.Articoli]' but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Blog.Models.Categorie]'.
I found on web a dirty solution that consist of to pass togheter the model of any view, some viewdata, one for every model to pass in partial view. But this solution don't respect DRY Principle..because you must repeat this code for each action!
So, my question is: Can I create a model that contain also partial view's model?
If, yes, in that way?
It Exist another solution more simple?
Thanks for help
How about the View Model Pattern?
I've created wrapper classes that are passed to my views rather than whatever object I would normally use
public class MyCreateUserView
{
public User CreatingUser { get; set; }
public MyPartialViewObject Blah { get; set; }
}
In your view write:
public ActionResult CreateUser()
{
MyCreateUserView createUser = new MyCreateUserView()
{
CreatingUser = GetUserFromSomewhere(),
Blah = GetPartialViewObject();
}
return View(createUser);
}
Then your page header looks like so:
<%# Page Language="C#" Inherits="ViewPage<MyCreateUserView>" %>
and when you render your partial write:
<% Html.RenderPartial("../MyPartialViewObject ", Model.Blah); %>
Instead of solving that with the pattern you describe (which is generally a great pattern), I solve that with calls to RenderAction and have it return a partial view. That way the code is in one place as each call to each view does not have to worry about marshalling all the data you need. If you want to see a short discussion on how to use it, I would check Haack's blog here: http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx. You can also check out the discussion on this other post here on SO: ASP.NET MVC Master Page Data
How can I set the Html title from within a controllers action?
I don't believe there is a way to simply set the title directly from the controller with out setting up your views to accept some sort of data associated with the information. Especially since to actually set the title you'll need to output data in between tags.
That said, I'm sure there's something you could do to make this easier on yourself. I'm more or less just thinking aloud here, so I can't guarantee this will work. If I was sure that I would set my title's on every action that I have, then I would keep the title tags in the master page and create a custom attribute so you could do something like this:
[CustomTitleAttribute(Title = "Hello World")]
public ActionResult Index()
{
return View();
}
It would be up to you to implement the attribute and set up how you capture this information in the view and/or master page.
Generally speaking, since you may want the title to change on pages that have dynamic data, the above is probably not something you should do. Instead, just incorporate some way to determine the title you need in a view model. Maybe even a base view model that subsequent view models can inherit from.
public class BaseViewModel
{
public string PageTitle { get; set; }
public string PageDescription { get; set; }
//etc.
}
Then in the views you can do this, or even in your master page I think:
<title><%= Model.PageTitle %></title>
I think from a standpoint of separating concerns(which is kinda the whole point) that would be the best way to go.
It sounds not very wise for controller to control the output.
All a controller do, is to give viewdata to the output view and let the view determine what to do with it.
Please refer to the default ASP.net MVC template for how it is done.