Grails render() with a fragment parameter - grails

Is there a way to use render() with a fragment param so on page load it automatically scrolls to a specific part of the page? Similarly to how we can call
redirect(controller: "book", action: "show", fragment: "profile")

You cannot pass it to render(), because by the time you're actually invoking render(), the URL has already been determined and mapped to your action; all render is doing is controlling what gets written back to the response.
The fragment must already be in the URL before the rendering controller action gets called. Here's an example:
grails-app/controllers/MyController.groovy
class MyController {
def foo = {
render(view: 'foo')
}
def quux = {
redirect(action: 'foo', fragment: 'baz')
}
}
grails-app/views/my/foo.gsp
<html>
<head>
<title>Foo</title>
</head>
<body>
<a id="bar">Bar</a>
<g:each in="${0..100}"><br/></g:each>
<a id="baz">Baz</a>
</body>
</html>
With various URLs:
http://example.com/myapp/my/foo - doesn't scroll to an anchor
http://example.com/myapp/my/foo#baz - scrolls to the 'baz' anchor
http://example.com/myapp/my/quux - scrolls to the 'baz' anchor'

There is no way to specify a fragment directly with a grails render call, but in my code I'm using a work around that seems to provide most of the desired functionality with only a little extra complexity. The trick is to pass in the desired fragment reference as a part of the model, and then to act upon that reference in the GSP page. Thus, the render call in my controller looks like this:
def foo() {
render(view : 'foo', model:[fragment:'myFragment'])
}
then, down in the GSP, I access the model with the following Javascript:
<g:javascript>
if ("${fragment}") {
window.location.href = "#${fragment}";
}
</g:javascript>
The GSP will then tell your browser to jump forward to the desired anchor in the page (if any).

Related

Controller is there, but showing "Error code 404 page not found" in grails

new.gsp:
<html>
<head>
</head>
<body>
<g:form name="myForm" controller="New" action="index" params="username:username">
<div>
<fieldset class="form">
<label for="name">Name:</label>
<div>
<g:textField name="username" value="${params.userName}"/>
</div>
</fieldset>
</div>
<g:submitButton name="Submit" value="Submit" />
</g:form>
</body>
<html>
NewController.groovy:
package sample
import com.sun.org.apache.bcel.internal.generic.NEW;
class NewController {
def index = {
if($params.userName){
render(view:"/login.gsp")
}
}
}
login.gsp is a simple page, having a simple welcome note.
if some body the solution please reply,
thanks in advance.
by prasanth
Change your controller name to "new" instead of New in
It will work.
Or else you can modify your "save" action in controller, so that when you click on save button new page will be rendered.
There are a few issues in the posted code that will cause you problems:
You're accessing the parameters with $params instead of params. The $ character is only necessary when you are in a GString. e.g. def foo = "your username is ${params.userName}"
Your view is named new.gsp but your action is named index. Grails will by default look for a view matching the action name in a directory named for the controller. In other words, since you don't tell it explicitly to render /new.gsp grails will look for /new/index.gsp. You can either rename the view to /new/index.gsp or tell grails to render the view new in the index action.
When attempting to render your logged in page, you're calling render(view: 'login.gsp'). The gsp extension is not necessary when calling the render tag. You're intended to use the grails view name, not the filename. render(view: 'login')
If you're using a recent version of grails (>2.0) you should be using controller methods rather than closures. e.g. def actionName() { } as apposed to def actionName() = { }. The reasoning is in the grails documentation.
Here's what it could look like with all the issues addressed:
rename new.gsp to /new/index.gsp. rename login.gsp to /new/loggedIn.gsp.
controller:
class NewController {
def index() {
if (params.userName) {
forward action: 'loggedIn'
return // render and forward don't end flow control
}
}
def loggedIn() {} // no logic, automatically renders '/new/loggedIn.gsp'
}
Add a handler to your controller named login.
def login = {}
If the view file is new.gsp then you need your action to also be new or else have a URL mapping (in UrlMappings.groovy) to do something like:
"/new" {
controller = 'new'
action = 'new'
}
Or you can set
static defaultAction = 'new'
...in your NewController.
Then Grails will find the appropriate action on your controller.
if your action is called index, you can acces the page on
localhost:8080/webapp/NewController

Messages are not rendered after I changed page mapping from controller methods to explicit view page

