Thymeleaf: disabling a component - html

I have a basic SpringBoot 2.0.5.RELEASE app. Using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file. I have this template, where I want to disable a select object based on a condition
<form id="menuFormId" class="form-style-9" action="#" th:action="#{/menu/save}" th:object="${menu}" method="post">
<ul>
<li th:each="e : ${#fields.detailedErrors()}" th:class="${e.global}? globalerr : fielderr">
<span th:text="${e.global}? '*' : ${e.fieldName}" ><b>The field name</b></span> : <span th:text="${e.message}" class="red">
<font color="red">The error message</font>
</span>
</li>
</ul>
<ul class="tab_form">
<li>
<select id="selectMenuId" th:field="*{resto}" th:classappend="${menu.id == null} ? disabled='disabled'">
<option value="0">PLEASE SELECT A MENU</option>
</select>
...
But I got this error:
Could not parse as expression: "${menu.id == null} ? disabled='disabled'"

Uhhhh, there are 2 issues:
1.) disabled is a attribute and not a class. Therefore, use the following snippet:
th:disabled="${menu.id == null}"
2.) You can't define a class disabled='disabled'.
The second issue isn't important. There is no need for such a class definition.

Related

Pass TemplateRef with data to 3rd party library

I'm using ng-zorro-antd library and I'm trying to pass a ng-template without duplicating HTML.
The component (in the url) expects TemplateRef as an Input property (nzDot).
This is my ng-template:
<ng-template #dotTemplate let-step>
<div [ngSwitch]="step.status">
<span *ngSwitchCase="'active'" nz-icon nzType="info-circle" nzTheme="outline"></span>
<span
*ngSwitchCase="'error'"
nz-icon
nzType="info-circle"
nzTheme="fill"
class="text-volcano-6"
></span>
<span
*ngSwitchCase="'onboarding'"
nz-icon
nzType="info-circle"
nzTheme="outline"
class="text-yellow-6"
></span>
<span
*ngSwitchCase="'syncing'"
nz-icon
nzType="sync"
nzTheme="outline"
class="text-geekblue-6"
></span>
</div>
</ng-template>
What I'm trying to do is basically send this step object (let-step) as variable to the outlet, but I don't see a way to do this, as this is controlled by the ngTemplateOutlet inside of the 3rd party component.
Is there any smart way to do this?
I see that I can make 4 templates and then bind to the property like this:
step.status === 'Active'
? dotActiveTemplate
: step.status === 'syncing'
? dotSyncingTemplate
: step.status === 'onboarding'
? dotOnboardingTemplate
: dotErrorTemplate
But this solution seems bad and I would appreciate help.
Thanks!

angular 2 validation on dynamic generated fields in loop

I have a list of input fields that are generated with a model. I am trying to add validation to them.
The requirement is they should not be empty or less than 2 characters.
problem is in the documentation only shows validation with non-dynamically generated variable Name. My fields are all generated dynamically. So there is no tempVariableName I can hardcode (otherwise they conflict), so I created temp variable from the name of the property I binded the field to. So I came up with something like this :
<div *ngFor="let field of connector.configFields">
<label>{{field.name}}</label>
<input [(ngModel)]="field.value" [type]="field.name === 'Password' ? 'password' : 'text'"
placeholder="{{field.name}} (required)"
ngControl="[fieldName+field.name]"
required minlength="2"
#fieldName+[field.name]="ngModel" />
<div *ngIf="(fieldName+[field.name]).errors && ((fieldName+[field.name]).dirty || (fieldName+[field.name]).touched)">
<span *ngIf="(fieldName+[field.name]).errors.required">Enter Name</span>
<span *ngIf="(fieldName+[field.name]).errors.minlength">Name minimum at 2 characters</span>
</div>
</div>
and the configFields in typescript look like this :
export class FieldModel {
public name: string;
public type: string;
public value: any;
}
But this simply would not work. I am new to angular 2 so I am not exactly sure what I did wrong.
You can use the unique index for each field in the array. Use this together with the name attribute (and ngModel) which will evaluate each form controls separately. So each input field gets the unique name, eg:
name="f{{i}}"
where we get {{i}} from the iteration:
<div *ngFor="let field of connector.configFields; let i = index">
So finally, your template could look like this:
<form #myForm="ngForm">
<div *ngFor="let field of connector.configFields; let i = index">
<input name="f{{i}}" [(ngModel)]="field.value" [type]="field.name === 'Password' ? 'password' : 'text'" required #f="ngModel" minlength="2"/>
<div *ngIf="f.errors && (f.dirty || f.touched)">
<div *ngIf="f.errors.required"> This field is required </div>
<div *ngIf="f.errors.minlength"> Min 2 chars </div>
</div>
</div>
</form>
Here's a live
Demo
Prepare data in model and return to angular. Angular and hard logic in the template = bad friends.
But if you have a select option and if has *ngFor for option then error message loses its mapping, due second *ngFor loop
better to define custom class for error message and use css display: none or **block*
.custom-valid-box{
display: none;
}
.form-control-danger + .custom-valid-box{
display: block;
}

How to change value of a variable in thymeleaf?

