Thymeleaf - How to call template html page from static html page - java

I'm developing a Spring boot application using Thymeleaf as the view technology. I have a html page dashboard.html inside src/main/resources/templates folder, which is being called from inside a controller.
#PostMapping("/users/register")
public String registerUser(#Validated #ModelAttribute User user, Model model) {
User registeredUser = usersDAO.registerUser(user);
if (registeredUser == null) {
return "500error";
}
model.addAttribute("name", user.getName());
model.addAttribute("username", user.getUsername());
model.addAttribute("emailId", user.getEmailId());
return "dashboard";
}
I have some more static html files inside static folder. I want to call dashboard.html from a static html file like using anchor tag <a/>. How can this be done?
I cannot directly link to this file when my app is running locally. For example: localhost:8080/templates/dashboard.html will not work.

You should create a controller for your thymeleaf html template. For example:
#Controller
#RequestMapping("/templates")
public class DashboardController {
#GetMapping("/dashboard")
public ModelAndView dashboard() {
DashboardModel dashboardModel = new DashboardModel();
return new ModelAndView("dashboard", "dashboard", dashboardModel);
}
}
Then you can link to http://localhost:8080/templates/dashboard and get your dashboard.html page.
Of course you can change the #RequestMapping("/templates") and #GetMapping("/dashboard") to control the url as you like.

Related

Post Object with List of Object containing multipart files

I'm trying to send a post request from Thymeleaf to my controller. The page dynamically creates new input fields when a button is pressed and the page can have multiple of these fields.
function addNewRow(){
let row = document.createElement("tr");
let data = document.createElement("td");
let floorData = document.createElement("td");
let imageData = document.createElement("td");
let floorInput = document.createElement("input");
let imageInput = document.createElement("input");
row.setAttribute("id","newAdditions");
floorInput.setAttribute("id","floorNumber");
floorInput.setAttribute("type", "text");
floorInput.setAttribute("placeHolder", "Floor Number");
floorInput.setAttribute("class", "form-control floorNumber");
floorData.setAttribute("id", "floorNumberData")
floorData.appendChild(floorInput);
imageInput.setAttribute("type","file");
imageInput.setAttribute("accept","image/*");
imageInput.setAttribute("id","imageData");
imageInput.setAttribute("class","imageData");
imageData.appendChild(imageInput);
row.appendChild(data);
row.appendChild(floorData);
row.appendChild(imageData);
document.getElementById("tableBody").appendChild(row);
}
Now when the submit button is pressed, the images (imageData) is passed into a List along with the floorNumber
function submitImages(){
let allItems = document.querySelectorAll('[id=newAdditions]');
for(let i = 0; i < allItems.length; i++){
let floor = allItems[i].getElementsByClassName('floorNumber');
let image = allItems[i].getElementsByClassName('imageData');
floor[0].setAttribute("name", "images[" + i +"].floorNumber");
image[0].setAttribute("name", "images[" + i +"].floorImage");
}
$('#floorForm').submit();
}
My #ModelAttribute/Dto class is as below
public class UploadFormDto{
private List<UploadData> images;
private Long id;
getters & setters
}
UploadFormDto contains
public class UploadData{
private MultipartFile floorImage;
private String floorNumber;
getters & setters
}
And my controller method is as below
#PostMapping(value = "/edit")
#Transactional
public String handleEditHotelRooms(#ModelAttribute("editForm") UploadFormDto editForm,
#PathVariable("hotelId") Hotel hotel,
BindingResult result, RedirectAttributes redirectAttributes) throws WebActionException {}
ISSUE: When the form is sent through, the list of objects populate the floorNumber correctly but the file is never sent through. What am i doing wrong or missing?
My thymeleaf page does not have the enctype='multipart/form-data' because if I add it, the controller complains that the "Current request is not a multipart request"
So i finally managed to fix this, the issue was not in the js or the DTO. The issue was on the Thymeleaf page itself, i had "ajax-form=true" in the form attribute of the page. Once that was removed, I was able to submit the form correctly and get all the images to the controller.

Frontend vs backend endpoints (spring boot and vuejs)