I changed mapping of my page in UrlMappings to the following value:
name producingCountry: "${adminArea}/producing-country/$id" (controller: "producingCountry", view: "/producingCountry/show") {
constraints { id(matches: /\d+/) }
}
The page (/producingCountry/show) is as simple as:
<%# page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<meta name="layout" content="main">
<title><g:message code="producingCountry.show.title"/></title>
</head>
<body>
</body>
</html>
The message is not rendered any more. It worked fine when its url was mapped to controller method 'show' with the very last line like:
render [producingCountry: (country)]
How can I fix this issue?
Use this mapping
"/${adminArea}/producing-country/$id" (controller: "producingCountry", view: "show")
When controller is specified, the view takes the name instead of the relative path.
UPDATE
I am not sure about how adminArea is evaluated, but the below works for me:
//UrlMapping:- [mark the "/" in the beginning of the mapping, it is required to append to root context]
name producingCountry: "/admin/producing-country/$id" (controller: "producingCountry", view: "show") {
constraints { id(matches: /\d+/) }
}
//show.gsp
Exactly same (copied) from your question.
//messages.properties
producingCountry.show.title=United States
Gives me a page with title United States when below url pattern is hit:
http://localhost:8080/DemoUrlMappingApp/admin/producing-country/123
I think you shouldn't specify controller parameter when you specify a view name in urlMapping definition, try this:
"/${adminArea}/producing-country/$id" (view: "/producingCountry/show")

Grails UI(plugin) dialog not firing the controller action

Happy new year to all,
I am working on a project where I have to show the details of each record in the "list" in a dialog (modal window) window while the user click the link on each record in the list. I am trying to accomplish this using the GrailUI plugin. Here is my code:
<gui:dialog width="300px"
controller="tag"
action="showTags"
params=" [id:userInstance.userId]"
update="dialogData"
draggable="true"
triggers="[show:[type:'link', text:'Show Tags', on:'click']]"
modal="true">
<div id='dialogData'>This will be updated by the controller action....</div>
For some reason the dialog tag is not firing the controller action. It opens the dialog window, but shows just this message "This will be updated by the controller action....". It's not showing the controller action rendered output(view). Could someone help me to understand what I am doing wrong?
Jquery and jquery-ui are the other plugins I am using in my project.
Appreciate your help.
Edit
def test(Integer max) {
....
....
userInstanceList = User.list(params)
render (view: "test", model: [userInstanceList: userInstanceList, userInstanceTotal: User.count()])
}
def showTags () {
def user = User.findByUserId(params.id)
def tagInstanceList = user.tags
render(view: "test", model: [tagInstanceList: tagInstanceList])
}
If you want something be submit to remote you need to set form="true". Then any form elements can be placed inside the dialog tag without defining a form. When form="true", the dialog creates its own form.
Here is a example I have tested:
test.gsp:
<html>
<head>
....
<r:require modules="grailsui-dialog"/>
</head>
<body class="yui-skin-sam">
<gui:dialog width="300px"
controller="test"
action="showTags"
params=" [id:userInstance.userId]"
form="true" <!-- the key to remote submit -->
update="dialogData"
draggable="true"
triggers="[show:[type:'link', text:'Show Tags', on:'click']]"
modal="true" >
<!-- You can put any input element here, which will be submitted in the form-->
<div id='dialogData'>This will be updated by the controller action....</div>
</gui:dialog>
</body>
</html>
TestController:
class TestController {
def test() {
.........
}
def showTags() {
def user = User.findByUserId(params.id)
def tagInstanceList = user.tags
render(template: "ajaxResponse", model: [tagInstanceList: tagInstanceList, user:user]) //render a template, not a view
}
For an Ajax request, you can not render a view, which will replace the original page. Instead, you should send back a template with the tags you want to show in the dialog:
_ajaxResponse.gsp
<h3>Tag List of user ${user.username}</h3>
<g:each in="${tagInstanceList}" var="tag">
<p>${tag}</p>
</g:each>

Grails - URL mapping/default action and flow

Question -
I've noticed that some applications I test with have calls to another view/controller from an action submit, but when that page is rendered, instead of seeing:
$controller/$page
I see:
$controller/index
Is this an issue with the URL mapping configuration? Default action? Just curious, because it just appears to be the URI mapping to a default instead of the actual action.
view code:
<table>
..
<g:actionSubmit class="stats" action="stats" value="View Stats"/>
..
</table
controller:
def stats() {
def teamId = Team.get(params.id)
def allPlayers = Player.withCriteria {
eq('team', teamId)
and {
eq('isActive', true)
}
}
[allPlayers:allPlayers, teamId:params.id]
}
UrlMapping:
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
}
Edit
I actually figured out what it is. Which makes me even more confused.
The grails actionSubmit has an action tied to it. That form was just a normal form, without call:
<g:form>
<g:actionSubmit class="stats" action="stats" value="View Stats"/>
<g:actionSubmit class="schedule" action="schedule" value="View Schedule"/>
<g:form>
So by default, the form redirects the action to $controller/index. If you add an action call in the g:form tag, those two buttons will direct to the correct page, but the URI will now be $controller/$g:form_action.
I guess I don't get the point of the actionSubmit's action if the g:form is needed as a wrapper.
Yes, index is the default action for all controllers. So if you do not specify one, that is the page you will land on for the controller.
It is discussed in further detail on their website. Namely, the rules are:
If only one action is present the default URI for a controller maps to
that action.
If you define an index action which is the action that
handles requests when no action is specified in the URI /book
Alternatively you can set it explicitly with the defaultAction property:
static defaultAction = "list"

