How to insert markup into fragment using Thymeleaf - thymeleaf

I want to declare some layout to use as template for all form fields.
So given roughly this fragment
<div th:fragment="control(input)" class="form-group">
<label class="col-sm-3 control-label">Edition description</label>
<div class="col-sm-9">
<input th:replace="${input}">
</div>
</div>
And this fragment "invocation"
<div th:replace=":: control(~{:: input})">
<!--Want to reference this one, not all inputs-->
<input th:field="*{editionDescription}" type="text" class="form-control" placeholder="перераб., дополн.">
</div>
It would produce the following result:
<div class="form-group">
<label class="col-sm-3 control-label">Edition description</label>
<div class="col-sm-9">
<input th:field="*{editionDescription}" type="text" class="form-control" placeholder="перераб., дополн.">
</div>
</div>
The problem is that :: input input selector is applied to all <input> tags in currently processed template. How do I reference only enclosed one?
Additionally, can you give an advice on how to approach this in general. I want to mimic Grails Fields plugin as much as possible. Do you think I'd better declare my own dialect?

You should be defining classes (or identifiers) to your input elements (the ones you want to replace) and then you can use the appropriate selector syntax just to select the desired input. You can find selector syntax details in here
Sample code might look like this:
<div th:fragment="control(input)" class="form-group">
<label class="col-sm-3 control-label">Edition description</label>
<div class="col-sm-9">
<input th:replace="${input}">
</div>
</div>
Then your selector should be like:
<div th:replace=":: control(~{:: input.description})">
<!--Want to reference this one, not all inputs-->
<input th:field="*{editionDescription}" type="text" class="form-control description" placeholder="перераб., дополн.">
</div>
Please note the use of :: input.description as a selector with class identifier

Related

Accessing Map values in gsp page Grails using variables as key

I am trying to create multiple divs on the fly using a template which is called for a number of times and each time i create a new div ... which seems to be working fine below is the code. I am passing a map tempMap from the controller to the gsp page which is of the below format
tempMap = [key_1:v1,key_2:v2,key_3:v3] //from the controller
//this is the gsp part
<g:set var="counter" value="${1}" />
<g:while test="${counter <= tempMap.counter}">
<g:render template="travelDetailsToShow" />
<g:set var="counter" value="${counter + 1}" />
</g:while>
I need to set the ids of the elements based on the counter which also is working ... But i am unable to set the values of the fields.
<div class="col-sm-6" id="key_${counter}"> // This sets the id to key_1, key_2 depending on the counter value
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
<g:set var="temp" value="kep_${counter}" />
**<input type="text" name="key_${counter}" class="form-control" value="${tempMap?.key_${counter}}" readonly></input> // this does not work**
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
I searched a lot but with no luck ... can anyone tell me where am i going wrong with this .. Any help is appreciated ... Thanks in advance people
The render tag has a model attribute. It works like the model passed from controller to view. You can use it to pass data into the template.
view
<g:each in="${1..tempMap.counter}" var="counter">
<g:set var="key" value="key_${counter}">
<g:render template="travelDetailsToShow" model="${[id: key, value: tempMap[key]]}"/>
</g:each>
template
<div class="col-sm-6" id="${id}">
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
<input type="text" name="${id}" class="form-control" value="${value}" readonly></input>
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
Thanks Emmanuel for your answer ... but i think i wasnt clear enough and misled you to think that the "key" would be static ... As your logic would expect the static value "key" ..... The proper structure of the map is below ...
tempMap = [key_1:v1,anotherkey_1:v2,yetanotherkey_1:v3,key_2:v4,anotherkey_2:v5,yetanotherkey_2:v6]
I havent tried out the solution posted by you but got another solution to work.... code for which is below ...
<div class="col-sm-6" id="key_${counter}">
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
<input type="text" name="key_${counter}" class="form-control" value='${tempMap?."${'key_' + counter}"}' readonly></input>
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
<div class="col-sm-6" id="anotherkey_${counter}">
<div class="form-group">
<label>Return Date</label>
<span class="input-icon-right input-group">
<input type="text" name="anotherkey_${counter}" class="form-control" value='${tempMap?."${'another_' + counter}"}' readonly></input>
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
</div>
The problem was that i was not referencing the value in the Map properly... The code that fails is stated below.
<div class="col-sm-6" id="departureDate_${counter}">
<div class="form-group">
<label>Departure Date</label>
<span class="input-icon-right input-group">
**<input type="text" name="departureDate_${counter}" class="form-control" value="${tempMap?.${departureDate_counter}"} readonly></input>** // THIS CODE DOES NOT WORK. This will look for a key ${departureDate_counter} and will through a gsp error that tags have not been closed
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
</span>
</div>
</div>
Although i have found a solution to my problem it would be nice if we posted more solutions as i feel that not a lot of solutions are present on the net for grails and gsp ...