Somewhat based on this guide:
https://jaxlondon.com/blog/java-core-languages/put-spring-boot-und-vue-js-practical-use-project-tutorial/
I have created a multi module maven project where one submodule is my backend and another submodule is my frontend. When I build the whole project first the frontend is "build" then its dist/ resources are copied to the backend which is then build and I can successfully start my spring boot backend with java -jar target/backend-1.0.0-SNAPSHOT and access it on localhost:8080
which makes sense based on the controller I have implemented in the backend:
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
#RequestMapping("/")
public Greeting root(#RequestParam(value = "name", defaultValue = "Root!") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
If I instead access: http://localhost:8080/index.html I end up in my frontend:
Which currently have the following two routes:
router.js
Vue.use(Router);
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: HomeRoute
},
{
path: '/myroute',
name: 'myroute',
component: MyRoute
}
]
});
export default router;
And in e.g. App.vue I have:
<template>
<div class="hello">
<li>
<router-link to="/MyRoute">GoToMyRoute</router-link>
</li>
<li>
<router-link to="/">GoToHome</router-link>
</li>
<router-view></router-view>
</div>
</template>
That I can also access, e.g.:
So far so good. But if I try to enter:http://localhost:8080/MyRoute directly in my browser I get:
which I assume is because I am missing a backend #RequestMapping for /MyRoute in my controller.
Based on the above my questions become:
Do I need to maintain a backend RequestMapping for each vuejs route I have if I want to be able to access it directly in the browser?
How do I separate/order my frontend and backend endpoint? Right now it seems there is no convention for when a backend endpoint is accessed compared to a pure frontend endpoint/route.
I would suggest you to do it this way:
Have one "ui" controller on your backend which would forward any unmapped routes to your frontend application e.g.:
#RequestMapping(value = "/**/{[path:[^\\.]*}")
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
Other rest endpoints defined in your backend should be defined after some prefix like "/api", or "/rest" (e.g. localhost:8080/api/some-data would return your json data). So every data endpoint will have this prefix.
Every route visible to user (view navigation) should be done on vue.js side as SPA routing. (e.g. localhost:8080/welcome etc.)
I tried with the following code (inspired by Gus answer):
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
#RestController
public class RouterCtrl {
#RequestMapping("/**/{path:[^.]*}")
public ModelAndView redirect() {
return new ModelAndView("forward:/");
}
}
And consider:
Your backend endpoints must start with the prefix api/ (or some other distinctive word)
Deal with the 404 not found with Router Vue, something like this:
Vue.use(Router);
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: HomeRoute
},
{
path: '/myroute',
name: 'myroute',
component: MyRoute
},
{
path: '/:catchAll(.*)',
name: 'notFound',
component: NotFound
}
]
});
export default router;
I made a simple gif to ilustrate :9
I would suggest remove the following from your code:
#RequestMapping("/")
public Greeting root(#RequestParam(value = "name", defaultValue = "Root!") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
Once the above is done, all routes will then go via index.html and your vue routes will take over from there.
You can place your other endpoints behind /api or something else.

Issue retrieving my documents from storage - hibernate, springboot, java, sql application

I have a java application that allows users to upload documents. The uploaded docs are saved on a network in a specific "client-files" folder and logged in the SQL database in a document storage table. Adding them the the specific file on the network is working accurately, but when I click to download (aka view) the file from the applicataion in the browser, it is returning a 404 error. The url is accurate, and I am not sure where to start with trouble shooting this issue. Any idea, what I should be looking at as to why this isn't working? Clearly the paths are set up correctly since I can download from the app to the storage file. Just not sure what is blocking it from sending it back. Can it be something on the server side?
Also, not sure what code you'd need to know to look at this. This is the download button....
<td>
<span>
<span th:text="${doc.storage}"></span>
<a th:href="#{|/client-files/${client.principleName+' '+client.id+'/'+doc.storage}|}"><i class="fa fa-download"/></a>
</span>
</td>
It doesn't look like the code base has a controller for the viewing aspect. It appears it is just appending the link and supposed to access the folder/files via calling the link?
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/client-files/**").addResourceLocations("file:client-files/");
registry.addResourceHandler("/client-files/**").addResourceLocations("file:carrier-files/");
}
#Controller
public class DocumentsController {
#Autowired
ClientRepository clientRepository;
#Autowired
LicenseRepository licenseRepository;
#Autowired
DocumentRepository documentRepository;
#Autowired
StorageService storageService;
#Autowired
DocumentService documentService;
#Autowired
InsuranceRepository insuranceRepository;
#Autowired
CarrierAppointmentRepository carrierAppointmentRepository;
#Autowired
RoleRepository roleRepository;
#RequestMapping(value="/documents/client/{id}")
public String manageDocuments(Model model, #PathVariable("id") Client client){
Document newDocument=new Document();
License newLicense=new License();
Insurance newInsurance=new Insurance();
model.addAttribute("insurances",insuranceRepository.findByClient(client));
model.addAttribute("newDocument",newDocument);
model.addAttribute("newLicense",newLicense);
model.addAttribute("newInsurance",newInsurance);
model.addAttribute("licenses",licenseRepository.findByClient(client));
model.addAttribute("docs",documentRepository.findByClient(client));
model.addAttribute("client",client);
return "documents";
}
#RequestMapping(value="/save/document")
public String addDoc(Model model, #ModelAttribute(value="newDocument") Document newDocument, #RequestParam("document") MultipartFile file){
Document doc=documentRepository.save(newDocument);
doc.setStorage(storageService.store(file,newDocument.getClient()));
documentRepository.save(doc);
documentService.markDocuments(newDocument.getClient(),newDocument.getType());
return "redirect:/documents/client/"+newDocument.getClient().getId();
}
#RequestMapping(value="/delete/doc/{id}")
public String deleteDoc(Model model, #PathVariable("id") Document doc, HttpServletResponse response) throws IOException {
if (doc.getClient()!=null) {
String type = doc.getType();
storageService.delete(doc.getStorage(),doc.getClient());
documentRepository.delete(doc);
documentService.markDocuments(doc.getClient(), type);
return "redirect:/documents/client/" + doc.getClient().getId();
}else{
String username= SecurityContextHolder.getContext().getAuthentication().getName();
Role role=roleRepository.findByUsernameContainingIgnoreCaseAndActive(username,true);
if (!role.getIsCarrierAdmin()){
return "redirect:/accessDenied";
}
storageService.delete(doc.getStorage(),doc.getCarrierAppointment());
documentRepository.delete(doc);
return "redirect:/carrierAppointment/details/" + doc.getCarrierAppointment().getId();
}
}
#RequestMapping(value="/save/insurance")
public String addInsuranceDoc(#ModelAttribute(value="newInsurance")Insurance newInsurance,#RequestParam("insurance-file") MultipartFile file){
Insurance insurance = insuranceRepository.save(newInsurance);
insurance.setProof(storageService.store(file,newInsurance.getClient()));
insuranceRepository.save(insurance);
documentService.markInsurance(newInsurance.getClient());
return "redirect:/documents/client/"+newInsurance.getClient().getId();
}
#RequestMapping(value="/delete/insurance/{id}")
public String deleteInsurance(Model model,#PathVariable("id")Insurance insurance){
storageService.delete(insurance.getProof(),insurance.getClient());
documentService.markInsurance(insurance.getClient());
insuranceRepository.delete(insurance);
return "redirect:/documents/client/"+insurance.getClient().getId();
}
}

Add video in jsp page

i am trying to put a video inside my jsp page, dynamically, like:
<video id="myVideo" width="420" height="345" controls="controls">
<source src="#{detailsBean.ref.videoLink}" type="video/ogg" /> </video>
But I get this error : org.apache.jasper.JasperException: /details.jsp(64,29) #{..} is not allowed in template text
What should i use instead?
package beans;
import dto.Referinta;
public class DetailsBean
{
Referinta ref = new Referinta( );
public Referinta getRef ( )
{
return ref;
}
public void setRef ( Referinta ref )
{
this.ref = ref;
}
}
I dont think that the problem is in the bean, it's about the tag, can't find one to use with dynamic src. This will work if in src i put the direct link to the video.

Can't display StreamedContent in Primefaces media in JSF [duplicate]

I use the <p:media> to display static PDF content.
<p:media value="/resource/test.pdf"
width="100%" height="300px" player="pdf">
</p:media>
How can I change it to display dynamic content?
Like as in <p:graphicImage>, the value attribute can point to a bean property returning StreamedContent. This only requires a special getter method for the reasons which is explained in detail in the following answer on using <p:graphicImage> with a dynamic resource from a database: Display dynamic image from database with p:graphicImage and StreamedContent.
In your particular example, it would look like this:
<p:media value="#{mediaManager.stream}" width="100%" height="300px" player="pdf">
<f:param name="id" value="#{bean.mediaId}" />
</p:media>
With
#ManagedBean
#ApplicationScoped
public class MediaManager {
#EJB
private MediaService service;
public StreamedContent getStream() throws IOException {
FacesContext context = FacesContext.getCurrentInstance();
if (context.getCurrentPhaseId() == PhaseId.RENDER_RESPONSE) {
// So, we're rendering the HTML. Return a stub StreamedContent so that it will generate right URL.
return new DefaultStreamedContent();
} else {
// So, browser is requesting the media. Return a real StreamedContent with the media bytes.
String id = context.getExternalContext().getRequestParameterMap().get("id");
Media media = service.find(Long.valueOf(id));
return new DefaultStreamedContent(new ByteArrayInputStream(media.getBytes()));
}
}
}

Categories