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

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.

Related

How to insert markup into fragment using 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

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 ...

Acts-as-Taggable in Rails 4 won't work properly

I am using the acts-as-taggable gem in a Rails 4 app with an angular frontend. If I use the console it works fine. I have done all the obvious things and I can get it to work by adding this to the create controller:
if params[:tag_list]
droplet.tag_list.add(params[:tag_list], parse: true)
end
The problem is that it should be doing this anyway. Does anyone have any insight as to why it is simply not firing the tag_list.add method automatically? I am uncomfortable using this hack to get it to work.
And yes, I have added :tag_list to the strong parameters.
Update: The form html
<form ng-submit="createNewDropletForm.$valid && createNewDroplet()" name="createNewDropletForm" novalidate>
<div class="form-group">
<label for="name">Title:</label>
<input ng-keyup="keyup()" ng-model="drop.name" ng-model-options="{ debounce: 500 }" type="text" class="form-control" placeholder="Add Droplet Title Here" required>
</div>
<div class="form-group">
<label for="description">Description:</label>
<textarea class="form-control" rows="4" ng-model="drop.description" placeholder="Add helpful description of what this droplet tests." required></textarea>
</div>
<div class="form-group">
<label for="tags">Tags:</label>
<input class="form-control" ng-model="drop.tag_list" placeholder="add tags separated by commas">
</div>
<button type="submit" class="btn btn-default">Next</button>
</form>

Guide to creating a contact form in rails 4

I am trying to teach myself RoR and am building a site, and want to include a contact form, with "Persons Email" "Subject" and "Message", then a submit button, which will send an email to my email address.
I can't find a simple straightforward guide for beginners for rails 4 that goes through this step by step. So if anyone knows of one or is willing to create a guide, it would be greatly appreciated.
Currently I only have done the visual part, now need to get it to actually send the emails:
<form class="form-horizontal" id="contact-form">
<div class="control-group">
<label class="control-label" for="name">Name</label>
<div class="controls">
<input type="text" name="name" id="name" placeholder="Your name" class="form-control input-lg ">
</div>
</div>
<div class="control-group">
<label class="control-label" for="email">Email Address</label>
<div class="controls">
<input type="text" name="email" id="email" placeholder="Your email address" class="form-control input-lg">
</div>
</div>
<div class="control-group">
<label class="control-label" for="message">Message</label>
<div class="controls">
<textarea name="message" id="message" rows="8" class="form-control input-lg"></textarea>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-default btn-lg btn-block">Submit Message</button>
</div>
</form><!-- End contact-form -->
Catch this - link to a Getting Started with Rails. This guide will show you how to create a simple app. I believe, you will be able to make your contact form after that. And about sending mails - here's a guide to Action Mailer Basics. It will teach you how to send emails from Rails application.
With power of these guides combined, you'll be able to accomplish your task :)
In case it's useful to you (or anyone else), this is a straight forward guide to making a contact form but in rails 5 (and in production too, which is handy, not just development). Not sure how it goes for rails 4

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.

Resources