Imagine you layout page of your grails application has some dynamic content that gets update on each page request. When a page is requested, the controller that is requested has nothing to do with the dynamic data provided by the main.gsp page. How should I manage this?
I mean when a page is requested, how do I update the dynamic portion of the layout portion that the page controller is agnostic about? Plus I do not want to put layout dynamic code is every controller.
You might be able create a TagLib for this.
grails create-tag-lib myLayout
Then make your taglib responsible for the remote service calls. You can do pretty much anything in a taglib that you can to in a controller, and you can also call any Grails services you've already created.
class MyLayoutTagLib {
static namespace = "myLayout"
def stockQuoteService
def getStockPrice = { attrs ->
out << stockQuoteService.getLatestPrice(attrs.stockSymbol)
}
}
Then in your gsp
<myLayout:getStockPrice stockSymbol="${user.favoriteStockSymbol}" />
Or however you get the relevant data to make the remote service call.
Related
I'm using Grails 1.3.8 (old but gold). In my view I render a template with the same variables as of the containing view via:
<g:render template="areaInfoTemplate" model="${pageScope.variables}"/>
This works unless I try to access methods on domain classes inside the template:
<g:if test="${currentState == State.findByName('foobar')}">...</g:if>
Leads to:
Exception Message: Cannot invoke method findByName() on null object
If I access State in the main view gsp everything is ok.
My domain class is:
class State {
String name
String value
static constraints = {
name(nullable:false,blank:false,size:0..255)
value(nullable:false,blank:false,size:0..255)
}
}
Grails isn't PHP - please don't make database calls from the view layer (unless you like to fight artificial, self-inflicted scalability issues). Just make the calls in a controller or service and pass the data to the view to be rendered. GSPs should only be responsible for rendering output, not higher-level concerns like database access.
I forgot to add an import statement at the beginning of the file:
<%# page import="my.wonderful.package.State"%>
I have a pretty standard MVC solution with _Layout.cshtml, Index.cshtml, etc. I'm trying to load some required bundles in the Layout only one time, so that when I change views, I only get what I need from the server, not what I already have (bundles, layout html, etc).
Following this post: MVC 3: How to render a view without its layout page when loaded via ajax?
I now have an updated _ViewStart. It doesn't seem to be working though:
With the following JS viewmodel being loaded as part of the bundle in _Layout:
var subjectservice = new baseservice(); // baseservice is an empty function
subjectservice.subjects = {};
subjectservice.getSubjects = function () {
alert('Hit');
subjectservice.subjects = 'data';
}
subjectservice.getSubjects();
The intended behavior is that:
I get Subjects when I initially load the application (with _Layout), no matter what page I'm on
I don't call getSubjects again when navigating - only when I specifically call it from other viewmodels
However, no matter what page I navigate to, I always get the alert message, even if Layout has already loaded. Viewing the network panel in Chrome Debugger shows all of my bundled js files are getting reloaded with every page load.
An example of a view that I'm trying to load without re-loading bundles and _Layout:
public ActionResult Index()
{
return View();
}
How can I load Layout and its bundles only once?
What I believe you're referring to is the ability to use Ajax and Pushstate.
Luckily there's a great OSS lib for this.
https://github.com/defunkt/jquery-pjax
Do the following to your _ViewStart.cshtml
#{
Layout = Request.Headers["X-PJAX"] != null ?
"~/Views/Shared/_PjaxLayout.cshtml" :
"~/Views/Shared/_Layout.cshtml"; // uses the _Layout.cshtml for unsupported browsers
}
More info here
http://chrisseroka.wordpress.com/2012/04/24/getting-starteg-with-pjax-and-asp-net-mvc/
I have a grails webflow that works fine for my desktop browser app. Now I'd like to reuse the webflow for my mobile site. I'd prefer not to have 2 separate webflows and just change the pages that are used for each state. I tried the following:
viewState{
String view = 'viewState'
boolean isMobile = this.isMobileUser()
if(isMobile){
view = "/flowDir/mobile/viewState"
}
render(view:view)
}
However this isn't working. If the mobile site is accessed first then the desktop will get the mobile pages and vice versa.
Has anyone encountered this problem? I'd really hate to have 2 webflows that do the same exact thing. I'd also hate to hack into Sitemesh. Any ideas or suggestions on how to reuse this flow would be greatly appreciated.
Try putting the mobile view resolution into an after filter so that your web flow executes the same, but you intercept the view being rendered and rename it.
http://adammonsen.com/post/548
I agree with droggo that responsive design is the best solution.
However, as an alternative, you could potentially have all webflow users going to the same view, but within that view itself you could override the g:layout taglib with a taglib of your own that is able to determine whether you're running as a mobile user or not, and hence can apply an appropriate layout.
It's maybe a little heavy handed in this context, but it would give you a solution that would be reusable across your entire app, not just within webflow, and it keeps the mobile-specific code in one place.
So in your webflow:
viewState{
String view = 'viewState'
[snip] The rest of your code goes here
render(view:view)
}
Then your view:
<myapp:applyLayout name="someWebflowLayout">
[snip] Your non-layout GSP code goes here
</myapp:applyLayout>
Then your taglib:
class MyAppTagLib {
static namespace = "myapp"
def applyLayout = { attrs, body ->
boolean mobileUser = false
[snip] some logic to determine if this is a mobile user or not goes here
if (mobileUser) {
attrs.name = "mobileLayout/${attrs.name}"
} else {
attrs.name = "desktopLayout/${attrs.name}"
}
out << g.applyLayout(attrs, body)
}
}
You would then have two layout files called someWebflowLayout - one within /layouts/mobileLayout and one within /layouts/desktopLayout (although I appreciate these might not exactly match your existing structure)
The code in the taglib above is only a rough guide, and it'd need bolstering to deal with the other parameters that g:applyLayout takes.
I have a master template that calls "RenderBody()" and a "OneIn" template that is a child under the Master page. I also call the RenderBody() function in the OneIn template but then it gives me this error:
The file "~/Views/OneIn.cshtml" cannot be requested directly because it calls the "RenderSection" method.
The "RenderSection" was i actually "renderbody" i was just trying something new, and it didn't work either
Nest Path:
Master
-> Homepage
-> OneIn
-> One Column Page
I also have these lines on code on top of all pages:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "ParentPageName.cshtml";
}
It seems like you have an action method that is trying to return the "onein" layout directly, which can't happen if you are calling RenderSection or RenderBody inside of it. Thank of any View that has RenderSection/Body in it as an abstract class that can't be new-ed up but instead have to be inherited!
Yes, I found this as well which is a bit jarring when coming from Master Templates. You can have many levels of template inheritance, but your content pages can only use templates that are at the lowest level. That would be One Column Page in your example.
I have some experience maintaining Grails apps; now creating a "task management" application as an exercise.
Apparently there is a view dichotomy of Groovy Server Pages versus Controller actions that render a view, as evidenced by this snippet from a URLMappings.groovy example:
static mappings = {
// ..
"/" (view:'/index')
"/login/$action?" (controller: 'login')
"/logout/$action?" (controller: 'logout')
"500" (view:'/error')
}
where user-facing URLs must be mapped to either views (GSPs) or controllers rendering a view, e.g.:
class LoginController {
/**
* Show the login page.
*/
def auth = {
// .. auth logic
String view = 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
render view: view, model: [postUrl: postUrl, rememberMeParameter: config.rememberMe.parameter]
}
}
From a design perspective, how do I choose which method to use? When do I create views with GSPs/taglibs like typical server pages outputting HTML, and when do I map a URL to a controller that renders through a delegate GSP? Can I combine both approaches? Have I oversimplified the options here?
To add to what hvgotcodes said, related to your question, the only time you'd want to map directly to a GSP view is when that view is effectively "static".
By static I mean that it isn't relying on the database or any real calculations for rendering the view. It can still be dynamic in that it relies on tag libraries for dealing with common elements, and things like the "Welcome user" text at the top of pages.
As soon as you want to deal with user-supplied input, looking up database information, manage more complicated URLs, or include calculations, you should be using a controller.
The end goal is that GSPs only contain visual and layout information, as well as the occasional static block of text. But you should always avoid mixing any logic in with the GSP, because it clutters the code and always leads to maintenance headaches later on.
Edit regarding Tag Libraries:
As I wrote below:
Tag libraries are for any logic that is connected to the view, like looping over elements, or toggling the visibility of something. Whenever you are tempted to put code directly into your GSP, it probably should be put in a tag library. Of course, there are always exceptions for one-offs.
So, if you have logic code in your view, that specifically relates to visual or layout content, that should be put in a tag library. A good example is the <sec:ifLoggedIn> tag from Spring Security Core, which can be used to toggle the visibility of an element if the user is logged in. This is much better than writing it manually like so:
<sec:ifLoggedIn>blah blah</sec:ifLoggedIn>
<g:if test="${session.user?.loggedIn}">blah blah</g:if>
Because it makes the purpose clearer (by its title), as well as abstracting the logic away, so if you later need to change the way something works, you only have to change it in one place.
tl;dr:
GSPs - Simplified "static" content
Tags - Reusable dynamic components specifically for visual or layout content
Controllers / GSPs - dynamic content
I don't think it's a dichotomy. GSPs and Controller actions (are intended to) work in tandem, the controller invoking services to load data in preparation for passing that data to the appropriate GSP.
The url mapping stuff is for if you want to break the Grails convention for urls, which is orthogonal to how loading data and displaying data (are supposed) to work.
The only time (IMHO) there is a dichotomy is when developers in a project code functionality inconsistently; i.e. it is certainly possible to give the appearance of a dichotomy.