I'd like to add a little variablility onto the path of my css link everytime my Site.Master view is processed. What is the correct way to do this? My code currently breaks on the Default.aspx saying I have not defined cssLink. Site.Master code below:
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
string cssLoc = "../../Content/css/expect.css?t=" + DateTime.Now.Ticks.ToString();
string cssLink = String.Format(#"<link rel=""stylesheet"" type=""text/css"" href=""{0}"" />", cssLoc);
}
</script>
<!DOCTYPE html>
<html>
<head id="Head1" runat="server">
<asp:ContentPlaceHolder ID="head" runat="server">
<title></title>
</asp:ContentPlaceHolder>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<%= cssLink %>
<script type="text/javascript" src="../../Scripts/jquery.js"></script>
</head>
Also, is there anyway to fix the "XHTML transitional, Title occurs too few times" message?
UPDATE:
Please ignoring the scoping issue. See Richard's answer. I should note however that fixing this does not solve the issue.
I think because of the Inherits="System.Web.Mvc.ViewMasterPage" declaration the entire
<script runat="server"> block does not get processed.
Just define your css link like you typically would and add some inline processing. You are attempting to do a Page_Load within a MVC view which is not a workable solution...
<link href="../../Content/css/expect.css?t=<%=DateTime.Now()%>" type="text/css" rel="Stylesheet" />
Consider writing a helper to link that CSS file.
public static string DatedStylesheet(this HtmlHelper Html, string url, DateTime date)
{
UrlHelper Url = new UrlHelper(new RequestContext(Html.ViewContext.HttpContext, Html.ViewContext.RouteData));
string html = "<link type=\"text/css\" rel=\"stylesheet\" href=\"{0}?t={1}\"/>";
return string.Format(html, Url.Content(url), date.Ticks.ToString());
}
<%= Html.DatedStylesheet("~/Content/css/expect.css", DateTime.Now);
On an unrelated note, does anyone know a cleaner way to do use UrlHelper outside of a view page?
You have declared cssLink as a local variable in Page_Load. As such, it will not be available to your page.
This should fix the issue for you:
<script runat="server">
private string cssLoc;
private string cssLink;
void Page_Load(object sender, EventArgs e)
{
cssLoc = "../../Content/css/expect.css?t=" + DateTime.Now.Ticks.ToString();
cssLink = String.Format(#"<link rel=""stylesheet"" type=""text/css"" href=""{0}"" />", cssLoc);
}
</script>
<!DOCTYPE html>
<html>
<head id="Head1" runat="server">
<asp:ContentPlaceHolder ID="head" runat="server">
<title></title>
</asp:ContentPlaceHolder>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<%= cssLink %>
<script type="text/javascript" src="../../Scripts/jquery.js"></script>
</head>
Edit: Aside from answering your direct question, I would recommend finding a more MVC-friendly solution (rather than putting code in your master page). For example, you could include the css location as ViewData["Stylesheet"] or use a different mechanism to update the css (I'm not up to date on the RC of MVC)
<head runat="server">
<title>Some Title</title> -- WILL FIX YOUR ISSUE
...
<%= Helper.CustomStyle() %>
...
</head>
This Helper.CustomStyle will have the logic inside that varies based on a DateTime.Now call as seen above.
A very good tutorial that will help you to understand the Custom Helpers can be found here: http://www.asp.net/learn/mvc/tutorial-09-cs.aspx
REMEMBER -- ASP.NET MVC does away with Page_Load type calls. It's a distinct design pattern!
Related
I want to prevent clients to access route Home/Index when they are not logged in. So the code in my view is here:
#{
Layout = null;
if (Session["userId"]==null)
{
Response.Redirect(Url.Action("Index","Login"));
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>QIndex</title>
</head>
<body>
<div>
<h1>Profile</h1>
<h2>Hello #Session["userName"].ToString() </h2>
<a href=#Url.Action("LogOut","Login")>خروج</a>
</div>
</body>
</html>
Problem is that when i eliminate #Session["userName"].ToString() everything is alright. But when this is not eliminated, an error occurs on this line System.NullReferenceException: 'Object reference not set to an instance of an object.'.
I set a break point on if (Session["userId"]==null). I realized that compiler goes to <h2>Hello #Session["userName"].ToString() </h2> before it checks the if statement and then returns to if.
Just remove .ToString(). It should render the string properly without it.
I have a problem with the following. I have a masterpage which contains the main layout of the site. Then I have a specific view which uses the masterpage as a layout but adds additional scripts like the following:
_Masterpage.cshtml
<html lang="en">
<head>
<meta charset="utf-8" />
<title>#ViewBag.Title - My ASP.NET MVC Application</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
#Styles.Render("~/Content/css")
#Scripts.Render("~/bundles/modernizr")
#RenderSection("head", required: false)
</head>
<body>
<header> ....
_Register.cshtml
#{
Layout = "_Master.cshtml";
}
#section head
{
#Scripts.Render("~/bundles/jqueryval");
#Styles.Render("~/Content/themes/base/css");
}
<div id="body">
<div data-role="page">
#RenderBody()
</div>
</div>
Now my view which contains the form data makes use of the _Register.cshtml, however when the Index in the RegisterController returns the view (like below), the scripts and styles in the _Register.cshtml are not found anywhere in the Head tag.
public ActionResult Index()
{
return View();
}
Can anyone help me please? What am I missing in order to get these scripts/styles in the head tag please?
Thanks
Sections don't work in partial views and that's by design. You may use some custom helpers - Using sections in Editor/Display templates - to achieve similar behavior, but honestly it's the view's responsibility to include the necessary scripts, not the partial's responsibility. I would recommend you using the #scripts section of the main view to do that and not have the partials worry about scripts
Im wondering if it is possible to use g:include to include only the body contents of a given page.
Say i have a main layout page as follows:
<html>
<head>
<title>My start page</title>
<g:layoutHead>
</head>
<body>
<g:layoutBody>
</body>
</html>
Then a main page (index.gsp)
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
THIS IS MY INDEX BODY CONTENT WITH AN INCLUDE
<g:include controller="book" action="list"/>
<g:link controller="book" action="list">See the full list!</g:link>
</body>
</html>
And finally the book/list page
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
<table>
<g:each in="${books}">
<tr>
<td>${it.author}</td>
<td>${it.title}</td>
<td>${it.price}</td>
</tr>
</g:each>
</table>
</body>
</html>
So what i want to achieve is that the main page (index.gsp) only includes the table defined in the book/list page. However, when i try this it includes the entire html defined (<html> tags and all).
is it possible to do this somehow? i've tried things like <g:include controller="book" action="list" view="someView.gsp"/> however this doesn't work. I really want to avoid having to add a book list logic to the "index controller" i want to reuse my existing controller.
I can't be the first person out there having this usecase, what solutions have you guys come up with?
You can use the applyLayout tag. Simply create an empty.gsp layout with only:
<g:layoutBody/>
And then decorate your include tag with applyLayout:
<g:applyLayout name="empty">
<g:include controller="book" action="list"/>
</g:applyLayout>
See the entry on the Grails guide for further reference.
This is IMHO directly not possible. An idea would be to create a custom tag based on g:include, that strips out parts of the code by e.g. an xpath expression. I'm not aware that this already exists somewhere.
An alternative would be to refactor the body part of book's list.gsp into a template and reference that template from index.gsp using g:render. But this means that the data model has to be available in index.gsp context since g:render does not invoke a controller.
Side note: when using g:include it's a good idea to use the springcache plugin for page fragment caching.
Yes, but you need you need one more level in there. Look at Grails templates. Essentially, you'd have a template: _books.gsp containing:
<table>
<g:each in="${books}">
<tr>
<td>${it.author}</td>
<td>${it.title}</td>
<td>${it.price}</td>
</tr>
</g:each>
</table>
Then your index would be:
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
THIS IS MY INDEX BODY CONTENT WITH AN INCLUDE
<g:render template="books">
<g:link controller="book" action="list">See the full list!</g:link>
</body>
</html>
And your list would be:
<html>
<head>
<!-- main layout reference -->
<meta name="layout" content="main"/>
</head>
<body>
<g:render template="books" />
</body>
</html>
(My syntax may not be 100% right, since it's been a couple months since I've done this, but the idea behind templates are short, reusable pieces of GSP code that aren't meant to be displayed on their own.
Hey. Imagine i have two separate gsp pages with diferent css formatting (with name conflicts between two). And i want to "display" or render one page with its ows formatation inside a div in the other page. Imagining this scenario:
page1.gsp
...
...
<div id="here"></div>
...
...
page2.gsp
Hello there!
I want my final page to be:
...
...
Hello there!
...
...
Is it possible to do that?
Yes use the g:render tag and create a "fragment" page to contain your content.
then use the g:render tag to pull it in.
Refer to the documentation or tutorials for more detail on this.
This is very similar to a question I posted a couple of days ago:
Can I display an inner dive with an independent stylesheet?
Is this something you want to work for every page? (Like a layout?)
If that is the case, use SiteMesh (built in already)
{app}/grails-app/views/layouts/mylayout.gsp
<html>
<head>
<g:layoutTitle default="My Application" />
<link rel="stylesheet" href="${resource(dir:'css',file:'layout.css')}" />
<g:layoutHead />
</head>
<body>
<div id="here"><g:layoutBody /></div>
</body>
</html>
{app}/grails-app/views/{somefolder}/page1.gsp
<html>
<head>
<meta name="layout" content="mylayout" />
<link rel="stylesheet" href="${resource(dir:'css',file:'page1.css')}" />
</head>
<body>
Hello There!!!!
</body>
</html>
If you already have that, and are just looking at breaking up you pages and keeping them DRY..
{app}/grails-app/views/{somefolder}/page1.gsp
<html>
<head>
<meta name="layout" content="yourLayout" />
<link rel="stylesheet" href="${resource(dir:'css',file:'page1.css')}" />
</head>
<body>
<div id="here2"><g:render template="page2" model="[foo:'bar']"/></div>
</body>
</html>
* The Model property of render is optional, but works for passing data to the template to be rendered
{app}/grails-app/views/{somefolder}/_page2.gsp
* Notice the "_" before the gsp name. (Convention for Template pages)
Hello There
Checkout the documentation for render and templating
I'm playing with ASP.NET MVC for the last few days and was able to build a small site. Everything works great.
Now, I need to pass the page's META tags (title, description, keywords, etc.) via the ViewData. (i'm using a master page).
How you're dealing with this? Thank you in advance.
Here is how I am currently doing it...
In the masterpage, I have a content place holder with a default title, description and keywords:
<head>
<asp:ContentPlaceHolder ID="cphHead" runat="server">
<title>Default Title</title>
<meta name="description" content="Default Description" />
<meta name="keywords" content="Default Keywords" />
</asp:ContentPlaceHolder>
</head>
And then in the page, you can override all this content:
<asp:Content ID="headContent" ContentPlaceHolderID="cphHead" runat="server">
<title>Page Specific Title</title>
<meta name="description" content="Page Specific Description" />
<meta name="keywords" content="Page Specific Keywords" />
</asp:Content>
This should give you an idea on how to set it up. Now you can put this information in your ViewData (ViewData["PageTitle"]) or include it in your model (ViewData.Model.MetaDescription - would make sense for blog posts, etc) and make it data driven.
Put it in your viewdata! Do something like the following...
BaseViewData.cs - this is a viewdata class that all other viewdata classes will inherit from
public class BaseViewData
{
public string Title { get; set; }
public string MetaKeywords { get; set; }
public string MetaDescription { get; set; }
}
Then your Site.Master (or whatever) class should be defined as follows:
public partial class Site : System.Web.Mvc.ViewMasterPage<BaseViewData>
{
}
Now in your Site.Master page simply have
<title><%=ViewData.Model.Title %></title>
<meta name="keywords" content="<%=ViewData.Model.MetaKeywords %>" />
<meta name="description" content="<%=ViewData.Model.MetaDescription %>" />
And you're away laughing!
HTHs,
Charles
Ps. You can then expand on this idea, e.g. put a getter to your User (IPrincipal) Class into a LoggedInBaseViewData class.