Grails - Pass JSON/Map object to client - grails

I am trying to save a JSON object to a data- field in a gsp page.
Mock controller:
def test(){
['comments': ["1": 1, "3":40 ] ]
}
GSP (I also tried (comments as JSON) )
<%# page import="grails.converters.JSON" %>
<div class='findme' data-comments="${comments as JSON}">
Result:
<div class='findme' data-comments="{" 1":1,"3":40}">
As you can see the quotes are all messed up. I tried using encodeAsHTML but it didn't work.

I ran into this recently as well and came up with two solutions:
Solution One: in the template do this:
<div class="comments" data-comments="${comments.encodeAsJSON().encodeAsHTML()}">
Solution Two: in the controller do this:
def test(){
['comments': ["1": 1, "3":40 ] as JSON]
}
and in the template do this:
<div class="comments" data-comments="${comments.encodeAsHTML()}">
I'm using jQuery to do the parsing. With either solution I get the JSON like so:
$.parseJSON($('.comments').attr('data-comments'));

I found a couple ways to do it.
<div class='findme' data-comments="${(new JSON(comments)).toString().encodeAsURL()}">
Then I have to pull it out like:
JSON.parse(unescape($('.findme').data('comments')))
Found another way to do it, but it will cause problems if you have anything that has a " in it. I think encoding it is the way to go.
${(comments as JSON).toString().replace("\"", "'")}

I have found an easy way to do that.
First you do:
<div class="comments" data-comments="${comments.encodeAsJSON()}">
And then in javascript:
eval($('.comments').attr('data-comments'));

Related

Action cable to a specific div

I am looking to update a specific div that can change depending on the stream.id.
I have the following :
ActionCable.server.broadcast('webconferencier_channel', participants_html: participants_html, stream_id: webconferencier.stream_id)
Where I do pass the stream_id.
I am looking to retrieve it in coffee file like this:
received: (data) ->
# Called when there's incoming data on the websocket for this channel
participantsHtml = data.participants_html
$('#show_participants' + data.stream_id).html(participantsHtml);
And then update the view as:
<div id="show_participants_#{<%= #stream.id %>}">
</div>
I can see the AC sending the div template with the other variables, but i cannot seem to retrieve and update it. I think my coffee syntax is incorrect.
In the view, the div id show up as :
<div id="show_participants_#{67089dbd-9826-41f2-97bc-b774b667ef66}">
</div>
with the UUID being the exact same string that i am passing through the coffee file.
Does anyone know how i could make this work please?
Thank you
You should remove curly braces and octothorp from div's id like show_participants_UUID.
You can add console.log to the coffee to see what's happening inside.

Get children of an XHPChild

I am trying to move my website to Hack and XHP, of course. Below is a structure of what code structure I want to achieve:
<ui:backstageHeader>
<ui:backstageHeader-navItem href="/">stories</ui:backstageHeader-navItem>
<ui:backstageHeader-navItem href="/story/send">send a story</ui:backstageHeader-navItem>
<ui:backstageHeader-navItem href="/aboutus">support</ui:backstageHeader-navItem>
</ui:backstageHeader>
(Note: :ui:backstageHeader-navItem basically renders to <a href={$this->:href}>{$this->getCHildren}</a> so there is not need to attach its class here.)
Below is the code for :ui:backstageHeader:
final class :ui:backstageHeader extends :ui:base {
attribute :div;
children (:ui:backstageHeader-navItem)*;
protected function compose() {
$dom =
<section class="backstage-header">
<div class="container">
<div class="cell-logo">
<a href="/">
<span class="no23-logo-white"></span>
</a>
</div>
<div class="cell-navigation">
</div>
<div class="cell-account">
<div class="cell-login">
<div id="siteNav-login">Autentificare</div>
</div>
</div>
</div>
</section>;
$mainContainer = $dom->getChildren("div")[0];
$cellNavigation = $mainContainer->getChildren("div")[1];
$navItems = <ul class="main-navigation"></ul>;
foreach($this->getChildren() as $child) {
$navItems->appendChild(<li>{$child}</li>);
}
$dom->appendChild($navItems);
return $dom;
}
}
I used the Terminal to debug my code using hhvm -m d <file.php>, and everything was alright there; however, when I get to my browser, I get 500 error header. This is what the log says:
Catchable fatal error: Hack type error: Could not find method getChildren in an object of type XHPChild at /var/www/res/ui/backstage-header.php line 25
The error comes from
$cellNavigation = $mainContainer->getChildren("div")[1];
But, somehow, I need to append ul.main-navigation to div.cell-navigation from my section.backstage-header.
How can I do it?
Don't structure your code this way. Built it up from the inside out, so that you don't have to do a ton of unreadable getChildren calls looking for specific children. Those calls are super hard to read, and super inflexible when you change the structure of your XHP. You wouldn't do something like node.firstChild.lastChild.lastChild.firstChild in the JS DOM, would you? No, there's a better way in JS, to find things by class or ID; in XHP, you can just build it up the right way in the first place!
I'd give you an example of this, but it doesn't look like you actually use $mainContainer or $cellNavigation, so you can just remove those two problematic definitions.
As an aside, you really shouldn't be getting your type errors as catchable fatals from HHVM; this is a last resort sort of check. Try running the hh_client checker directly, maybe even showing its result in your IDE; it will give you a much faster iteration cycle, and much more information than HHVM provides.
From my experience, appendChild is very prone to human error. It's easier to do something like:
$items = (new Vector($this->getChildren()))->map($child ==> <li>{$child}</li>);
return <div id="container">{$items}</div>;
If you want to wrap the children in <li />.
Not sure if that will work but it will be close.
Pro tip: You can assign variables from within an XHP tree.
$root =
<div>
{$child = <span>
Text children
</span>}
</div>;
Now $child is already set to the <span> element.

