Thymeleaf generates URL with no query parameters, but '?' appears - spring-boot

I'm working on a simple Spring Boot MVC application using Thymeleaf.
Part of the thymeleaf code:
<tr th:each="dept: ${departments}">
<td th:text="${dept.getId()}"></td>
<td th:text="${dept.getName()}"></td>
<td>
<form th:object="${dept}" th:action="#{'/departments/' + ${dept.getId()}}">
<button class="btn btn-secondary">Details</button>
</form>
</td>
<td>
<form th:object="${dept}" th:action="#{/departments/{id}/edit(id=${dept.getId()})}">
<button class="btn btn-secondary">Edit</button>
</form>
</td>
<td>
<form th:object="${dept}" th:action="#{/departments/{id}(id=${dept.getId()})}" th:method="delete">
<button class="btn btn-secondary">Delete</button>
</form>
</td>
</tr>
The UI interface looks like this:
When clicking on the Edit button (same goes for the Details one), it loads correctly, but the URL generates the ? character for other query parameters, but there are actually none (for example http://localhost:8089/departments/3/edit?).
The controller code is:
#GetMapping("/{id}/edit")
public String editDepartment(#PathVariable("id") String departmentId, Model model) {
var department = departmentService.getDepartmentById(Long.valueOf(departmentId));
model.addAttribute("department", department);
return "edit-department"; // returns view
}
My problem is with the ? character that is being displayed. I don't want it to appear as there are no query parameters. Any help?

It's adding a question ? because you are submitting an empty form. Why not just use a link styled as a button?
<a class="btn btn-secondary" th:href="#{/departments/{id}/edit(id=${dept.id})}" role="button">Edit</a>

Try
<form th:object="${dept}" th:action="#{/departments/edit/{id}(id=${dept.getId()})}">

Related

(Vue + Laravel) v-if / v-else inside <td> throws no matching end tag error

I apologize in advance if my question is silly-- Vue newbie here but very eager to learn!
In order to create an interface to manage user privileges in a web app, I've made a component in which I want to create a table with nested v-fors.
For each row, I want 5 cells (): the first one includes text depending on the current iteration of object permisos and the other 4 should be created from the object tipos_permisos (which is an object with 'fixed' values).
The problem:
When I try to compile, I get multiple errors claiming that some tags have no matching end tag. I assume it is due to the v-for nested inside another v-for and/or the v-if inside the innermost v-for... or something like this. The claim is that has no matching tag or that the element uses v-else without corresponding v-if :/
I've tried writing out the last 4 cells manually (without using the tipos_permisos object) but even then, I get errors. In this case, claiming that , and have no matching end tag.
The desired result:
Please note that for some of the resources listed, some of the privileges might not apply (i.e., the log viewer is read-only always so it doesn't have the C (create), U (update) or D (delete) privileges, hence the conditional to show either a checkbox or an icon)
My component:
<template>
<form :action="usr.url.savepermisos" method="post" class="">
<div class="card">
<div class="card-header bg-gd-primary">
<h3 class="card-title">Privilegios de {{ usr.nombre }}</h3>
</div>
<div class="card-content">
<p class="mb-3">
El usuario
<span class="font-w700">{{ usr.nombre }}</span>
tiene los siguientes privilegios:
</p>
<table class="table">
<thead>
<tr>
<th>Recurso</th>
<th>Alta / Creación</th>
<th>Visualización</th>
<th>Edición</th>
<th>Eliminación</th>
</tr>
</thead>
<tbody>
<tr v-for="(recurso, idxrecurso) in permisos">
<td :data-order="recurso.id">{{ recurso.etiqueta }}</td>
<td class="align-middle" v-for="(color,tp) in tipos_permisos">
<label :class="'checkbox checkbox-' + color + ' mycheckbox'" v-if="(typeof recurso[tp] !== "undefined")">
<input type="checkbox" class="checkbox-input check_permiso" />
<span class="checkbox-indicator"></span>
</label>
<i class="fas fa-minus-square fa-lg text-muted" data-toggle="tooltip" title="N/A" v-else></i>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</template>
<script>
export default {
data() {
return {
tipos_permisos: {
'C': 'success',
'R': 'info',
'U': 'warning',
'D': 'danger'
}
}
},
props: [
'usr',
'permisos',
'perm_log'
]
}
</script>
If something is unclear please let me know so that I can provide further info.
There is a missing "=" after v-for:
<td class="align-middle" v-for"(color,tp) in tipos_permisos">
I didn't understand this part:
v-if="(typeof recurso[tp] !== "undefined")"
If undefined in your code is a string, your condition should be
v-if="recurso[tp] !== 'undefined'"
If it's a real undefined, it should be like this:
v-if="recurso[tp] !== undefiened"

How to filter data in Thymeleaf table

I'm a new Thymeleaf developer (Spring-Boot 2.1.5 environment).
I want to implement filtering over a list from an Input. When I enter the name of the client in the input it appears on a table (without submit button).
<form th:action="#{/clientslistbypage}" method="get" th:object="${Clients}" class="form-inline" >
<input type="text" name="client" th:value="${client}" id="clientSearch" >
<!-- <input type="submit" name="clients" th:value="Search">-->
</form>
I tried the Projection and Collection function but I do not know how to dynamically assign the input value to the Collection formula.(${Clients.?[firstName >='here i want to insert the value of the Searche Input']})
<tr th:each = "c: ${Clients.?[firstName >='']}" >
<td th:text = "${c.id}"></td>
<td th:text = "${c.firstName}"></td>
<td th:text = "${c.LAST_NAME} "></td>
</tr>
I tried DANDELION but unfortunately, do not run under Spring-Boot 2.1.5.
any proposal, tutorial, or example is welcome

Why "checked" attribute doesn't apply to my Thymeleaf's checkboxes?

I was working with Spring and Thymeleaf when I've encountered the following problem: I need a Form object, which has a list of items (Item) as attribute; I'm using an html form to print the Name of the Item, and to generate a checkbox for each Item (any checkbox's value is the corresponding item's id).
The form works correctly, sending to the Controller a list of item's ids corresponding to the checked checkboxes.
However, now, I'm trying to check some checkbox upon the occurrence of a condition (if itemIds, which is a list, contains the current item's id). For that I'm using:
th:checked="${#lists.contains(itemIds, item.id)}"/>
But it doesn't work (checkbox are all unchecked).
I tried also with a "dummy test":
th:checked="${1 == 1 ? 'checked' : ''}"/>
But, again, all the checkbox remain unchecked; the "checked" attribute is ignored as you can see in this example of the rendered HTML:
<input type="checkbox" value="12" class="chkCheckBox" id="ids1" name="ids">
What am I doing wrong? What am I missing here?
form.html
<form th:action="#{${uriBase}+'/new/' + ${date}}"
method="POST" th:object="${form}">
<div class="table-responsive">
<table class="table">
<thead class=" text-primary">
<tr>
<th>Name</th>
<th><input type="checkbox" th:id="checkAll"/>Check</th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${items}">
<td th:text="${item.name}"></td>
<td>
<input type="checkbox" th:field="*{ids}"
th:value="${item.id}" th:class="chkCheckBox"
th:checked="${#lists.contains(itemIds, item.id)}"/>
</td>
</tr>
</tbody>
</table>
<button type="submit" class="btn btn-primary pull-right">Submit</button>
<div class="clearfix"></div>
</div>
</form>
Form class
public class Form implements Serializable {
private List<Long> ids;
//getter and setter
}
Thank you in advance.
I have been struggling with this as well. If you use the th:field it will override the checked and value options, as Xaltotun mentions, because it is trying to get the value and checked option from the field/form.
If you change it to th:name it should work how you want...
But this forum seems to be helpful for doing it with th:feild.
As far as I understand there are 2 issues in your post:
The dummy example is incorrect.
th:checked="${1 == 1 ? 'checked' : ''}"
In fact the value must true or false not 'checked'. If you try with
th:checked="${1 == 1}"/>
It will work.
If you set th:field="*{ids}" then the checkbox should be trying to get the value from the field item.ids and will not use the "th:checked" or "th:value" properties. Does the item has the field ids?

Select a button on the form of a webpage

I am trying to click on upload button which is inside a <form> tag.
I am using java with selenium. I tried with xpath locator to click on button but unable to identify it.
Html Code:
<form id="sportForm" action="/sportmetadata/saveSport" method="post" enctype="multipart/form-data">
<table>
<tr class="hidden">
<td class="lbl-dialog">Id</font></td>
<td>
<td class="lbl-dialog width200" >Background for Team </td>
<td>
<input type="button" class="btn btn-default btn-command btn-upload" value="Upload" onClick="openFileBrowser('sp_backgroundUploadId')" />
<input id="sp_backgroundUploadId" multiple type="file" class="no-display" name="backgroundUpload" />
<input id="backgroundDelesectUpload" value="Clear" type="button" onclick="clearSelection('sp_backgroundUploadId','sp_backgroundDisplayTableId');" class="btn btn-default btn-command btn-upload" />
</td>
</tr>
Please help
Thanks in advance
Locate Upload button using xpath locator.
//input[#value='Upload'][#type='button']
Explanation:- Use value and type attribute of <input> tag.
Suggestion:- Instead of using absolute xpath, use relative xpath.
OR
Locate Upload button using cssSelector
input[value='Upload'][type='button']
Explanation:- Use value and type attribute of <input> tag.
You should try to avoid XPATH directly copied from browsers as they are not stable ones. When I copy xpath from my browser with the same HTML code I get
//*[#id="sportForm"]/table/tbody/tr/td[4]/input[1] which is different from what you have posted
You should build your xpath more intellegently
This should work
//input[#type='button' and #value='Upload']
You can use cssSelector to identify as follows:
driver.findElement(By.cssSelector("#sportForm > table > tr td:nth-child(3)>input"))

Angular 2 doesn't update my view after I add or edit item

I've finally made my app in angular 2. Everything is solved, except one thing. When I add item into my table or edited it, I can't see the change until I refresh page or click for example next page button (I have implemented pagination). I included:
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
in this order. My method for adding item is very simple:
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(departments => this.department = departments.json());
this.getAll();}
Whhen I add item, and put breakpoint on get method, It is called correctly and I get right information from my DB, but I don't know why view isn't refreshed then. Do you have any idea why is it happened? Thanks for suggestions!
EDIT: department is just department: Department, where Department is interface with properties (departmentNo, departmentName, departmentLocation). The view for adding item looks like:
<form [ngFormModel]="myForm"
(ngSubmit)="addDepartment(newItem); showAddView=false" [hidden]="!showAddView" align="center">
<div>
<label for="editAbrv">Department name:</label><br>
<input type="text" [(ngModel)]="newItem.departmentName" [ngFormControl]="myForm.controls['departmentName']" >
<div *ngIf="myForm.controls['departmentName'].hasError('required')" class="ui error message"><b style="color:red;">Name is required</b></div>
</div>
<br/>
<div>
<label for="editAbrv">Department Location:</label><br>
<input type="text" [(ngModel)]="newItem.departmentLocation" [ngFormControl]="myForm.controls['departmentLocation']" >
<div *ngIf="myForm.controls['departmentLocation'].hasError('required')" class="ui error message"><b style="color:red;">Location is required</b></div>
</div>
<br/>
<div>
<button type="submit" [disabled]="!myForm.valid" class="ui button">Add item</button>
<button><a href="javascript:void(0);" (click)="showHide($event)" >
Cancel
</a></button>
</div>
</form>
and my department table is:
<table align="center">
<thead>
<tr>
<td>#</td>
<td><strong>Department</strong></td>
<td><strong>Department Location</strong></td>
<td><strong>Edit</strong></td>
<td><strong>Delete</strong></td>
</tr>
</thead>
<tbody>
<tr *ngFor="#department of departments | searchString:filter.value ; #i = index">
<td>{{i + 1}}.</td>
<td> {{department.departmentName}}</td>
<td>{{department.departmentLocation}}</td>
<td>
<button class="btnEdit" (click)="showEdit(department)">Edit</button>
</td>
<td>
<button class="btnDelete" (click)="deleteDepartment(department)" >Delete</button>
</td>
</tr>
</tbody>
</table>
With this code, you don't wait for the response of the addDepartment request and execute the getAll request directly.
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(departments => this.department = departments.json());
this.getAll();
}
You should move the call to getAll within the callback registered in subscribe. At this moment, the addDepartment is actually done and you can reload the list...
The code could be refactored like this (it's a guess since I haven't the content of addDepartment and getAll methods):
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(addedDepartment => {
this.department = this.getAll();
});
}
This issue occurs because of the way you're using departmant and how change detection works. When you use *ngFor="#department of departments", angular change detection looks for a object reference on departments array. When you update/change one of the items in this array object reference to the array itself doesn't change, so angular doesn't run change detection.
You have few options:
1) change reference of the array by replacing it with new array with updated values
2) tell angular explicitly to run change detection (which I think is easier in your case):
constructor(private _cdRef: ChangeDetectorRef) {...}
addDepartment(item){
this._departmentService.addDepartment(item)
.subscribe(departments => this.department = departments.json());
this.getAll();
this._cdRef.markForCheck();
}

Resources