I am new to Grails and I am trying to get a very simple example to work. I should just submit a form and display "Hello World" on the screen. It consists of the following controller:
package surface
class SearchController {
def index() {
render(view: "search")
}
def result() {
render "Hello World"
}
}
and a view, with the form:
<%# page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<g:form name="searchform" url="result">
<g:textArea cols="80" rows="30" name="searchfield"/>
<g:actionSubmit value="Ask"/>
</g:form>
</body>
</html>
When I click on "Ask" I get a 404 error but the browser correctly accesses "/surface/search/result". When I enter that address directly without using the form the "Hello World" appears correctly. This is probably a no-brainer but I seem to be unable to find out why this does not work from the documentation.
Complementing the #Tom Metz answer, what you need to keep in mind in the Grails controller structure is that every public method is considered an action. This action is mapped to a url. In your example will exists /search/index and /search/result (controller + action).
The documentation of the g.form is corret, since this says that;
url (optional) - A map containing the action,controller,id etc.
So to correct your view you can set the action as commented or you can adjust the way you use url:
<g:form name="myForm" url="[action:'result',controller:'search']">
Related
in my grails application, I have a controller that reads a directory and returns a list of files in it.
My issue is that in my GSP (view) it is not matching the out put from the controller.
Here is my controller:
package frametest
import groovy.io.FileType
class Read_dirController {
def index() {
def list = []
def dir = new File("D:\\TestRepoLocal\\APIAutomation\\src\\test\\cucumber\\features")
dir.eachFileRecurse (FileType.FILES) { file ->
list << file
}
list.each {
println it.name
}
def found = []
dir.eachFileMatch(FileType.ANY, ~/.feature/) {
found << it.name
}
render(view: "index", model: [name:name])
}
}
here is my GSP (view):
<%# page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<meta name="layout" content="main"/>
<title>Welcome to The Marriot test Page</title>
</head>
<body>
<div class="body">
<h2>Marriott Automation Test Page</h2>
<br>
<p></p>
<h3>This is where we can launch our tests and get reports</h3>
<br>
<br>
<p></p>
${name}
</div>
</body>
</html>
The output should just list the file names. It does in the controller output (shown in console), but in the view it shows this:
[D:\TestRepoLocal\APIAutomation\src\test\cucumber\features\APIWAL_525_Account_Security_otp.feature, D:\TestRepoLocal\APIAutomation\src\test\cucumber\features\AssignRoomToReservation.feature, D:\TestRepoLocal\APIAutomation\src\test\cucumber\features\DeleteAccount.feature, D:\TestRepoLocal\APIAutomation\src\test\cucumber\features\DeleteConsumersPaymentMethods.feature, D:\TestRepoLocal\APIAutomation\src\test\cucumber\features\GetActivitiesByID.feature, D:\TestRepoLocal\APIAutomation\src\test\cucumber\features\GetAddressType.feature, D:\TestRepoLocal\APIAutomation\src\test\cucumber\features\GetAddrMiscType.feature, D:\TestRepo
Local\APIAutomation\src\test\cucumber\features\GetAlerts.feature,
the console output for the controller shows this:
APIWAL_525_Account_Security_otp.feature
AssignRoomToReservation.feature
DeleteAccount.feature
DeleteConsumersPaymentMethods.feature
GetActivitiesByID.feature
GetAddressType.feature
GetAddrMiscType.feature
GetAlerts.feature
GetAttractionsByID.feature
What do i need to do to make the view match the controller output from console??
thanks!
ironmantis7x
UPDATE!!!!!
to solve my listing issue I did this:
I changed the controller file to do this little trick:
render(view: "index", model: [name:list.name])
then to make the gsp list the file names on a new line, I did this:
<ul>
<g:each in="${name}" var="fileName">
${fileName}<br>
</g:each>
</ul>
And presto!!
This is where we can launch our tests and get reports
APIWAL_525_Account_Security_otp.feature
AssignRoomToReservation.feature
DeleteAccount.feature
DeleteConsumersPaymentMethods.feature
GetActivitiesByID.feature
GetAddressType.feature
GetAddrMiscType.feature
.....
Thanks guys for encouraging me to struggle to learn it and helping me along the way!
Your gsp is rendering the list but your list of names is in the variable found, not name. Anyway, your last action line should be:
render(view: "index", model: [name: found])
In other hand, your gsp is rendering the list, but should give it some style. An example:
<ul>
<g:each in="${name}" var="fileName">
<li>${fileName}</li>
</g:each>
</ul>
You are storing your o/p in field named found, but in model you are using field name to be included as name, which doesn't contain your o/p. You haven't declared name, or have added anything to it, idk how it is showing that o/p even on gsp.
I have a list with two buttons and I want that everytime that I press a button, work the code that I have associated to this button. This code is in a service, and the gsp file is a default list.gsp
To call the service from the gsp. I took this post:(How do I call a Grails service from a gsp?) to do it.
The problem is that I have two methods in the service and I don't know why, but always that I enter in the list.gsp and always that I press the button I can see display in the console of grails that the two methods are working at the same time.
This is that I can see in the console everytime I push button A or B:
2015-12-01 12:51:47,565 [http-bio-8090-exec-5]
hello
world
That I want is that if I press button A show this:
2015-12-01 12:51:47,565 [http-bio-8090-exec-5]
hello
and if I press button B show this:
2015-12-01 12:51:47,565 [http-bio-8090-exec-5]
world
And this is that I can see in the console everytime that I run and enter in the gsp.file and I want that don't run the code unless I press the button:
2015-12-01 12:51:47,565 [http-bio-8090-exec-5]
hello
world
Thanks in advance
This is my code in gsp:
<%# page import="com.app.test" %>
<%# page import="mypackage.myService" %>
<!DOCTYPE html>
<html>
<head>
<meta name="layout" content="main">
<g:set var="entityName" value="${message(code: 'licenseType.label', default: 'LicenceType')}" />
<title><g:message code="default.list.label" args="[entityName]" /></title>
</head>
<body>
<g:message code="default.link.skip.label" default="Skip to content…"/>
<div class="nav" role="navigation">
<ul>
<li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
<% def LicenceTypeService =
grailsApplication.classLoader.loadClass('mypackage.myService').newInstance()%>
<li><g:link action="list"><g:message code="Calculate Licence Type" args="[entityName]" /><span>${myService.calculate()}</span></g:link></li>
<li><g:link action="list"><g:message code="Re-Calculate Licence Type" args="[entityName]" /><span>${myService.recalculate()}</span></g:link></li>
</ul>
</div>
In the service:
class MyService {
def calculate(){
println "hello"
}
def recalculate(VoiceUser vUser){
println "world"
}
}
I'm fairly certain this is impossible; what you are asking is to send an Ajax call directly to a grails service. If you think about it, once the generated HTML has been received by the client, there is not a direct connection between the browser and back-end (grails application) so there can't be any grails magic that allows this to happen.
There is a grails tag g:remoteLink that will allow Ajax calls to a controller action, the controller is necessary at this point because all new interactions once the generated html is in place must be through standard HTTP requests and requests are handled through grails controllers.
Your best bet would be to create a controller that wraps the 2 service methods and use the actions with g:remoteLink or another Ajax request.
As to using a service within a gsp, instead of instantiating a new service, I would argue in favor of using the existing spring bean.
Replace:
<%# page import="mypackage.myService" %>
...
<% def LicenceTypeService = grailsApplication.classLoader.loadClass('mypackage.myService').newInstance()%>
With:
<g:set var="myService" bean="myService"/>
New to Grails and this may be simple problem.
I am trying to save some values to database, so I created a simple form with two text fields and a button in GSP (info.gsp). When button (update) is pressed it calls method update() on the controller & saves the change. Good till here.
And I want to show "saved " on the same gsp (info.gsp) with the saved new values. render takes me to a new page.
Please help how to do it.
thanks
Sana
On the action that is saving your object render the value you want to display for example
def save() {
println params
def bookInstance = new Book(params)
if (!bookInstance.save(flush: true)) {
render "not saved"
}
flash.message = message(code: 'default.created.message', args: [message(code: 'book.label', default: 'Book'), bookInstance.id])
render bookInstance.id // "saved!!!!"
}
and on your view update a placeholder using its ID. In my example, submitToRemote has a update attribute that will take care of it. Hope this code helps
<html>
<head>
<title>Page Title</title>
<meta name="layout" content="main"/>
<r:require modules="jquery"/>
<g:javascript library="application" />
<modalbox:modalIncludes />
</head>
<body>
<g:form name="myform" action='save'>
book: <input name="name" type="text" />
<g:submitToRemote action='save' update="updateMe" name="myname"/>
</g:form>
<div id="updateMe">
</div>
</<body>
Before you redirect in your controller add something like:
flash.message = "I saved something"
And then in your GSP, have the following:
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
Also note that if you generate the Grails scaffold code, you'll see the exact same thing I just showed you.
I'm learning to use Grails and have run into a situation I don't understand when handing what should be a simple form submission.
I have created a controller called 'add' (there is an AddController.groovy source file and an appropriate add/index.gsp view) and have defined a very sparse 'process' action which currently renders a small amount of HTML to verify that the action is being called.
The URL for the process action on the add controller is (not surprisingly) http://localhost:8080/frontend/add/process/.
I would like to submit a very simple form to the process action as a first step towards integrating with some existing Java libraries.
Sending a GET request to http://localhost:8080/frontend/add/process/ causes the process action to be called and the browser to display the relevant simple HTML content.
Sending a POST request to http://localhost:8080/frontend/add/process/ returns a HTTP 404 error.
I appreciate I'm missing some fundamental addition to my application such that the above action works with both GET and POST requests. I had assumed by default the request type would not matter.
I'd be very happy at this stage if I can send a POST request to the appropriate action and have some markup rendered just to demonstrate that things are working.
What fundamentally essentials piece of the puzzle am I missing?
controllers/frontend/AddController.groovy:
package frontend
class AddController {
def index = { }
def process = {
render "<h1>process action being performed</h1>"
}
}
views/add/index.gsp
<html>
<head>
<title>Test View for index action</title>
<meta name="layout" content="main" />
</head>
<body>
<g:form controller="add" action="process">
<label for="title">Title:</label>
<g:textField name="title" id="title" />
<label for="content">Content:</label>
<g:textArea name="content" id="content" />
<g:actionSubmit value="Add" />
</g:form>
</body>
</html>
The <g:actionSubmit /> directive needs an action attribute to indicate the action to be handled. I had assumed the form action would have been sufficient.
I needed to change:
<g:actionSubmit value="Add" />
to:
<g:actionSubmit value="Add" action="process" />
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.