Grails render template or JSON? - json

I have a controller that uploads and processes a file. Afterwards, I wish to render the processing result in a modal div. I wanted to know what the best way is to get the results from the controller to the modal div on the gsp. I thought about a template but I didn't know how to specify what the target div for the template should be because this template wouldn't be rendered by a button click where a target for template render is set as an attribute, it would be done on a timed basis (i.e. when the file is done uploading). The other way is to send JSON back from the controller but I don't know how to intercept this JSON at the right time because I still don't quite understand the timings of the information flow between the GSP and the Controller. I know how to send the JSON but how to alert the GSP that "hey, some JSON is now ready for your modal that's about to go up." Here is some pseoducode of basically what I am trying to get done.
Controller:
upload() {
// process file and store results in three integers
// int1 = result1
// int2 = result2
// int3 = result3
// send the three numbers to the gsp
}
Now what is the best way to get these three numbers to the GSP so that they are displayed on a modal dialog which is about to go up like this:
<div id="fileUploadResultsModal">
Results:
${int1}, ${int2}, ${int3}
</div>
Here is the JS associated with my ajax upload function:
$("#chseFile").upload("${createLink(controller: 'customer', action: 'upload',)}",
{dataTypegrp: parseInt(getCheckedValue(document.getElementsByName('dataTypegrp'))),
fileTypegrp: parseInt(getCheckedValue(document.getElementsByName('fileTypegrp')))},
function(success) {
$("#cancel1").trigger("click");
setTimeout(function(){
$("#summary").trigger("click");
}, 250);
displaySuccess(data);
},
function(prog, value) {
console.log(value);
$("#prog").val(value);
if (value == 100) {
$("#prog").hide();
$("#progressbar").html("Uploading and processing. Please wait...");
}
});
but right now JS complains that 'data' is not defined. 'data' is meant to be the JSON coming back from the controller.
Thanks

you can render them as JSON:
render( [ int1:111, int2:222, int3:333 ] as JSON )
or as a HTML-string
render "<div id=\"fileUploadResultsModal\">Results:${int1}, ${int2}, ${int3}</div>"
or use a template
render template:'/yourController/templateName', model:[ int1:111, int2:222, int3:333 ]
or a TagLib
render g.yourResultTag( int1:111, int2:222, int3:333 )
For this tiny bit of information, the performance is not of concern. It's rather a matter of taste, or what is more appropriate for your client.
If the later is JSON-biased, use JSON-rendering. If it has a mix of JSON and HTML, use others.

inside controller at the enf of controller action you can use
render [data:['name':'firstname','surname':'secondName'] as JSON]
this will render the data to GSP

Related

Html Data Attribute Not Properly Translating To "success" Portion of "jquery.get()" in HtmlHelper

I'm trying to create an HtmlHelper for jstree nodes. Part of my objective is to encapsulate the "select_node" logic into a re-usable format. For our purposes, we can expect to use a jquery "GET" which will call into an MVC controller method. That URL needs to be dynamic and is passed in via an html custom data attribute. The resultant behavior upon success also needs to be dynamic and is also passed in via the same means. My problem is that, despite the passed in URL being interpreted and called properly (the controller method is hit), I cannot get the "success" portion to work properly. Below are code examples with corresponding comments. The non-working scenario (the last commented attempt with "data.node.li_attr.success") would be the ideal as we would need to pass in multiple lines.
writer.WriteLine("</ul>");
writer.WriteLine("</div>");
writer.WriteLine($"<script>");
writer.WriteLine($"$({treeParamModel.TreeName}).jstree({{"); // base tree definition
// "core" config standard to every jstree
writer.WriteLine("'core':{");
writer.WriteLine($"'multiple':{treeParamModel.IsMultiSelectAllowed.ToString().ToLower()}");
writer.WriteLine("},");
// "types" plugin
writer.WriteLine("'types':{");
writer.WriteLine("'default':{");
writer.WriteLine($"'icon':'{treeParamModel.IconPath}'");
writer.WriteLine("}");
writer.WriteLine("},");
writer.WriteLine("'plugins' : ['types']"); // define which plugins to bring in
writer.WriteLine("})");
writer.WriteLine($".on('select_node.jstree', function (e, data) {{");
writer.WriteLine("$.get({url: data.node.li_attr.get_url,");
writer.WriteLine("success: function (result) { alert(data.node.li_attr.success)}"); // works (correctly displays variable content.)
//writer.WriteLine("success: function (result) { $(data.node.li_attr.success)}"); // WHY? "Uncaught Error: Syntax error, unrecognized expression: alert('hello')"
writer.WriteLine("});");
writer.WriteLine("});");
writer.WriteLine("</script>");
The MVC view passes in the following:
#{
var treeName = "tree";
var success = "alert('hello');";
}
Update
My colleague and I have found that we can ALMOST achieve our desired result by using the "eval()" function.
writer.WriteLine("success: function (result) { eval(data.node.li_attr.success)}");
works as expected with the exception that it stops evaluating content after the first space.

