I am using .NET MVC, and within view pages I set a contentplaceholder that contains an ID to be used on the master page like so:
View page:
<asp:Content ID="CDomBodyId" ContentPlaceHolderID="DomBodyId" runat="server">LmpDemoRequests</asp:Content>
Master page:
<body id='<asp:ContentPlaceHolder ID="DomBodyId" runat="server"></asp:ContentPlaceHolder>'>
So in this particular case, the body tag would render like this on the final HTML page:
<body id='LmpDemoRequests'>
I would like to have double quotes around the body id tag, but inverting the quotes like the following makes intellisense unable to find the contentplaceholder, giving me a lot of warnings when I compile.
<body id="<asp:ContentPlaceHolder ID='DomBodyId' runat='server'></asp:ContentPlaceHolder>">
Is there any way around this?
This is an issue with ASP.NET editor. It's not specific to MVC. I think the workaround is pretty good and I don't see a specific drawback.
Try declaring BodyID as a property of your MasterPage. Set its value in the View pages. Then you can do something like
<html>
<body='<%= BodyID %>'>
</body
</html>
Not sure if I have misunderstood your question, but you can also add:
<body id="site" runat="server"></body>
And then access it on your page
HtmlControl body = (HtmlControl)Master.FindControl("site");
body.Attributes.Add("class", "LmpDemoRequests");
I hope I understood your correctly.
Related
I'm setting a variable in grails-app/views/layout/main.gsp to a value, like
<g:set var="welcometext" scope="session">Hello, pleased to meet you!</g:set>
But when I try to access it in my view grails/app/views/index.gsp with
<html>
<head>
<meta name="layout" content="main"/>
</head>
${welcometext}
<body>
</html>
Nothing gets printed out. So the variable set in main.gsp is not accessible to my view index.gsp. I tried also to set the scope to page, request and session, but success.
How can I set a variable in my layout main.gsp and reference it in other views?
You can define variables in your page (index) and your layout can see them (which is really useful for breadcrumb for example).
However, you can't access to a variable defined in your layout from your page (index) because the page is rendered like there was no layout, and then we apply the layout to the page rendered.
As a shortcut, you can see the rendering flow like this: (render Index) THEN (render Layout)
So, in your case, you have to find another solution, depending on your content:
Put the welcometext in session (not the best solution)
Pass it as a parameter of each view (not the best solution)
Create a custom tag to automatically print it
Create a template and render it where you need
Hardcode it ? (I don't like this one, but it's closer to what you're trying to do)
Use JavaScript to add the text ?
...
Try to see which one is the best for you ;)
I have a plugable scenario on asp.net mvc. My Layout file contains a method for Head and body. Methods are rendering related plugin. So I have only one Controller/Action (Page/Index) and I dont need any view file for this action. Is that posible working without View file or only Layout file?
Thanks.
Layout example.
<html>
<head>
<title></title>
</head>
<body>
<div> MENU </div>
<div>
Plugin.Render("body"); //Render plugin method.
</div>
</body>
</html>
It seems an odd situation, but regardless:
It all depends on what you expect your one method to return. If it returns non-HTML (e.g. JSON, any primitive type, ...), you don't need it, obviously.
A HTML-layout is generally rendered via Views. I'm not sure if you can address your Master/Layout page as a view in and of itself. What I would suggest is adding a View file, setting it up with the correct Master/Layout page, and then basically empty out the view file.
So you'd have a View file, but it wouldn't have any content in it (except for the Master/Layout page setting).
It'd be slightly more understandable for future developers (although not that much), and I don't see any real downside to this (performance-wise or other).
I used PHP years ago but have since been developing in C#/VB.Net.
In ASP.Net MVC 2, you can provide a master page which defines content areas eg:
<html>
<head>
<title>Overshare | <?=$Title?></title>
<ContentArea name="Head"/>
</head>
<body>
<ContentArea name="Body"/>
</body>
</html>
Your view would then have something like:
<Content name="Head">
<!-- Some Head Content -->
</Content>
<Content name="Body">
<h1>Some Body Here</h1>
</Content>
I don't seem to be able to emulate the same functionality with Code Igniter. The options seem to be:
manually pre-set some associative array of variables (eg in the controller) and then simply substitute the values into a template file - This is a lot of code to repeat in each view and doesn't belong in the controller. It also means it's a real pain to put large bodies of html into one of the ContentAreas - It's either string concatenation or something equally nasty with almost no chance of HTML intellisense in any IDE.
Use a templating library - I haven't found one which doesn't fundamentally work as described above
Now, I haven't used CodeIgniter before and am about to start a large PHP project so want to make sure it's the correct tool before actually starting work. Am I missing something obvious or is this templating functionality difficult to replicate?
Edit: Libraries tested:
Phil Sturgeon's Template Library
CI Smarty
PHXView
If you have a good idea of how your pages are to be built then you can write a set of functions to deal with it either in a MY_Controller.php file or in a library.
So you could have a routine which calls
$this->mypagetemplates();
Which calls data out of a class's properties eg $this->page->title;
I split my data as I create it into
$this->page->head,
$this->page->header,
$this->page->content,
$this->page->aside
$this->page->footer
Which corresponds with the HTML5 sections we use in 90% of our projects
My $this->mypagetemplates() function (or method if you prefer) can take a number of arguments and calls various views as a result eg:
$contentview = 'shop/products';
$asideview = 'shop/basket';
Which, if populated, are then called thus
If ($asideview) {
$this->load->view($asideview, $this->page->aside);
}
Overall Though, I'd say don't design your biggest ever project on a framework that us new to you. Play around first.
I ended up creating 3 files which represented the following
OpenHeader:
<html>
<head>
<Head Stuff/>
OpenBody:
</head>
<body>
<html>
<Templating Stuff>
Close:
</Templating Stuff>
</html>
</body>
</html>
And then modified my views to include these three at the appropriate time.
It's inelegant but flexible and powerful so it'll do for now - especially since I can pass varuables eg Page title into the files during the include if I use the CodeIgniter view engine to retrieve them
I've got an issue with MVC routing(or at least I think it's w/routing :) )...
Just upgraded to MVC RC1, but I'm not sure that it's related as this is my first attempt at setting a MapRoute and corresponding RouteLink.
here's the route:
routes.MapRoute("Test1",
"Forecast/CurrentLineItems/{propertyID}/{forecastYear}/{forecastMonth}",
new { controller = "Forecast", action = "CurrentLineItems", propertyID = "", forecastYear = "", forecastMonth = "" }
);
here's the RouteLink...in the view it's wrapped in a table cell:
Html.RouteLink(Html.Encode(myProperty.Description),"Test1", new { controller = "Forecast", action = "CurrentLineItems", propertyID = myProperty.PropertyID.ToString(), forecastYear = "2008", forecastMonth = "10" })
here's a snippet from the controller:
namespace AnApplication.Controllers
{
[HandleError]
[Authorize]
public class ForecastController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult CurrentLineItems(string propertyID, string forecastYear, string forecastMonth)
{
//Some code
}
Now for the strange behavior, when I click the link specified by the RouteLink, the app enters the CurrentLineItems method and all the method arguments are correct...
then it enters the CurrentLineItems method again!
with, for instance, these arguments:
propertyID = "scripts"
forecastYear = "jquery-1.2.6.js"
forecastMonth = ""
It then repeats this several times as it seems to run through all the scripts on this view and the Site.Master and then the last one is the .css file for this page!
What is going on!
The Call Stack is of no help as it lists the above-mentioned CurrentLineItems method and then below that is the dreaded [External Code]
When I profile the page/view in FireFox/FireBug all I see are the jQuery calls
Here's the html from the Site.Master re the scripts
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title><%= Html.Encode(ViewData["Title"]) %></title>
<script type="text/javascript" src="../../scripts/jquery-1.2.6.js"></script>
<script type="text/javascript" src="../../scripts/calculations.js"></script>
<script type="text/javascript" src="../../scripts/common.js"></script>
<style media="all" type="text/css">#import "../../Content/all.css";</style>
<!--[if IE]><link rel="stylesheet" type="text/css" href="../../Content/ie.css"media="screen"/><![endif]-->
<!--<link href="../../Content/Site.css" rel="stylesheet" type="text/css" />-->
</head>
here's a snippet from the view re the scripts
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"AutoEventWireup="true" CodeBehind="CurrentLineItems.aspx.cs" Inherits="AnApplication.Views.Forecast.CurrentLineItems" %>
<asp:Content ID="lineItemsContent" ContentPlaceHolderID="MainContent" runat="server">
<!--<script type="text/javascript" src="../../scripts/MicrosoftAjax.debug.js"></script>-->
<script type="text/javascript" src="../../scripts/lineItems.js"></script>
<script type="text/javascript" src="../../Scripts/jquery.formatCurrency.js"></script>
<!--<script type="text/javascript" src="../../scripts/jquery-1.2.6.min.js"></script>-->
Note that this ActionLink works fine(It's basically just a menu item used for testing and the three arguments are set in the code inside the controller...):
<%= Html.ActionLink("Line Items", "CurrentLineItems", "Forecast")%>
Any help in solving this issue is greatly appreciated.
Thanks,
Greg
There were in fact two subtle, yet annoying bugs in the recently release ASP.NET MVC Release Candidate. These were the two bugs:
We changed all our URL-rendering methods to render relative URLs instead of absolute URLs. While we feel this might be the right decision in general, we found that it breaks an awful lot of scenarios. AJAX scenarios were especially affected since the URLs for retrieving data asynchronously are often different from the original URL as seen in the browser's address bar.
Html.RouteLink specifically (and not Html.ActionLink) had a bug in it (so it is not in fact a red herring - at least not necessarily). Html.RouteLink would erroneously take the "current" controller and action and pass those values into the routing system. Only Html.ActionLink is supposed to do that. Html.RouteLink is not supposed to do any processing at all. It's supposed to just take the values you give it and pass it along to the ASP.NET Routing system.
Since these two bugs were both pretty bad, we decided to roll back the change that caused #1 and to fix the issue that caused #2 and release an updated ASP.NET MVC Release Candidate Refresh.
You'll see some posts on ScottGu's blog, Phil Haack's blog, and the ASP.NET MVC Forums detailing the refresh.
Thanks,
Eilon
It could be caused by the usage of relative paths to include the scripts.
When you click on the link the URL of the new page will be something like http://[server]/Forecast/CurrentLineItems/xxx/2009/1
Now check the html code in the browser: If the URLs are in the form of '../../scripts/jquery-1.2.6.js', this will cause the browser to load it from http://[server]/Forecast/CurrentLineItems/scripts/jquery-1.2.6.js - and so your controller gets called again.
If that is the case you could either use absolute paths ('/scripts/jquery-1.2.6.js') or use a path relative to the application root ('~/scripts/jquery-1.2.6.js') and resolve it on the server side with Page.ResolveClientUrl().
Maybe there has been a change from Beta to RC1, so that the URLs in the head, even with runat="server", don't get remapped.
RouteLink versus ActionLink is a red herring here. The only thing that matters is the rendered a href="[something]". You would get exactly the same results if you wrote the a href manually instead of generating it via RouteLink.
So, yes, now we're down to routing. Inside your controller action, inspect RouteData in the debugger, and see which route name was matched. Chances are very likely it's the wrong one, and that is causing other things to misbehave. Either change the order of your reps, or add constraints to prevent the wrong one from being matched.
RouteLink works very well to prevent finding the wrong route when you're generating a URL. But when your application is consuming a URL, you still need to have your routes in order in global.asax.
I have tried to use ASP.NET MVC for a while, then I face a problem that I don't want to include all of my js and css in master page. But how can I register it in head of master page from my specific view?
The default master page template includes a Content PlaceHolder for the head. If it doesn't you can easily add one:
<head runat="server">
<title></title>
<asp:ContentPlaceHolder ID="head" runat="server" />
</head>
Your views can then put anything they want in the head:
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<script src="Scripts/myScripts.js" type="text/javascript"></script>
<link href="Styles/myStyles.css" rel="stylesheet" type="text/css" />
</asp:Content>
It doesn't look like there's a simple option 'built-in' to the ASP.NET MVC framework just yet. If you are using a user control (.ascx), which you may be if you are creating self-contained controls which also want to manage their own JavaScript requirements, then you can't even use the placeholders to help you out.
In the end I created a helper class and in it there are a couple of methods:
private static SortedList<int, string> GetRegisteredScriptIncludes()
{
var registeredScriptIncludes = System.Web.HttpContext.Current.Items["RegisteredScriptIncludes"] as SortedList<int, string>;
if (registeredScriptIncludes == null)
{
registeredScriptIncludes = new SortedList<int, string>();
System.Web.HttpContext.Current.Items["RegisteredScriptIncludes"] = registeredScriptIncludes;
}
return registeredScriptIncludes;
}
public static void RegisterScriptInclude(this HtmlHelper htmlhelper, string script)
{
var registeredScriptIncludes = GetRegisteredScriptIncludes();
if (!registeredScriptIncludes.ContainsValue(script))
{
registeredScriptIncludes.Add(registeredScriptIncludes.Count, script);
}
}
public static string RenderScripts(this HtmlHelper htmlhelper)
{
var registeredScriptIncludes = GetRegisteredScriptIncludes();
var scripts = new StringBuilder();
foreach (string script in registeredScriptIncludes.Values)
{
scripts.AppendLine("<script src='" + script + "' type='text/javascript'></script>");
}
return scripts.ToString();
}
That's a basic form of it anyway to try and show the way it works. It could be enhanced in many ways, but at the moment it just filters out duplicate script insert requests for you. Whenever you want to add a new script in the ascx (or aspx for that matter) you can do it this way:
<%
Html.RegisterScriptInclude(Url.Content("~/Scripts/MapLayers/MapLayer.js"));
Html.RegisterScriptInclude(Url.Content("~/Scripts/MapLayers/Vehicles.js"));
%>
Then you need to remember to output them once you're done. This is achieved by making the following call at the place in your page where you want to output the script tags:
<%=Html.RenderScripts() %>
Seems to work so far for me. I did half expect to have rendering issues depending at what point RenderScripts was called, especially if not all of the RegisterScriptIncludes had been called yet, but so far it seems to do the job. If you render the scripts last then you should have no problems.
#Jason: WARNING, you shouldn't be using static variables like this... In a web context static variables are shared across all users and all page requests. I am amazed that you haven't run into trouble with your code. The principle is fine but the code is wrong and will give you trouble. In this case you should be using System.Web.HttpContext.Current.Items. See http://www.hanselman.com/blog/ATaleOfTwoTechniquesTheThreadStaticAttributeAndSystemWebHttpContextCurrentItems.aspx for more.
Here is a solution similar to the one Jason gave, but takes vdh_ant's comments into consideration:
http://frugalcoder.us/post/2009/06/29/Handling-Scripts-in-ASPNet-MVC.aspx
technically you should be putting all your js at the bottom of the page for the best performance.
I think the only way you could do this though would be to include the javascript in the VIewData and have the ViewData displayed on the masterpage (not a great solution).
MVC Futures now has built in helpers for this...
1.<head>
2. <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
3. <%= Html.Css("BlueTheme/site.css") %>
4. <%= Html.Script("jquery-1.3.2.js") %>
5.</head>
More information here: http://blog.osbornm.com/archive/2009/10/12/mvc-script-css-helpers.aspx
Mathew,
I took a look at your blog on Html.Css and Html.Script helpers. I don't mean to be critical but I do not see any mention in the blog about how the Css and Script helpers would address the problem discussed here. The problem here is one of needing to "registered" script references at any point during the rendering process, and possibly multiple times (in the case of an template or partial view that is used several times and registers its own scripts), and then outputting the aggregate results, sans duplicates, in a single location.
If your solution addresses this, please correct me with some clarification.
--Regards,
Ken
I wrote just such a manager for MVC, and wrote about it on my blog:
"JavascriptHelper–Managing JS files for ASP.NET MVC"
UPDATE: I added bundling:
"JavascriptHelper:Managing JS files for ASP.NET MVC (With Bundling)"