Grails "render" renders the template

In my Grails controller I'm responding to an AJAX call and using render to return the text:
def ajaxRandomPersonName = {
def person = get a random person ...
render "Name: ${person.name}"
}
The problem is that render renders the whole template. So instead of just rendering "Name: John" it renders all the icons, navigation, etc defined in the template. How do I get render to just render without the template?
I'm pretty much following Chapter 1 of "Grails in Action" (page 28) using Grails 1.1.1.
Follow up:
Returning false per Rhysyngsun's suggestion has no impact. I also tried setting the template to null but it still renders the template:
def ajaxRandomPersonName = {
def person = get a random person ...
render (template:null, text:"Name: ${person.name}")
}
render has its heart bent on rendering it through the template no matter what I do.
Follow up 2: Parallel discussion on grails-user mailing list.
Follow up 3: Sample code:
I paired down my code the bare minimum and it still exhibits the undesired template rendering.
controllers/PersonController.groovy:
class PersonController {
def index = { }
def home = { [message:"Hello"] }
def ajaxTest = {
println "ajaxTest called"
render text: "ajax message"
}
}
views/person/home.gsp (view page for home method)
<html>
<head>
<title>Home View</title>
<g:javascript library="prototype" />
</head>
<body>
<p>
<g:remoteLink action="ajaxTest" update="test1">ajax call</g:remoteLink>
</p>
<p>Message = ${message}</p>
<p id="test1">Blank</p>
</body>
</html>
views/layouts/person.gsp (layout template for person controller)
<html>
<head>
<title>Test App - <g:layoutTitle/></title>
<g:layoutHead/>
</head>
<body>
<h1>Test App</h1>
<g:layoutBody/>
</body>
</html>
I access person controller with the home view:
http://localhost:8080/test/person/home
the page renders as:
Test App
ajax call (hyperlink)
Message = Hello
Blank
"Test App" is from the template. When I click "ajax call" it makes an asynchronous call to PersonController's ajaxTest method (verified with println). All ajaxTest does is println and render static text. This resultant in the following:
Test App
ajax call
Message = Hello
Test App
ajax message
Note that the template is being rendered within "test1" <p> which results in the second "Test App".
I'm running Grails 1.1.1. Any ideas? The code seems straightforward. I downloaded the Grails source and looked at RenderDynamicMethod.java. It doesn't do any template rendering unless template is in the argument list, which it isn't. So my only guess is something up steam is rendering the template again.
Resolved: Adding contentType results in the template not being rendered:
render text: "Name: ${person.name}", contentType: "text/plain"
Make your client side javascript code handle a JSON respond and render your response with:
render [text:"Name: ${person.name}"] as
JSON
You might be getting burnt by the 'layout-by-convention' feature in Grails. If your layout name matches the controller name prefix, for example, Grails will apply the layout to every view managed by that controller. Unfortunately, it even applies to text and templates. There are currently a few JIRAs logged regarding this (see http://jira.grails.org/browse/GRAILS-7624 for example).
I got burnt by this today. I resolved it by simply renaming my layout gsp such that it doesn't match any controller name. My layout was initially named 'storefront.gsp' and I have a controller named StorefrontController. I renamed the layout to 'public.gsp'.
We've found that explicitly returning false from the action fixes this.
I believe doing render foo as JSON returns false implicitly.

Resources