Angular 4 html for loop displaying loosely typed object (string) normally but not when element is extracted directly?

I'm using Angular 4 to develop an app which is mainly about displaying data from DB and CRUD.
Long story short I found that in Angular 4 the component html doesn't like displaying loosely typed object (leaving the space blank while displaying other things like normal with no warning or error given in console) even if it can be easily displayed in console.log output, as shown in a string.
So I made a function in the service file to cast the values into a set structure indicating they're strings.
So now something like this works:
HTML
...
<div>{{something.value}}</div>
...
Component.ts
...
ngOnInit() {
this.route.params.subscribe(params => {
this.pkey = params['pkey'];
this.service.getSomethingById(this.pkey)
.then(
something => {
this.something = this.service.convertToStructure(something);
},
error => this.errorMessage = <any>error);
});
}
...
Code of the function convertToStructure(something)
convertToStructure(someArr: myStructure): myStructure {
let something: myStructure = new myStructure();
something.value = someArr[0].value;
return something;
}
But as I dig into other files for copy and paste and learn skills from what my partner worked (we're both new to Angular) I found that he did NOT cast the said values into a fixed structure.
He thought my problem on not being able to display the values (before I solved the problem) was because of me not realizing it was not a plain JSON object {...} but an array with a single element containing the object [{...}] .
He only solved half of my problem, cause adding [0] in html/component.ts was not able to make it work.
Component.ts when it did NOT work
...
ngOnInit() {
this.route.params.subscribe(params => {
this.pkey = params['pkey'];
this.service.getSomethingById(this.pkey)
.then(
something => {
console.log(something[0].value); //"the value"
this.something = something[0]; //html can't find its value
},
error => this.errorMessage = <any>error);
});
}
...
HTML when it did NOT work
...
<div>{{something[0].value}}</div> <!--Gives error on the debug console saying can't find 'value' of undefined-->
...
And of course when I'm using the failed HTML I only used this.something = something instead of putting in the [0], and vice versa.
So I looked into his code in some other page that display similar data, and I found that he used *ngFor in html to extract the data and what surprised me is that his html WORKED even if both of our original data from the promise is identical (using the same service to get the same object from sever).
Here's what he did in html:
...
<div *ngFor="let obj of objArr" ... >
{{obj.value}}
</div>
...
His html worked.
I'm not sure what happened, both of us are using a raw response from the same service promise but using for loop in html makes it automatically treat the value as strings while me trying to simply inject the value fails even if console.log shows a double quoted string.
What's the difference between having the for loop and not having any for loop but injecting the variable into html directly?
Why didn't he have to tell Angular to use the set structure indicating the values are strings while me having to do all the trouble to let html knows it's but a string?
The difference here is as you said that your JSON is not simple object , its JSON Array and to display data from JSON array you need loop. So, that is why your friends code worked and yours did not. And please also add JSON as well.

Node js loop in res.render

Hey Everyone i have a router that basically pulls in a JSON file and outputs various instances of the array. When i console.log the title in my for loop it loops through and outputs and each instance is outputted, works great. When i put my res.render inside of the for loop and pass it the same variable being looped in the console.log it will only output the first instance. Does anyone know why this is? and is there a way to loop through the res.render to output all instances in the JSON same as the console.log.
Thank you for your time,
router.get('/fuelTypeFilter', function(req, res, next) {
var url = "Example.JSON"
request({
url: url,
json: true
}, function (error, response, obj) {
if (!error && response.statusCode === 200) {
// console.log(obj) // Print the json response
for(key in obj.categories){
var img = obj.categories[key];
var title = obj.categories[key].product_category_title;
console.log(title);
// console.log(obj.categories[key]);
}
res.render('fuelTypeFilter', { title: 'Fuel Type', item: title });
} //end of if
}); // end of request
});
What res.render does is basically serve up a plain HTML file with some template inserted, so with data like the object you pass into the render call. Therefore, res.render acts like a return statement, as in when your code reaches that mark, it signals to the router that we need to render this page (present in the browser to user), therefore we must be done with our router handling logic.
As to whether it's possible to 'loop through' render calls, the answer is no since that would be implying to just, even if succeeded, present however many pages you want instantaneously one after the other, so in that perspective, you wouldn't really want to keep making render calls as your view in the browser would effectively keep refreshing moment after moment.
One way to do this with render is just package your array-like data into the object that you pass into res.render and then, assuming you are using some sort of templating engine, do something analogous to a 'for loop' there, looping and printing the elements as, say HTML divs.
Hope this is a bit helpful.

$.post(url, data) doesn't work

I'm creating simple twitter_clone using Rails to create json API and ReactJS in frontend.
What I need now is to save new created tweet into DB and then to update an API in json which contain list of tweets to be able to use them to render a view.
To achieve it I try to use post request:
My add tweet function in main.jsx file
addTweet(tweetToAdd){
$.post("/tweets", { body: tweetToAdd }) //after saving to database
.success( savedTweet => {
let newTweetsList = this.state.tweetsList;
newTweetsList.unshift(savedTweet);
this.setState({tweetsList: newTweetsList});
})
.error(error => console.log(error));
}
There is a problem with delivering body of the tweet to database, cause after submitting there is NULL here.
Probably it means that body isn't send to DB ,but rest of parameters there are.
in /tweets there is an json API which looks like:
[{"id":17,"user_id":1,"body":null,"created_at":"2015-12-18T10:11:25.085Z","updated_at":"2015-12-18T10:11:25.085Z","name":"Marek Czyż"}]
When I create tweet manually form console everything works. so the problem must have been in previous piece of code.
Secondly after pressing SUBMIT tweet Ive recevied a warning that
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of TweetList. See fb.me/react-warning-keys for more information.
although Ive got a key to every Tweet:
let tweets = this.props.tweets.map(tweet => );
Please, help me.
Assuming you're passing the right value as tweetToAdd, make sure you permit the body param in your controller. If it works in the console, it's not a validation problem, rather an unpermitted param.
As for the error you're seeing, you'll need to add a key prop to each rendered tweet. Something like:
render() {
let tweets = this.props.tweets;
return <ul>
{tweets.map(tweet => {
return <li key={tweet.id}>{tweet.body}</li>;
})}
</ul>;
}

calling JSON webservice & using html

I've been using XML a lot lately but now I'm practicing with some JSON.
What I am trying to do is make a button and text box - so the user can type in a zip code and it will get the info for that zip code...
Using JSON from geonames.org
It's frustrating me trying to figure this out, I've found it easy when I was making my own files with XML but now I am trying to use an actual website and JSON.
Please show me how to do this! Would appreciate it! Thanks.
First of all HTML cannot process a json response from a server. You can send a get or post in json format to a server and get a json response back but you need something other than HTML to process that JSON message. Browsers can format and display XML but not JSON (they just display it as a string). The easiest way to do that in a browser is use JavaScript. For this I would recommend using the jquery library.
http://jquery.com
Here's an example of some jquery I used recently to process a returned JSON string.
$(document).ready(function() {
$(".img a").on("click", function(event) {
event.preventDefault();
var item;
if ((item= $(this).attr( 'href' ))=="saved") return false;
$(this).html("<div style='line-height:4em '>Saved</div>");
$(this).attr("href","saved");
var action ="add";
jqxhr = $.post("webservice.php", { action: action, color: item }, function(data) {
var result=data.result;
if (result=="saved") {
self.html("<div>Saved</div>");
self.attr("href","saved");
}
}, "json")
.error(function() {
alert("error: unable to contact web service");
});
});
});
The returned JSON string from this request is { result: saved }. So as you can see you access the associated array as part of the data object. In my case data.result provided me with the value of result from the json string.
Note my example is for using an anchor tag to pass a value to send in the webservice call. In your case you will need to use a form.
If you're using jeoquery then just look at the html source of UI sample. It's showing the same autocomplete box that you might be trying to implement no custom code to write to parse returned JSON. Below code should work for you:
<input class="input-large" id="city" type="text"/>
$("#city").jeoCityAutoComplete();