I am new to thymeleaf. I am bit confused now. Please check out the codes below
<th:block th:with="${someVarible=false}">
<th:block th:each="dem : ${demo}">
<th:block th:if="${dem.status==0}">
//Here I need to change the value of someVarible to true
</th:block>
</th:block>
<th:block th:if="${someVariable}">Its true</th:block>
</th:block>
I need to edit the value of someVarible . How can I do it. Thanks in advance.
As Lukas said, it's not possible to change the value of a variable in Thymeleaf, because that only applies to the content within that element. However it is possibe to achieve something very similar using Thymeleaf only.
You can use Collection Selection and the ^[...] syntax to select the first element in your list that matches the criteria status==0. This expression would look like:
${demo.^[status==0]}
If the demo list contains an element with status==0, then that will be returned. Otherwise, it will result in null. This can be used directly in your th:if:
<th:block th:if="${demo.^[status==0]}">Its true</th:block>
Or, if you need to use someVariable for other things too, you can assign it to a variable using th:with (Docs):
<th:block th:with="someVariable=${demo.^[status==0]}">
<th:block th:if="${someVariable}">Its true</th:block>
</th:block>
You can't achieve your desired functionality as you described with Thymeleaf.
th:with does just local variable definition which is only available for evaluation inside that fragment.
<div class="example1" th:with="foo=${bar}">
<!--/* foo is availabile here */-->
<th:block th:text="${foo}" />
</div>
<div class="example2">
<!--/* foo is NOT availabile here! */-->
</div>
And you can't change that variable in template. Thymeleaf is just presentation layer, you're trying to achieve something which has to be done on application layer (Java code).
On application layer (Java code) you could do:
Map<Integer, Boolean> fooMap = new HashMap<Integer, Boolean>();
for(Demo demo : demos) {
if(demo.getStatus() == 0) {
fooMap.put(demo.getId(), true);
} else {
fooMap.put(demo.getId(), false);
}
}
And then on presentation layer (Thymeleaf):
<th:block th:each="demo : ${demos}">
<th:block th:text="${demo.getId()}" />
</th:block>
<th:block th:each="demo : ${demos}">
<th:block th:if="${fooMap.get(demo.getId()) == true}">It's true</th:block>
</th:block>
If you don't want to use HashMap you could use inheritance and extend object Demo.
(Please note that code which I wrote is not tested, so it may need some small fixed, but I hope that I helped you.)
Do you have a form? How do you send data to server?
If your variable of type boolean you can add, e.g., a checkbox for editing :
<th:block th:with="${someVarible=false}">
<th:block th:each="dem : ${demo}">
<th:block th:if="${dem.status==0}">
<label for="someVariableCheck">Edit someVariable</label>
<input id="someVariableCheck" type="checkbox" th:value="${someVariable}"/>
</th:block>
</th:block>
<th:block th:if="${someVariable}">Its true</th:block>

Spring security using thymeleaf with "if" condition

I a using Spring security with an HTML page using thymeleaf. I have a problem to use the "sec:authorize" property in this case:
<ul class="nav nav-tabs margin15-bottom">
<li th:each="criteriaGroup,iterGroups : ${aGroupList}"
th:class="${iterGroups.index == 0}? 'active'">
<a th:href="'#' + ${criteriaGroup.id}" data-toggle="tab"
th:text="${criteriaGroup.groupName}"></a>
</li>
</ul>
Now I want to add a spring security property like: if I have this particular criteria, I need a authorization (sec:authorize="hasRole('Criteria')") although I will not see the tab corresponding to this criteria:
<ul class="nav nav-tabs margin15-bottom">
<li th:each="aGroup,iterGroups : ${aGroupList}" th:class="${iterGroups.index == 0}? 'active'"
th:sec:authorize="$({criteriaGroup.id =='criteriaA'} || ${criteriaGroup.id =='criteriaB'}) ? 'hasRole('Criteria')'">
<a th:href="'#' + ${criteriaGroup.id}" data-toggle="tab"
th:text="${criteriaGroup.groupName}"></a>
</li>
</ul>
But when I am doing this I have the following error:
org.thymeleaf.exceptions.TemplateProcessingException: Error processing template: dialect prefix "th" is set as non-lenient but attribute "th:sec:authorize" has not been removed during process
How can I avoid it?
Remove th: in front of sec:authorize
The Spring Security 3 integration module is a Thymeleaf dialect. More information can be found here.
Perhaps you'd want to use the #authentication object instead of the sec dialect.
From the docs:
<div th:text="${#authentication.name}">
The value of the "name" property of the authentication object should appear here.
</div>

GSP/JSP for loop - first node 'selected' class

This is a GSP problem/niggle I've faced before in JSP and I'm looking for the cleanest possible solution.
Essentially, I'm using a for loop (<g:each> in GSP) to iterate through a list of items and output a HTML node for each item:
<g:each status="i" var="item" in="items">
<span class="item">${item}</span>
</g:each>
All <span> nodes contain a CSS class of item, but I want the first node to also contain a selected class. Thus, I update the code to:
<g:each status="i" var="item" in="items">
<g:if test="${i == 0}">
<span class="item selected">${item}</span>
</g:if>
<g:else>
<span class="item">${item}</span>
</g:else>
</g:each>
This seems like a complicated approach however as I'm duplicating a lot of code. Another solution is to use a custom tag library and pass the current index into it:
<g:each status="i" var="item" in="items">
<span class="item <g:getItemClass index='${i}' />">${item}</span>
</g:each>
The tag library will return selected when index is equal to 0, otherwise it won't return anything at all. Again, this adds complexity.
Other possible solutions:
Use the index in your CSS class name (very messy)
Set a class name var (). Not better than a custom tag imo.
Use scriptlets (no way)
Any other approaches to this that are clean and simple?
Thanks!
Usually it's just a:
<g:each status="i" var="item" in="items">
<span class="item ${i == 0 ? 'selected' : ''}">${item}</span>
</g:each>