How to inject a JS script in Qute Template Engine - quarkus

I am using Quarkus with the qute template engine. I need to inject some dynamic js script to load on the HTML page. But qute convert js like this:
Template file hello.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>redirect by script</title>
</head>
<body>
<p>Hi {name}!</p>
<script>
{script}
</script>
</body>
</html>
Controller
#RequestScoped
public class Resource {
#Inject
#Location("hello.html")
Template hello;
#Route(path = "/s/:name", methods = HttpMethod.GET)
public Uni<String> rScript(RoutingContext rc) {
String s = "console.log('Hi from script');";
return Uni.createFrom().completionStage(() -> hello
.data("name", rc.request().getParam("name"))
.data("script", s)
.renderAsync());
}
}
The template render file like below and script will not run on browser:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>redirect by script</title>
</head>
<body>
<p>NOT_FOUND!</p>
<script>
console.log('test redirect by script')
</script>
</body>
</html>
How can I pass script data to qute template file?

You will want to turn off Character Escapes:
Either use raw:
<script>
{script.raw}
</script>
</body>
</html>
Or return a RawString:
#Route(path = "/s/:name", methods = HttpMethod.GET)
public Uni<RawString> rScript(RoutingContext rc) {
String s = "console.log('Hi from script');";
return Uni.createFrom().completionStage(() -> hello
.data("name", rc.request().getParam("name"))
.data("script", new RawString(s))
.renderAsync());
}
}

Related

Spring Boot AJAX call API

I tried write code for ajax call api from controller. I want ajax get content of link api. I have trouble write code ajax. Please help me write this code with ajax
RestController.java
#Responsebody
#GetMapping("api/hello")
public String hello (){
return "Hello World";
Index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<button type="submit" name ="clickPost"></button>
</body>
</html>
I want when I click button, website return strings Hello World. Please help me write code ajax execute this request, i'm newbie. If my code controller has error, please change it help me. Thanks
Your RestController.java is fine (only missing '}' ). There is actualy no ajax call from your index.html.
It can look somethink like that:
<!DOCTYPE html>
<html>
<head>
<script>
function callAPI() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(this.responseText)
}
};
xhttp.open("GET", "/api/hello", true);
xhttp.send();
}
</script>
</head>
<body>
<button onClick="callAPI()">Click me</button>
</body>
</html>
Depending on your particular environment you still might need to update the API URL in javascript or enabling CORS. Check these links
https://web.dev/cross-origin-resource-sharing/
https://spring.io/guides/gs/rest-service-cors/

Flutter web the type File is defined in multiple classes

