Grails dynamic rendering of map-injected g:select - grails

I have the following Grails (2.3.6) controller:
class WidgetController {
def index() {
Map<String, List<String>> widgetMap = getSomehow()
render (
view: "widgets",
model: [ widgetMapping: widgetMap ]
)
}
}
And the following GSP:
<!DOCTYPE html>
<html>
<head>
<!-- Omitting a bunch of stuff for brevity -->
</head>
<body>
<h3>Widget Mappings</h3>
<g:select name="widgetMapping" from="${widgetMapping}"/>
<div id="widgetMapping">
</div>
</body>
</html>
I am trying to accomplish the following:
For each key in the widgets map, display an <option/> in the dropdown <select/>
When a user selects one of those <option/>s (or when the page first loads with the default option), the <div id="widgetMapping"> section should be populated with a <ul> list, where each <li> in the list corresponds to one of the elements in the List<String> that the selected widget is mapped to
In other words, if the widgetMap (above, in the controller) looks like this at runtime:
Map<String, List<String>> widgetMap = []
List<String> colors = []
List<String> pets = []
colors << "Red"
colors << "Blue"
colors << "Green"
pets << "Dog"
pets << "Cat"
widgetMap.put("Colors", colors)
widgetMap.put("Pets", pets)
...then in the rendered HTML I would expect to see a <select> with 2 <option> children: "Colors" and "Pets". When you select "Colors" you should see a bulleted (<ul>) list enumerating "Red", "Blue", "Green"; and if you select "Pets" you would see any previous list clear and then display a list of "Dog" and "Cat" in its stead. Thus, making any new selection from the dropdown should clear the currently-displayed list and display the correct list for that option's respective key in the map.
Given the GSP code I have above, this behavior is simply not happening. Any ideas as to what I need to do?

Based on what you have mentioned, you have a couple of different options.
Pass the entire map back to the client and store the values in a javascript value. I don't like this because it doesn't scale very well. In your example, you only have a couple of keys with a couple of values in each list, but this could grow pretty quickly, and I don't think you should necessarily be storing all of that data in memory on the client side.
Make subsequent Ajax calls to simply return the list for each key. This could be accomplished by listening for a change event on the drop-down, then replacing the contents of the div with a GSP rendered template. I like this option much better as it scales. Sure it makes more calls to the server, but the amount of data per call is minimal and your user experience will actually improve overall as the initial page-load will be reduced.
Controller:
class WidgetController {
def index() {
def widgetKeys = getSomehow()
render (
view: "widgets",
model: [ widgets: widgetKeys ]
)
}
def widgetList (String key) {
def widgetList = getSomehow(key)
render (
template: "widgetTemplate",
model: [ widgetList: widgetList ]
)
}
}
Template:
<ul>
<g:each in="${widgetList}">
<li>${it}</li>
</g:each>
</ul>
Javascript:
$(function () {
$('#selectId').on('change', function(){
var select = this;
$.ajax({
url: 'widgetList', //might have to play with this so it is not hardcoded
data: { 'key': select.value },
success: function (list) {
$('#widgetMapping').html(list);
}
});
})
});
I'm not guaranteeing that this will get you exactly what you want, but this should at least get you on the right track. There is a wealth of documentation for JQuery and several examples around the web, so I suggest you look around if you think you want to do something different.

Related

Refactoring Grails templates to expect maps instead of lists

