I have very werid situation. I have a controller with two methods:
#RequestMapping("/gameDetails")
public String gameDetails1(Model model){
model.addAttribute("game",gameService.findById(1L));
return "gameDetails";
}
#RequestMapping("/gameDetails/{id}")
public String gameDetails2(#PathVariable("id") Long id, Model model){
model.addAttribute("game",gameService.findById(1L));
return "gameDetails";
}
When I open /gameDetails URL page is OK,
but when I use /gameDetails/1 whole page is messy, like there's no CSS at all. Im using html pages with Thymeleaf. I tried with style tags inside html and with external CSS loaded with:
<link rel="stylesheet" href="../static/css/gameDetails.css" th:href="#{/css/gameDetails.css}" />
No success. Do you have any idea what is wrong? How is this even possible?
Related
I am evaluating Thymeleaf and Flying Saucer for pdf generation from templates, and I am having a problem with applying css to my Thymeleaf template. I already read the relevant questions & answers here, here, and here; but none of the suggested solutions fixed my problem.
This is how my resources folder looks like:
So I am using the default directories that Spring will look for. And that's how the head tag looks like in my template.html:
<head>
<title>Spring Boot and Thymeleaf Example</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="../static/css/style.css" th:href="#{/css/style.css}"/>
</head>
If I inline my css in template.html then the generated pdf file will be styled properly (so there shouldn't be a problem with how I generate the pdf). However, when I try to link to the css file as shown above the generated pdf is not styled (so the css is not applied).
Lastly, I can access my css file at http://localhost:8080/css/style.css, so there doesn't seem to be a problem with Spring serving the static content.
For completeness, this is how I generate the pdf:
private final SpringTemplateEngine templateEngine;
private final Log log;
#Autowired
public PdfGenerator(SpringTemplateEngine templateEngine) {
this.templateEngine = templateEngine;
log = LogFactory.getLog(getClass());
}
public void generate(HttpServletRequest servletRequest, HttpServletResponse servletResponse, ServletContext servletContext) {
// Parse the pdf template with Thymeleaf
Locale locale = getLocale(servletRequest);
WebContext context = new WebContext(servletRequest, servletResponse, servletContext, locale);
context.setVariable("user", buildDummyUser());
context.setVariable("discounts", buildDummyDiscounts());
String html = templateEngine.process("template", context);
// Create the pdf with Flying Saucer
try (OutputStream outputStream = new FileOutputStream("generated.pdf")) {
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(outputStream);
} catch (IOException | DocumentException e) {
log.error("Error while generating pdf", e);
}
}
I am using WebContext instead of Context because I was getting the following error with Context:
org.thymeleaf.exceptions.TemplateProcessingException: Link base "/css/style.css" cannot be context relative (/...) unless the context used for executing the engine implements the org.thymeleaf.context.IWebContext interface
What am I missing here, why is my style.css not applied to template.html?
I had same problems and I was also trying to use thymeleaf template resolver for pdf generation. I did lots research on thymeleaf and spring framework, I tried WebContext, I tried HttpServletRequest, I tried some of Spring Thymeleaf integration solutions it was not working either. So I think it was not syntax error, and I finally end up with using absolute path instead of relative.
Url for reference
Here the reason with my assumption, lets say our resources are served on localhost:8080/myapp/css/style.css. And the relative path to request resource is really ups to what context it relatives to.
For eaxmple a normal thymeleaf model Veiw, which return as html pages on browser for client, so the context in that case would be the request hostname, port and application context(eg: localhost:8080/myapp). And relative path will be based on that. So if relative path is /css/style.css, context + relative path will result to be localhost:8080/myapp/css/style.css
Unlike web context, in our case, offline template is on server backend, so the context I assume would be the server running context, which would be the local server path + appcontext(eg: D:/myServer/apps/myapp), relative path /css/style.css on this would be D:/myServer/apps/myapp/css/style.css, this is not make sense. In order to use static resources, I have to pass it's absolute path.
I started use :
<link rel="stylesheet" type="text/css" th:href="#{http://localhost:8080/myapp/css/style.css}"/>
It's working fine but what if there are multiple host names or server is running on a proxy, then this is going to be a hard coded solution. It's better to know what is the real base url the user is requesting. So we can't really get rid off HttpSevletRequest.
Here is my code:
1.Config resource handler:
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**")
.addResourceLocations("classpath:/css/")
.setCachePeriod(31556926);
}
Get base url from HttpServletRequest, you can inject it in method or autowired in your service class, or get from RequestContextHolder. I write this in my Service class:
private static String getCurrentBaseUrl() {
ServletRequestAttributes sra = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest req = sra.getRequest();
return req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath();
}
This is the place I use template engine in my class:
Context context = new Context();
context.setVariable("variales", variables);
context.setVariable("baseUrl", getCurrentBaseUrl());
String content = springTemplateEngine.process("myTemplate",context);
In my template, I use absolute css url like this:
<link type="stylesheet" th:src="#{|${baseUrl}/css/style.css|}" />
Syntax looks fine so the problem is not with the syntax.
Also you cannot use #{...} syntax without an IWebContext interface so You are getting this exception.
I had a similar problem - my css was not applied to my template page.
My problem was that the css file was in css sass format
.table
margin: 0 0 40px 0
when I convert it to the normal css format like
.table {
margin: 0 0 40px 0;
}
it worked
I solved this problem by changing the path structure in href. I had the same directory structure as you (html files are in templates doc, css files are in static doc).
<head>
<title>Spring Boot and Thymeleaf Example</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<link rel="stylesheet" type="text/css" href="/css/style.css"/>
</head>
It might help you to apply css to your html page.
I found a lazy man's way of taking care of this. It works, with a very simple approach. The 'inserted' fragment is just a CSS style tag in the body of a simple HTML document. I place this in the HEAD of my target file, right where I would have put the LINK REL tag:
<th:block th:insert="std-reports/std-reports-css-fragment.html :: style"></th:block>
I want do add an ID to a URL in my template:
#Controller
public class DashboardController {
#RequestMapping("/dashboard")
public String index(Model model){
model.addAttribute("provider_id", "1");
return "dashboard";
}
}
In my dashboard.html I want do display the ID.
<a th:href="#{'/cloudservice/' + ${provider_id}}">Show</a>
But the Generated URL is /cloudservice/null. Why isn't the 1 displayed that i put into the model?
I tested this out and I couldn't recreate this issue, this is working perfectly fine for me. Please share the complete controller code and your dependencies here.
One wild guess, your Model class is of type org.springframework.ui.Model, right?
you can use like this http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html#link-urls
<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
view
On my main page, I have the code #{Html.RenderPartial("_Partial1.cshtml");}, and on my Partial, I have an HTML string:
#{
// The string is actually dynamic, not static. This is here for simplicity
string abc="<div class=\"error\">abc</div>";
}
#abc
I want to output abc with some CSS error styles, but I actually got <div class="error">abc</div> - of course, no styles there. How do I make it interpreted as HTML source code and not a string?
You can use the Html.Raw() method for that.
And if you are using model in your view than use:
#model YourApp.Models.YourModel
....
#Html.Raw(#Model.NameOfYourProperty)
I'm working on setting up a shared content (navigation) for an asp.net MVC layout page.
Here is my partial view "_LayoutPartial.cshtml" with code to pull navigation data from a model.
#model MyApp.Models.ViewModel.LayoutViewModel
<p>
#foreach (var item in Model.navHeader)
{
//Test dump of navigation data
#Html.Encode(item.Name);
#Html.Encode(item.URL);
}
</p>
Here is how the code for my controller "LayoutController.cs" looks like.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MyApp.Models.ViewModel;
namespace MyApp.Controllers
{
public class LayoutController : Controller
{
//
// GET: /Layout/
LayoutViewModel layout = new LayoutViewModel();
public ActionResult Index()
{
return View(layout);
}
}
}
Here is the code for the "_Layout.cshtml" page. I'm attempting to call the partial view here using Html.RenderAction(Action,Controller) method.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<p>
#{Html.RenderAction("Index","Layout");}
</p>
#RenderBody()
</body>
</html>
When the layout page executes the #{Html.RenderAction("Index","Layout");} line, it throws out an error message "Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'."
What am I missing friends? How can I call a partial view in a layout page?
Thank you all in advance!
Instead of:
public ActionResult Index()
{
return View(layout);
}
do:
public ActionResult Index()
{
return PartialView(layout);
}
If you don't do that when you return a normal view from your child action, this normal view attempts to include the Layout, which in turn attempts to render the child action, which in turn returns a view, which in turn includes the Layout, which in turn attempts to render the child action, ... and we end up with names like the one ported by this very same site.
Also in your partial you don't need to do double encoding. The # Razor function already does HTML encode:
#model MyApp.Models.ViewModel.LayoutViewModel
<p>
#foreach (var item in Model.navHeader)
{
#item.Name
#item.URL
}
</p>
First verify that your child view is inside the Shared directory
#Html.Partial("_LayoutPartial")
OR
#{Html.RenderAction("actionname", "controller name");}
And don't use #Html.Encode(), Razor is already doing for u. Just use
#item.Name
#item.URL
I have solved this error getting on Layout page
System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper
Important !
First create partial view inside shared folder
In Controller ,
public PartialViewResult Userdetails()
{
....
return PartialView("PartialViewName", obj);
}
In Layout Page,
#{Html.RenderAction("action","controller");}
I know this is an old question but I thought I would throw this in here. You can use either Html.Action or Html.RenderAction. They both technically do the same thing but depending on how much content you're returning back can have an impact on which one you should really use for best efficiency.
Both of the methods allow you to call into an action method from a view and output the results of the action in place within the view. The difference between the two is that Html.RenderAction will render the result directly to the Response (which is more efficient if the action returns a large amount of HTML) whereas Html.Action returns a string with the result.
Source
I used the following tutorial to help me build an RSS Reader in my ASP.NET MVC3 Razor application:
http://weblogs.asp.net/jalpeshpvadgama/archive/2011/08/17/creating-basic-rss-reader-in-asp-net-mvc-3.aspx
However, unlike the tutorial example, I want the RSS feed to be displayed on every page, and have therefore added it to my layout file, /Views/Shared/_Layout.cshtml
I currently only have 2 views on my site, and to get the RSS Reader to work on both views I've got the following code in my HomeController:
public class HomeController : Controller
{
//
// GET: /Index/
public ActionResult Index()
{
return View(CT.Models.RssReader.GetRssFeed());
}
public ActionResult About()
{
return View(CT.Models.RssReader.GetRssFeed());
}
}
From my WebForms experience, I would simply add the RSS Reader code in my master page code behind, and it would automatically work on every page.
Is there a Controller for layout pages which allows me to do the same?
How can I get this to work on every call of the layout page, without having to return anything?
EDIT: Following #Sebastian's advice, I've now added this code to a Partial View, removed CT.Models.RssReader.GetRssFeed() from return View() and included this in my layout file:
#Html.Partial("_MyPartialView")
The code in this partial view is:
<ul>
#foreach (var item in Model)
{
<li>
#item.Title
</li>
}
</ul>
However, I'm not getting a runtime error:
Object reference not set to an instance of an object.
It's erroring on the line #foreach (var item in Model)
You have to create a partial view and add functionality there.
Then in your layout, render this partial.
EDIT
Is your partial view really a partial view? The reason I said that is because you have "_" in front of the name which suggests that it might be a layout (might just be a naming convention).
To fix object reference error, you have to add the #Model declaration on top of your partial view.
Hope it helps.
UPDATE
In order to use different model in partial view, you need to explicitly declare which model you are going to use on render partialmethod.
#{Html.RenderPartial("../YourFeed", Model.YourFeedModel);}
Let me know if that resolved your issue.
The new error you are having is due to you not passing a Model to the partial view. You can do this with the second argument of the Html.Partial function...
Html.Partial("ViewName", MyModel);
As I think you are trying to do this in a Layout page you could also consider using a static reference to get your RSS feed. So forget about needing to pass in a Model and in your partial have:
#foreach (var item in RssRepository.GetFeed())
{
<li>
#item.Title
</li>
}
this like to a class something like...
public static RssRepository
{
public static MyModel GetFeed()
{
return new MyModel();//<- return what you would normally pass as a Model for RSS feeds
}
}
Hope that all makes sense