I have a Flutter web project, where I would like to select a picture from the device and upload it to Firebase Storage. I found this solution:
Future<void> uploadToFirebase(File imageFile) async { //I get the error here
final filePath = 'images/${DateTime.now()}.png';
StorageTaskSnapshot snapshot = await FirebaseStorage.instance
.ref()
.child(filePath)
.putFile(imageFile)
.onComplete;
print("UploadToFirebase");
if (snapshot.error == null) {
final String downloadUrl = await snapshot.ref.getDownloadURL();
await Firestore.instance
.collection("images")
.add({"url": downloadUrl, "name": "${DateTime.now()}.png"});
} else {
print('Error from image repo ${snapshot.error.toString()}');
throw ('This file is not an image');
}
}
void uploadImage() async {
InputElement uploadInput = FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen(
(changeEvent) {
final file = uploadInput.files.first;
final reader = FileReader();
reader.readAsDataUrl(file);
reader.onLoadEnd.listen(
(loadEndEvent) async {
print("Calling uploadToFirebase");
uploadToFirebase(file);
print("Done");
},
);
},
);
}
But this code has the following error in the line with the comment:
The name 'File' is defined in the libraries 'dart:html' and 'dart:io'.
Try using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports.dartambiguous_import
After this I added a hide in my import dart html:
import 'dart:html' hide File;
However this resulted in another error in the uploadImage function, where I call uploadToFirebase(file):
The argument type 'File (where File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\html\html_dart2js.dart)' can't be assigned to the parameter type 'File (where File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\io\file.dart)'.dartargument_type_not_assignable
html_dart2js.dart(15975, 7): File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\html\html_dart2js.dart
file.dart(241, 16): File is defined in C:\Users\Asus\Documents\flutter\bin\cache\pkg\sky_engine\lib\io\file.dart
My index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="12 órás eventek kezelésére">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="event_maker">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>event_maker</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-app.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-firestore.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-analytics.js"></script>
</script>
<script>
// Your web app's Firebase configuration
var firebaseConfig = {
...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
Any other ideas on how to solve this? Or is there a better way to upload a file with a web app?
I'm a beginner to Flutter, so sorry, if it is a dumb question. Thanks for your help in advance!
I think you're missing a for Firebase storage. Try adding the following line:
<script src="https://www.gstatic.com/firebasejs/7.19.0/firebase-storage.js"></script>
dart:html 'File' extends Blob <-use this if uploading from web
dart:io 'File' extends FileSystemEntity <-use this if uploading from a platform with file access
in your case
use .putBlob(imageFile)
instead of .putFile(imageFile)

Get Raw Html From the controller Spring thymeleaf

I have a controller that create model attribute and passes to the view "partial.html" to generate output
partial.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<p>
<span th:text="'Today is: ' + ${message}"></span>
</p>
</body>
</html>
and inside a controller method
model.addAttribute("message", search);
How to do I get Htlm Output to a string inside controller method?
like this
String htmlOutput="from partial.html";
Let's say you have a HTML file with two variable name and todayDate.
You want to process it and want to store it in a string / database / AWS S3.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Hello</p>
<p th:text="${name}"></p>
<p th:text="${todayDate}"></p>
</body>
</html>
Your HTML file location is src/main/resources/templates/home.html
By using the below function you can get the final processed HTML as:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Hello</p>
<p>Manoj</p>
<p>30 November 2019</p>
</body>
</html>
import org.thymeleaf.context.Context;
#GetMapping("/")
public void process() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
// https://github.com/thymeleaf/thymeleaf/issues/606
templateResolver.setForceTemplateMode(true);
templateEngine.setTemplateResolver(templateResolver);
Context ctx = new Context();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
Calendar cal = Calendar.getInstance();
ctx.setVariable("todayDate", dateFormat.format(cal.getTime()));
ctx.setVariable("name", "Manoj");
final String result = templateEngine.process("home", ctx);
System.out.println("result:" + result);
}
If you're using the usual Spring MVC approach, as Joanna says you're doing things in the wrong order. The Controller creates the model and specifies the view, and then after that the view is rendered by the Thymeleaf template that uses the model.
If, on the other hand, you're trying to render Thymeleaf templates yourself (rather than sending them to the user's browser directly, maybe for use in HTML email or to store prerendered pages in a database or something), then you'd need to create your own Thymeleaf Template Engine to use. Refer to the "Creating and configuring the Template Engine" section of the documentation for details. You can create your own Engine, and then use its process method to get the result of the template to put into a variable for further use.
You may looking for this, getting directly the result HTML, just ignore the email part of the post.
Then, you can create this:
final Context ctx = new Context(locale);
ctx.setVariable("name", recipientName);
ctx.setVariable("subscriptionDate", new Date());
ctx.setVariable("hobbies", Arrays.asList("Cinema", "Sports", "Music"));
ctx.setVariable("imageResourceName", imageResourceName);
// so that we can reference it from HTML
final String htmlContent = this.templateEngine.process("html/email-inlineimage.html", ctx);
So you have the htmlContext of rendered thymeleaf template (with vars)
Once the control goes out to view processor (JSP/Thymeleaf etc), it will not be coming back to controller. You will be able to get the raw html response in a customFilter, but not in the Controller.

Why can I not use a body class selector with Laravel Dusk?

