Grails UI(plugin) dialog not firing the controller action - grails

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>

Related

Ruby on Rails - Show the same modal with different content

I have a home page with a list of software in a table.
With this link I open my softwares pages in modals:
<% = link_to software_path (software), remote: true do %> ... <% end%>
I created a script allowing me to open my modals when I click on td with class .clickable and not open modals when I click on a td with class .no-click.
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
$('#software-modal').modal('show');
}
});
structure :
_software.html.erb open _software_modal.html.erb
Problem:
When I try to open my modals via the script, it's always the same modal that opens. That is, the modal of software 1 opens when I click on software 2, software 3, ... but no problem when I just use the link.
So I have a problem with id or something like that in my script ...
Can you help me ?
EDIT 1: With the response of VAD I tried to search the id with data-id.
_software.html.erb
<tr class="clickable" data-id="<%= software.id %>">
...
</tr>
<div id='software-content'></div>
_software_modal.html.erb
<div id="software-modal" class="modal fade" role="dialog">
...
</div>
show.js.erb
$('#software-content').html("<%= j render 'software_modal', software: #software %>");
$('#software-modal').modal('show');
this show.js.erb allows me to open my modals via example links:
<%= link_to software.name, software_path(software), remote: true,class:"no-click" %>
but not via a clickable table.
So I added a script in _software.html.erb that makes my table clickable:
$(".clickable[data-id]").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
window.location = '/softwares/' + $(this).attr('data-id');
}
});
I also tried with data-link, ... is there a way to open this window.location in a modal (like the link)? I searched for several hours yesterday but I did not find anything ..
EDIT 2
following the response of #VAD with render layout: false, we can put:
class ApplicationController < ActionController::Base
   layout proc{|c| c.request.xhr? ? false : "application" }
end
which allows to have a false render for the js and true render for html. And so to keep my html pages with my starting layout.
Thx #dogenpunk
Render without layout when format is JS (needs drying)
You need to have different modals for every place you want to show the modal from. Like for software1 you need to show the modal with content relative to this particular object. For software2 you'll need to show the same model but with a brand new content relative to software2
Another option (and the better one) is that you may have one single modal for every Software object but by some event (for example actually showing the modal) you'll update the modal content with the data specific for this particular Software object. You may use some controller action for that
Additional
Try to pass software id as data parameters in your clickable elements and then to update your modal content with ajax and like that
<div class = 'clickable' data-id = "#{software.id}">
</div>
$(".clickable").click(function(e) {
if (!$(e.target).hasClass('no-click')) {
$.get('/softwares/' + $(this).attr('data_id'), function(data) {
$('#software-modal body').html(data);
});
$('#software-modal').modal('show');
}
});
Of course I can't give precise code so you'll need to adjust it according to you current setup
Update
The idea is that you have show action in softwares_controller.rb which eventually renders the content for your modals:
def show
// some code you need
render layout: false
end
It will render show.html.erb view. In that view you should keep the html for your modal content. Notice that the action will render that view without layout. You need this because you will soon take this html and will put it into the modal, so you don't need any extra html like the layout.
So, you have an action which render the modal content for every software object by its id.
Now you need to put it into the modal.
You have clickable elements in your markup. You attach correspondent software ids to them as data-id. Then in your jquery code you use these ids to construct a url like this:
'/softwares/' + $(this).attr('data_id')
This url will lead to your show action in softwares_controller.rb. So by clicking on one of clickable elements you take the id, generate the url with it, send request to the url, get the response (the modal content) and then put it into the modal like this:
$.get('/softwares/' + $(this).attr('data_id'), function(data) {
$('#software-modal body').html(data);
});
Then you show the modal with already updated content:
$('#software-modal').modal('show');

Grails display data from database

I'm creating a Grails app following this tutorial: http://grails.asia/grails-tutorial-for-beginners-display-data-from-the-database
When I try to display data from database though, nothing happens. Below is my controller code:
def index(){
def users = User.list();
[users:users]
}
def displayUsers(){
if(User.count()==0){
render 'User list is empty!';
}else {
render(view: 'userlist.gsp');
//a little test
println User.list().size();
println User.count();
for(User u : User.list()){
println u.username;
}
}
}
The User.count() is working, because I can see the usernames in console and userlist.gsp renders every time, but it seems that my view code doesn't see the list. Here's the userlist.gsp code:
<body>
userlist.gsp
<g:each in="${users}" var="user" status="i">
<h3>${i+1}. ${user.username}</h3>
</g:each>
</body>
What can be wrong with this code? I've been making precisely the same steps as in the tutorial above in my analogical app, but it doesn't seem to work. This is especially weird, since I've found a similar question under this link: grails: show list of elements from database in gsp
and it's been marked as accepted answer. Why does exacly the same way not work in my app?
render gsp view as follows (pass data as a model).
render(view: "userlist", model: [userList : User.list()])
Now get model data in gsp as follows.
<body>
userlist.gsp
<g:each in="${userList}" var="user" status="i">
<h3>${i+1}. ${user.username}</h3>
</g:each>
</body>
You can do with many options:
rename your gsp action in controller from def index() to def userlist()
or rename index.gsp file to userlist.gsp
you can redirect to userlist.gsp with ${users} object
So change in controller def index() action
[users:users] to redirect(action: "userlist", params: ["users": users])
Note: 2nd method will show parameters in url.
Refer Grails Doc
You can use chain
e.g. chain(action: "userlist", model: ["users": users])
Refer Grails Doc
every gsp action (page) needs to be injected

How can i use formRemote feature in my grails app

Iam formRemote in a gsp and calling a controller method, inside that method iam calling a sevice class and there iam doing my save action. But the problem is my update div is not working after a successful saving
You form should like this
<g:formRemote method="post"
update="updateThisDiv"
name="formRemote" url="[ controller: 'main', action:'myAction']">
//your form fields
</g:formRemote>
<div id="updateThisDiv"></div>
"updateThisDiv" is the id of an html-object, that you want to render after submit (but it's not mandatory).
Controller
def myAction() {
//TODO: your logic..
render "Your view" //you can render jsp view, json etc..
}

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

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