make hint span wrapper div in simple_form horizontal-form?

When using the horizontal-form in simple_form 3, I've got a f.input :name, hint: "this is a really long hint" the hint will display under the input field and wrap to whatever width the input is constrained to.
How do I make the hint display under both the label and the input but still within the form-group div that is around the label and the input?
This is how one of my fields is currently rendered (as shown in the screenshot above):
<div class="form-group string optional scenario_answers_value">
<label class="string optional col-xs-8 control-label" for="scenario_answers_attributes_14_value">Customer's Normal Oil Change Interval</label>
<div class="col-xs-4">
<input class="string optional form-control" data-original-title="hrs" data-placement="bottom" data-toggle="tooltip" data-trigger="focus" id="scenario_answers_attributes_14_value" name="scenario[answers_attributes][14][value]" rel="tooltip" type="text">
<p class="help-block">Most LT manufacturers recommend 250 hours because of wet stacking problems from the engine not running under a load.</p>
</div>
</div>
You force your form-group to render with bootstrap grid. 8/12 of row for a label and 4/12 for input field. In this way, you can`t use hint as you want.
You need something like:
`
<div class='form-group'>
<div class='row'>
<div class='col-xs-8'>
<label>
</div>
<div class='col-xs-4'>
<input class='form-control'>
</div>
</div>
<div class='row'>
<div class='col-xs-12'>
<p class='hint'></p>
</div>
</div>
</div>
Or you could try to style hint with CSS, but it is not the best way.
Also, you could provide your erb\haml\slim markup for more useful answers.

When using Angular.js, how to retain form data after submit?

I have used the basic angular script which update what ever you type in the input field on any element we specify, real time...
<!doctype html>
<html ng-app>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">
<hr>
<h1>Hello {{yourName}}!</h1>
</div>
</body>
</html>
Im new to Angular. I used this on may rails app. But the problem is, the field I used ng-model will reset its valu after submit. Even setting the 'value' attribute won't work. How can I fix this?
Exact code generated from my rails application :
<form accept-charset="UTF-8" action="/members" class="custom" id="new_member" method="post">
<div class="row collapse text-field">
<div class="small-4 columns">
<h3>Add Member : </h3>
</div>
<div class="small-6 columns left inline">
<h3 class="subheader inline"> {{newEntry.name}}</h3>
</div>
</div>
<div class="row collapse text-field">
<div class="small-3 columns">
<label class="prefix" for="member_name">Full Name</label>
</div>
<div class="small-7 columns left">
<input class="input" id="member_name" name="member[name]" ng-model="newEntry.name" type="text" value="gj" />
</div>
</div>
<div class="row collapse text-field">
<div class="small-3 columns">
<label class="prefix" for="member_address">Address</label>
</div>
<div class="small-9 columns">
<textarea class="input" height="115" id="member_address" name="member[address]">
</textarea>
</div>
</div>
<div class="row">
<div class="small-9 columns" ><input class="button radius" name="commit" type="submit" value="Add Member" /></div>
</div>
</form>
Note : I haven't used an ng-controller. Im new to Angular. If its required, please tell me how to convert the above to the controller. I can get the value of the field and send it back to the form in rails. Its there in a variable. But Angular keeps wiping it!
Note2 : This problem persist only for the input field I used angular-model.. All the other fields retained the data!
Ok this is not the best solution but you could do this:
<input type="text" ng-init="yourName = 'Your Value Goes Here'" ng-model="yourName" placeholder="Enter a name here">
ng-init directives are run when the app intialises, so your value will be assigned to angular's internal "yourName" model and be updated in the view accordingly.
That would solve your problem but its not the best way. Hopefully that will get you going for now - I'll try and post a more "ideal" solution shortly.

How to handle Twitter Bootstrap fields with Thymeleaf Spring and less code