I am using Laravel 5.5 and Dusk 2.0. I have the following html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body class="my-body-class" id="my-body-div">
<div class="my-content-class" id="my-content-div">
Content goes here.
</div>
</body>
</html>
Here's my Dusk test.
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit('/test/admin-fixed-layout');
$this->assertNotNull($browser->element('.my-content-class'));
$this->assertNotNull($browser->element('#my-content-div'));
// $this->assertNotNull($browser->element('.my-body-class'));
$this->assertNotNull($browser->element('#my-body-div'));
});
}
If I un-comment the assertion that uses the body class selector, the test fails. Why?
This is because by default prefix is set to body:
public function __construct($driver, $prefix = 'body')
{
$this->driver = $driver;
$this->prefix = trim($prefix);
}
in Laravel\Dusk\ElementResolver class.
If you really need to change this (but probably there is no point), you can add the following method into Tests/DuskTestCase class:
protected function newBrowser($driver)
{
return new \Laravel\Dusk\Browser($driver, new \Laravel\Dusk\ElementResolver($driver, ''));
}
This will override default browser and pass empty prefix instead of default body prefix

Thymeleaf th:inline="javascript" issue

I don't know how to solve the following: I'd like to let my Model generate real javascript dynamically based on some model logic.
This final piece of javascript code then should be added inside the $(document).ready { } part of my html page.
The thing is: If I use inline="javascript", the code gets quoted as my getter is a String (that is how it is mentioned in the Thymeleaf doc but it's not what I need ;-)
If I use inline="text" in is not quoted but all quotes are escaped instead ;-) - also nice but unusable 8)
If I try inline="none" nothing happens.
Here are the examples
My model getter created the following Javascript code.
PageHelper class
public String documentReady() {
// do some database operations to get the numbers 8,5,3,2
return "PhotoGallery.load(8,5,3,2).loadTheme(name='basic')";
}
So if I now try inline="javascript"
<script th:inline="javascript">
/*<![CDATA[*/
jQuery().ready(function(){
/*[[${pageHelper.documentReady}]]*/
});
/*]]>*/
</script>
it will be rendered to
<script>
/*<![CDATA[*/
jQuery().ready(function(){
'PhotoGallery.load(8,5,3,2).loadTheme(name=\'basic\')'
});
/*]]>*/
</script>
Which doesn't help as it is a String literal, nothing more (this is how Thymeleaf deals with it).
So if I try inline="text" instead
<script>
/*<![CDATA[*/
jQuery().ready(function(){
PhotoGallery.load(8,5,3,2).loadTheme(name='basic')
});
/*]]>*/
</script>
Which escapes the quotes.
inline="none" I do not really understand, as it does nothing
<script>
/*<![CDATA[*/
jQuery().ready(function(){
[[${pageHelper.documentReady}]]
});
/*]]>*/
</script>
To be honest I have no idea how to solve this issue and hopefully anybody out there knows how to deal with this.
Many thanks in advance
Cheers
John
I would change the approach.
Thymeleaf easily allows you to add model variables in your templates to be used in Javascript. In my implementations, I usually put those variables somewhere before the closing header tag; to ensure they're on the page once the JS loads.
I let the template decide what exactly to load, of course. If you're displaying a gallery, then render it as you would and use data attributes to define the gallery that relates to some JS code. Then write yourself a nice jQuery plugin to handle your gallery.
A relatively basic example:
Default Layout Decorator: layout/default.html
<!doctype html>
<html xmlns:layout="http://www.thymeleaf.org" xmlns:th="http://www.thymeleaf.org">
<head>
<title>My Example App</title>
<object th:remove="tag" th:include="fragments/scripts :: header" />
</head>
<body>
<div layout:fragment="content"></div>
<div th:remove="tag" th:replace="fragments/scripts :: footer"></div>
<div th:remove="tag" layout:fragment="footer-scripts"></div>
</body>
</html>
The thing to notice here is the inclusion of the generic footer scripts and then a layout:fragment div defined. This layout div is what we're going to use to include our jQuery plugin needed for the gallery.
File with general scripts: fragments/scripts.html
<div th:fragment="header" xmlns:th="http://www.thymeleaf.org">
<script type="text/javascript" th:inline="javascript">
/*<![CDATA[*/
var MY_APP = {
contextPath: /*[[#{/}]]*/,
defaultTheme: /*[[${theme == null} ? null : ${theme}]]*/,
gallery: {
theme: /*[[${gallery == null} ? null : ${gallery.theme}]]*/,
images: /*[[${gallery == null} ? null : ${gallery.images}]]*/,
names: /*[[${gallery == null} ? null : ${gallery.names}]]*/
}
};
/*]]>*/
</script>
</div>
<div th:fragment="footer" xmlns:th="http://www.thymeleaf.org">
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/my_app.js"></script>
</div>
In the scripts file, there are 2 fragments, which are included from the decorator. In the header fragment, a helpful context path is included for the JS layer, as well as a defaultTheme just for the hell of it. A gallery object is then defined and assigned from our model. The footer fragment loads the jQuery library and a main site JS file, again for purposes of this example.
A page with a lazy-loaded gallery: products.html
<html layout:decorator="layout/default" xmlns:layout="http://www.thymeleaf.org/" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Products Landing Page</title>
</head>
<body>
<div layout:fragment="content">
<h1>Products</h1>
<div data-gallery="lazyload"></div>
</div>
<div th:remove="tag" layout:fragment="footer-scripts">
<script type="text/javascript" src="/js/my_gallery.js"></script>
</div>
</body>
</html>
Our products page doesn't have much on it. Using the default decorator, this page overrides the page title in the head. Our content fragment includes a title in an h1 tag and an empty div with a data-gallery attribute. This attribute is what we'll use in our jQuery plugin to initialize the gallery.
The value is set to lazyload, so our plugin knows that we need to find the image IDs in some variable set somewhere. This could have easily been empty if the only thing our plugin supports is a lazyloaded gallery.
So the layout loads some default scripts and with cleverly placed layout:fragments, you allow certain sections of the site to load libraries independent of the rest.
Here's a basic Spring controller example, to work with our app: MyController.java
#Controller
public class MyController {
#RequestMapping("/products")
public String products(Model model) {
class Gallery {
public String theme;
public int[] images;
public String[] names;
public Gallery() {
this.theme = "basic";
this.images = new int[] {8,5,3,2};
this.names = new String[] {"Hey", "\"there's\"", "foo", "bar"};
}
}
model.addAttribute("gallery", new Gallery());
return "products";
}
}
The Gallery class was tossed inline in the products method, to simplify our example here. This could easily be a service or repository of some type that returns an array of identifiers, or whatever you need.
The jQuery plugin that we created, could look something like so: my_gallery.js
(function($) {
var MyGallery = function(element) {
this.$el = $(element);
this.type = this.$el.data('gallery');
if (this.type == 'lazyload') {
this.initLazyLoadedGallery();
}
};
MyGallery.prototype.initLazyLoadedGallery = function() {
// do some gallery loading magic here
// check the variables we loaded in our header
if (MY_APP.gallery.images.length) {
// we have images... sweet! let's fetch them and then do something cool.
PhotoGallery.load(MY_APP.gallery.images).loadTheme({
name: MY_APP.gallery.theme
});
// or if load() requires separate params
var imgs = MY_APP.gallery.images;
PhotoGallery.load(imgs[0],imgs[1],imgs[2],imgs[3]).loadTheme({
name: MY_APP.gallery.theme
});
}
};
// the plugin definition
$.fn.myGallery = function() {
return this.each(function() {
if (!$.data(this, 'myGallery')) {
$.data(this, 'myGallery', new MyGallery(this));
}
});
};
// initialize our gallery on all elements that have that data-gallery attribute
$('[data-gallery]').myGallery();
}(jQuery));
The final rendering of the products page would look like so:
<!doctype html>
<html>
<head>
<title>Products Landing Page</title>
<script type="text/javascript">
/*<![CDATA[*/
var MY_APP = {
contextPath: '/',
defaultTheme: null,
gallery: {
theme: 'basic',
images: [8,5,3,2],
names: ['Hey','\"there\'s\"','foo','bar']
}
};
/*]]>*/
</script>
</head>
<body>
<div>
<h1>Products</h1>
<div data-gallery="lazyload"></div>
</div>
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/my_app.js"></script>
<script type="text/javascript" src="/js/my_gallery.js"></script>
</body>
</html>
As you can see, Thymeleaf does a pretty good job of translating your model to valid JS and actually adds the quotes where needed and escapes them as well. Once the page finishes rendering, with the jQuery plugin at the end of the file, everything needed to initialize the gallery should be loaded and ready to go.
This is not a perfect example, but I think it's a pretty straight-forward design pattern for a web app.
instead of ${pageHelper.documentReady} use ${pageHelper.documentReady}

Resources