Chicago Boss CSRF and variable passing to template files

I've two problems with my simple application.
I've read the documents twice, but had no luck to using boss_csrf_filter and passing variables to templates.
First I've done the configuration for using CSRF filter in boss.config file as the following :
{controller_filter_config, [boss_csrf_filter]},
{session_adapter, mock},
{session_key, "_myapp_session"},
{session_exp_time, 525600},
{session_cookie_http_only, false},
{session_cookie_secure, false},
{session_domain, ".myappdomain.com"},
Then, I've add to variable in template (which is under src/view/world/hello.html)
<div class="row">
<div class="12u">
<textarea name="message" id="message" placeholder="placeholder" required></textarea>
{{ csrf_token }}
</div>
</div>
Everthing seems fine, but no any hidden input element appearing. Should I check/configure anything else?
The second problem is with my app is passing variables to templates. My is controller something like the following code :
-module(myapp_world_controller, [Req]).
-compile(export_all).
hello('GET', []) ->
{ok, [{world}]}.
postcontact('POST', []) ->
{redirect, "/",[{contactformsend, "ok"}]}.
The code is redirecting without any value. So, the following code is not working (the same template file with I've mentioned above) :
{% if contactformsend %}
<script>
alert("Hi there is!");
</script>
{% endif %}
What I am doing wrong? How can I fix the problems?
I would propose to split your question into two, because I have answer only for the second part. When you use redirect in controller, you don't pass the variables. In the docs you can read this:
{redirect, Location, Headers::proplist()}
So the third argument is list of headers, not variables passed to templates. It makes sense: the variables will be taken from the controller, that is responsible for rendering "/".

Kraken, Dust and Makara

<span id='sitemap'>
{#footer.sitemaps}
<a id="{id}" href="{url}">{#pre type="content" mode="json" key="footer.{id}"/}</a>
{/footer.sitemaps}
</span>
In the code, {id} is a property of one element in footer.sitemaps. I want Makara to get the key value dynamically using {id}. May I know how to do that?
See the discussion around this makara issue: https://github.com/krakenjs/makara/issues/36
Check this: https://github.com/mikesparr/Kraken_Example_i18n_Helper
You can use #bundleString helper rather than #pre.
So, you can use something like {#bundleString key="footer.{id}" bundle="your_data_properties_file name"}
You can do that by using the {#provide} tag. Here is an example using yours:
{#provide}
{#footer.sitemaps}
<a id="{id}" href="{url}">{footerMap[id]}</a>
{/footer.sitemaps}
{:footerMap}
{#pre type="content" mode="json" key="footer"/}
{/provide}
If your properties file looked like
footer.key1=SomeVal
footer.key2=AnotherVal
and your footer.sitemaps data object looked like
{
id: 'key1',
href: 'http://my/url
}
The result of running this would be
<a id="key1" href="http://my/url">SomeVal</a>

Grails 2.0 update select with submitToRemote and render

I face a problem using Grails 2 submitToRemote tag.
The following code is what I use in the controller:
def getProposal = {
def layouts = importService.getLayoutsFor(params.product as int)
render(contentType: "text/xml") {
for (layout in layouts) {
option("${layout}")
}
}
}
and in the GSP:
<g:submitToRemote action="getProposal" update="layouts"
onLoading="showProgress();" onComplete="hideProgress();"
value="Do It" />
<select id="layouts" name="layout" required="">
</select>
Using jquery this results in:
showProgress();;jQuery.ajax({type:'POST',data:jQuery(this).parents('form:first').serialize(), url:'/app/controller/getProposal',success:function(data,textStatus){jQuery('#layouts').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){},complete:function(XMLHttpRequest,textStatus){hideProgress();}});return false
which not works and returns am error:
Node cannot be inserted at the specified point in the hierarchy
But if I use another render method like:
render(status: 0, text: "<option value='1'>Layout 1</option>")
it works.
In both cases the expected answer is transmitted back.
I did not understand why it will not work with the first nicer method. Could anyone explain what I do wrong?
Thx
Edit:
I noted that if I use render(contentType: "text/text") instead it will work. May be it has something to do, that the xml is not properly formatted (no root node?). But why does it work in Grails 1.3.7?
Grails 1.3.7 had a different default javascript provider (prototype). The jquery ajax call trys to infer the type of the response based on what it receives

Resources