I need some advice how is a recommended way to handle Twitter Bootstrap fields with Thymeleaf. I know that recommendations are not so easy, so I wrote my thoughts about it and hope you can comment it. At the end there a some concrete questions.
First I tried a fragment which shows what is needed to generate
<div th:fragment="textfield">
<div class="control-group"
th:classappend="${#fields.hasErrors('__${fId}__')}? 'error'">
<label class="control-label" th:for="${fId}"
th:text="#{model.__*{class.simpleName}__.__${fId}__}+':'">
FirstName
</label>
<div class="controls">
<input type="text" th:class="${inputclass}" th:field="*{__${fId}__}" th:disabled="${disabled}"/>
<span class="help-inline" th:if="${#fields.hasErrors('__${fId}__')}"
th:errors="*{__${fId}__}"></span>
</div>
</div>
</div>
which can be used with
<div class="control-group replace" th:include="templates::textfield" th:with="fId='userId'" th:remove="tag">
<label class="control-label replace">Benutzer-Id</label>
<div class="controls replace">
<input type="text" value=""/>
</div>
</div>
or in short
<div class="control-group replace" th:include="templates::textfield" th:with="fId='userId'" th:remove="tag"/>
It's not very flexible about the input, so you need for a checkbox an own fragment.
Next I choose the layout-approach:
<div layout:fragment="bsfield">
<div class="control-group" th:classappend="${#fields.hasErrors('__${fId}__')}? 'error'">
<label class="control-label" th:for="${fId}"
th:text="#{model.__*{class.simpleName}__.__${fId}__}+':'">
FirstName </label>
<div class="controls">
<span layout:fragment="bsinput" th:remove="tag">
<input type="text" class="replace" th:field="*{__${fId}__}" th:disabled="${disabled}"/>
</span>
<span class="help-inline" th:if="${#fields.hasErrors('__${fId}__')}"
th:errors="*{__${fId}__}"></span>
</div>
</div>
</div>
Which is very flexible because I can define my input directly.
I can use it shortly with
<div layout:include="templates::bsfield" th:with="fId='firstName'" th:remove="tag">
<div layout:fragment="bsinput">
<input type="text" th:field="*{__${fId}__}" th:disabled="${disabled}"/>
</div>
</div>
or more prototype style
<div class="control-group" layout:include="templates::bsfield" th:with="fId='lastName'" th:remove="tag">
<label class="control-label" th:remove="all">Last Name</label>
<div class="controls" th:remove="tag">
<div layout:fragment="bsinput">
<input type="text" th:field="*{__${fId}__}" th:disabled="${disabled}"/>
</div>
</div>
</div>
Both variants has still a lot of boilerplate. So I think about the following solution inspired by Playframework helper.
<input type="text" th:bsfield="firstName" th:disabled="${disabled}"/>
and writing a Processor which creates
<div class="control-group"
th:classappend="${#fields.hasErrors('${fId}')}? 'error'">
<label class="control-label" th:for="${fId}"
th:text="#{model.__*{class.simpleName}__.${fId}}+':'">
FirstName </label>
<div class="controls">
<input type="text" th:class="${inputclass}" th:field="*{${fId}}" th:disabled="${disabled}"/>
<span class="help-inline" th:if="${#fields.hasErrors('${fId}')}"
th:errors="*{${fId}}"></span>
</div>
</div>
and replace ${fId} with the value of bsfield in this example "firstname". After that Thymeleaf should recompute it (setRecomputeProcessorsImmediately (true);) For the prototype I think it's necessary to write a JS-Solution.
I'm unsure if this is really clever or a misuse of Processors. Furthermore I'm unsure how much time a beginner need to write such a processor. Are 4 hours realistic or more a few days?
Would appreciate if someone can give me a hint.
In the meantime I did it. As a beginner you must calculate 4-8 hours, without JUnit tests (it looks difficult to test processors) and DTD and editor-support. The most problems I had was that it's difficult to reuse an existing node after changing attributes. Here it's better to clone it.
Next time I think I can do it in 1 or 2 hours.
The experience is very good, you have clean and short code. With the JS-File you don't lose the prototyping experience.

ruby on rails using bootstrap - prepend text and append button to an input field in a form

I am using bootstrap-sass 2.0.0 in my gemfile, but I am having trouble getting both prepended and appended content added to an input tag in a form to work.
I got on FireBug with FireFox and copied the exact html that they use for the example on the twitter bootstrap site, copied here:
div class="control-group">
<label class="control-label" for="appendedPrependedInput">Append and prepend</label>
<div class="controls">
<div class="input-prepend input-append">
<span class="add-on">$</span>
<input id="appendedPrependedInput" class="span2" type="text" size="16">
<span class="add-on">.00</span>
</div>
</div>
</div>
But I get the prepended text facing the wrong way (it looks like the appended tag) so it kind of looks like this: [span)[.....input....][span) instead of this: (span][.....input.....][span) .
Also appended buttons do not register as being appended (also with copied code from the example) like so: (....input....) (button) instead of (....input.....][button)
Any ideas on how I can get to this: (span][....input....][button) ? I already tried the code below:
<form class="form-inline">
<div class="control-group">
<div class="controls">
<div class="input-prepend input-append">
<span class="add-on">Search</span>
<input id="search" class="input-xlarge" type="text" size="16">
<button class="btn" type="button">Go!</button>
</div>
</div>
</div>
</form>
I appreciate any and all help!
You have the right idea, you just have to put everything that is either to be appended/prepended or have something appended/prepended to it on one line with no spaces.
Like this:
<form class="form-inline">
<div class="control-group">
<div class="controls">
<div class="input-prepend input-append">
<span class="add-on">Search</span><input id="search" class="input-xlarge" type="text" size="16"><button class="btn" type="button">Go!</button>
</div>
</div>
</div>
</form>
I tested this on my own Bootstrap setup and it displays properly.

Resources