Grails 2.4.4 here. I currently have a GSP that displays a list of Widget objects like so:
// The Widget domain object
#Canonical
class Widget {
String title
String history
}
// The controller
class WidgetController {
def widgetService // Grails Service provided below
def index() {
def widgetType = params.widgetType
def model = widgetService.getAllWidgetsByType(widgetType)
render(view: 'index', model: model)
}
}
// WidgetService
class WidgetService {
def widgetDataService // Omitting for brevity; makes a call to a DB
def getAllWidgetsByType(widgetType) {
def model = [:]
def widgetList = widgetDataService.getWidgets(widgetType)
model.put('widgetList': widgetList)
model.put('foo', 'baz')
model.put('fizz', 'buzz')
model
}
}
// grails-app/views/widget/index.gsp
<!DOCTYPE html>
<html>
<head>
<!-- omitted for brevity -->
</head>
<body>
<div class="content">
<h2>Here are the widgets:</h2>
<g:render model="[providedWidgetList:widgetList]" template="/shared/widgetBlock"></g:render>
</div>
</body>
</html>
// grails-app/views/shared/_widgetBlock.gsp
<g:each var="widget" in="${providedWidgetList}">
<div id="widgetBlock">
<h3>${widget.title}</h3>
<span>${widget.history}</span>
</div>
</g:each>
So, to recap:
WidgetService pulls back a List<Widget> from a DB and plops that into a model map (along with) some other stuff
WidgetController feeds this model to a widget/index.gsp
widget/index.gsp renders a shared/widgetBlock template that generates a widgetBlock div tag for each widget in the returned List
So, if there are 3 widgets returned by the DB, the resultant HTML might look like:
<!DOCTYPE html>
<html>
<head>
<!-- omitted for brevity -->
</head>
<body>
<div class="content">
<h2>Here are the widgets:</h2>
<div id="widgetBlock">
<h3>Hello There, Little Widget!</h3>
<span>What a nice little widget.</span>
</div>
<div id="widgetBlock">
<h3>My Fair Widget</h3>
<span>The best there ever was.</span>
</div>
<div id="widgetBlock">
<h3>A Time of Dogs!</h3>
<span>It was a time of tradition. It was a time of Dogs.</span>
</div>
</div>
</body>
</html>
I now need to add a new property to the Widget, a favoriteFood property, so that now Widget looks like:
#Canonical
class Widget {
String title
String history
String favoriteFood // values might be 'Pizza', 'French Fries' or 'Ice Cream'
}
And now, in the UI, I need the widget list visually-grouped by favoriteFood, so that widgets who share the same favorite food appear in their own section like so:
<!DOCTYPE html>
<html>
<head>
<!-- omitted for brevity -->
</head>
<body>
<div class="content">
<h2>Here are the widgets:</h2>
<h3>Pizza</h3>
<hr/>
<div id="widgetBlock">
<h3>Hello There, Little Widget!</h3>
<span>What a nice little widget.</span>
</div>
<div id="widgetBlock">
<h3>My Fair Widget</h3>
<span>The best there ever was.</span>
</div>
<h3>Ice Cream</h3>
<hr/>
<div id="widgetBlock">
<h3>A Time of Dogs!</h3>
<span>It was a time of tradition. It was a time of Dogs.</span>
</div>
</div>
</body>
</html>
So in the above example, the first two widgets both have favoriteFood = 'Pizza' and the last widget alone loves Ice Cream.
To accomplish this, I need to group all the widgets (returned from the DB) according to favoriteFood, so something like this:
def widgetsByFavoriteFood = widgetList.groupBy { widget -> widget.favoriteFood}
However because of how the service returns a model map, and how the index.gsp invokes and renders the template, and because the template is expecting a list (not a map), I'm just not seeing where I would make these changes.
Very important: This is a gross simplification of my actual Grails app. There are several things I cannot change without enormous refactoring (which I really don't want to have to do):
I really can't change the fact that the WidgetService returns a model map
I really can't change the fact that the widget/index.gsp invokes the _widgetBlock
Anyone have any ideas?
You can make the change right in the GSP. I consider your case UI logic, so it makes sense to put it in the GSP.
Oops, that was an opinion ;)
<g:each var="entry" in="${widgetList.groupBy { widget -> widget.favoriteFood } }">
<!-- entry.key is the favorite food,
and entry.value is the list of widgets grouped under
the favorite food -->
</g:each>
Iterating over a Map, which is what groupBy() returns, produces Map.Entrys.
Tip:
You can build your model much more succinctly like this:
[widgetList: widgetList, foo: 'baz', fizz: 'buzz']
So you get a grossly simplified answer... :)
The service returns a map -- still a map, just the inner details of the widgetList elements are different (rather than a list of individual items, have it return a list of lists of items. Maybe extract the favorite food into a parent element.
[
widgetList:[
[
favoriteFood:'Pizza',
widgets:[
[
title: 'title1',
history: 'blah'
]
]
],
[
favoriteFood:'Eggses',
widgets:[]
]
],
foo: 'baz',
fizz: 'buzz'
]
Widget block has to change no? Not sure what your groupBy yields exactly but,
g:each var foodGroup in widgetList
output the foodGroup stuff: h3, hr
g:each var widget in foodGroup.widgets
output the widget div

How to Send a Javascript Variable to a Grails Controller to Query Neo4J

I'm a newbie trying to find uses for Neo4J on Grails.
Basically, I've made 20 grocery item nodes through the Neo4J browser and I want to create a simple Grails site that will let users search a grocery item and visually show the items related to it.
My index.gsp has:
<input id="item" />
My viz.js has:
$('#item').keyup(function() {
var item = $('#item').val();
My Item Domain class has
class Item {
static mapWith = "neo4j"
String name
My ItemController class has:
def index() {
def item = Item.list() [item:item] //No idea, just trying out whatever i find :(
and a query with something like:
def query = Item.cypherStatic ("""start n=node({Item}) match (n)-[r]->(x) where r='partner' return n, x)
Questions:
How can I properly send the JS 'item' variable into the ItemController?
How can I use the 'item' variable to properly query the node names which have a 'partner' relationship with the item?
in addition to Motilals answers, you definetly need a wrapping form with an action that points your controller
like
<g:form controller="itemController" action="index" >
<input type="text" id="item" name="item" value="" />
<input type="submit" value="submit" >
</g:form>
then on clicking submit the for will call your index action and there you could parse the value with
def item = params.item
but it looks more like you want some asynchronous stuff right after keyup-function, therefore you could do sth like this :
$('#item').keyup(function() {
var item = $('#item').val();
$.ajax({
url: "${createLink(controller:'itemController', action:'index')}",
data:"&item="+item
})
.done(function( data ) {
console.log(data)
});
});
in this case, you need to pay attention what your index-action is returning, so you can do in the .done() whatever you want with the response.
also note, that when you name an action "index" it will be available at
.../myproject/item/index
or, and thats important
.../myproject/item/
so if your index method requires the data from the input, it will miss them if a user has gone straight to that url
so your index action would rather render the page with the input
and you define another action for executing your query based on input and returning data
set the item to hidden field and then you can access it directly in your controller using params
here you go:
//in index.gsp add below hidden field and set the hidden filed in your js code
<g:hiddenField name="item" value="" />
$('#item').keyup(function() {
var item = $('#item').val();
//in your controller
def index() {
def item = params.item
print item // you see the value for item
//do your stuff
}
once you have item value you could directly use HQL query or use the domain instance
hope this helps you
Regards
Motilal

Rails 4 - how to trigger Object edit form without refreshing the page

Hi and thanks for looking into this.
I have a list of objects displayed on my page. Now when a user selects one of the objects, I'd like to show Edit form for this object without refreshing the page and allow user to update it. I already have a form partial with :remote tag. What I cannot figure out is how to trigger this Edit form and pass the correct object into it.
Thanks in advance!
You can include the form partial for each item in an hidden div, and show the div with javascript only when needed :
<% #items.each do |item| %>
<div>
<%= item.label %>
Edit
<div class="editFormWrapper">
<%= render '_form', locals: {item: item} %>
</div>
</div>
<% end %>
css:
.editFormWrapper {
display: none;
}
javascript (with jquery)
$(document).ready(function() {
$('.editLink').on('click', function() {
$(this).siblings('editFormWrapper').show(); // or .slideDown(); or any other effect
});
});
If the page contains a lot of items, or if the hidden form is big, it may be better to use Ajax to request and include the edit form "on demand", to keep the weight of the page to minimum.
When you render page with objects, you can render objects array in javascript variable
<script>
OBJ_ARR = {
*obj_id1* = {
name: 'test1',
desc: 'test test'
},
*obj_id2* = {
name: 'test2',
desc: 'test2 test'
},
......
}
</scpipt>
Next we use javascript scenario
When object clicked you get certain object_id, and then you get appropriate object from our array
OBJ_ARR[object_id]
So, we put object properties to form and display that form

Populate drop down menu Grails

I am currently trying to populate a drop down menu based upon the selection in the previous drop down menu I have. For example if you click on Spring 2013 in the first drop down menu the second menu will show all the respective weeks assigned to that semester so that one can be chosen and modified. How do I propagate the second menu with respects to the choosing from the first menu?
This is my controller:
class TermController {
static scaffold = Term
def dataSource
def list = {
def db = new Sql(dataSource)
def result = db.rows("SELECT id, semester FROM term")
[ result: result ]
}
}
Here is my list.gsp to show the view of the controller:
<div class="fieldcontain">
<g:select name="termSelection" from="${Term.list()}" values="${params.id}" optionKey="id" optionValue="semester" noSelection="['':'----Term----']"/>
</div>
<table>
<tr>
<g:select name="it.week" from="${Week.list()}"/>
</tr>
</table>
It looks to me like you want to make an ajax call triggered by the selection change of another list?
Listen to changes in your termSelection list
Upon changes, make an ajax call to retrieve the list of weeks
Feed the retrieved ajax List to your it.week list

Grails render() with a fragment parameter

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).

Resources