Reputation: 1231
I'm trying to see if I can get the Bootstrap datePicker (datePicker)to work in Grails v3.3.9/fields plugin 2.2.10
I've done a standalone page, that is I create a controller, and give it an action called 'ldt'.
I create grails view called ldt.gsp like this. When I hit trigger my controller in the browser - this simple page renders fine and datePicker works
<!DOCTYPE html>
<html>
<head>
<title>Bootstrap datepicket demo</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script type='text/javascript' src='//code.jquery.com/jquery-1.8.3.js'></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker3.min.css">
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js"></script>
<script type='text/javascript'>
$(function(){
//$('.input-group.date').datepicker({
$('#datepicker').datepicker({
calendarWeeks: true,
todayHighlight: true,
autoclose: true
});
});
</script>
</head>
<body>
<div class="container">
<h1>Bootstrap datepicker</h1>
<div class="input-group date" id="datepicker">
<input type="text" class="form-control">
<span class="input-group-addon">
<i class="glyphicon glyphicon-calendar"></i>
</span>
</div>
</div>
</body>
</html>
You can see that here. This is not using normal Grails site mesh etc
So I tried to put the same into views/layouts/main.gsp
So here is the head section of my revised main.gsp. (I've not included Bootstrap nor jQuery as them come with grails application.js).
<!doctype html>
<html lang="en" class="no-js">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>
<g:layoutTitle default="Grails"/>
</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<asset:link rel="icon" href="favicon.ico" type="image/x-ico"/>
<asset:stylesheet src="application.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker3.min.css">
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js"></script>
<script type='text/javascript'>
$(function(){
$('#datepicker').datepicker({
calendarWeeks: true,
todayHighlight: true,
autoclose: true
});
});
</script>
<g:layoutHead/>
<%-- header assets --%>
</head>....
Now I am trying to render a specific field in the domain class - which is of type LocalDateTime. To try and do this I have created a _fields/localDateTime/_wrapper.gsp that looks like this in which I setup the input control (as I did for the standalone page), but I give the input a start date time for now.
localDateTime/_wrapper.gsp
<%@ page import="java.time.LocalDateTime; java.time.format.DateTimeFormatter" %>
<g:set var="localDateTimePattern" value="${message(code: 'default.localDateTime.format',default: 'yyyy-MM-dd HH:mm')}"/>
<div class="fieldcontain" >
<div class="container">
<div class="row">
<div class='col-sm-6'>
<div class="form-group">
<div class="input-group date" id='datetimepicker1' >
<label for=${this.pageScope.property}> ${this.pageScope.getVariable("label")} </label>
<input type='text' class="form-control" value="${java.time.LocalDateTime.now().toString()}"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
When I go this page in the browser it gets rendered, but the clickable field behaviour etc does not fire and the field has no calendar icon on the right hand side (this may be because Grails is loading Bootstrap 4, and when I tried the standalone with v4.3.1 the icon didn't render - but the click event still worked).
See rendered form here - using the 'edit' action which in turn is using standard scaffolds with <f:all
which calls my _wrapper.gsp.
The string value of the date gets rendered fine (not formatted), but I get no click action and calendar doesn't pop up - no action at all. I've included a snip of the browser page inspection in the browser opened for the contractSignedDate property.
So if a standalone page gsp works just fine (albeit with Bootstrap 4 as loaded by Grails bau) does my click action not fire
So why can't the gsp action action fire when using the main.gsp/normal fields plugin/and _wrapper.gsp for java LocalDateTime? It worked for standalone gsp example so I know the datePicker works
How can I get the Bootstrap datepicker as described here working in a Grails scaffolded form?
PS it's not missing jqueryUI library - I've included 1.12.1 in both standalone and main.gsp see this image from inspector. Works fine on standalone but not using main.gsp and scaffolded forms
PS if I click into the scaffolded form property as shown in the _widget.gsp, the outer text box is highlighted in blue (which the bau grails rendered elements don't - so some form of click action is happening - just not getting a calendar to pop up.
Upvotes: 0
Views: 924
Reputation: 1231
This is very odd. When referenced on standalone gsp and link JS/CSS from CDN, the page worked.
If I add <meta name="layout" content="main" />
and had the page rendered via site mesh in main.gsp it would fail.
I tried many different combinations in the standalone page <head>
, and basically the date picker doesn't seem to need the bootstrap js to work - but it does need the jQuery and it must be in the head section.
If you want the styles you need to include the bootstrap.css and the bootstrap-datepicker3 CSS.
<head>
<%--<meta name="layout" content="main" />--%>
<title>Bootstrap datepicket demo</title>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<%--<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script> --%>
<script type='text/javascript' src='//code.jquery.com/jquery-3.3.1.js'></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker3.min.css">
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js"></script>
</head>
However if you uncommented the meta tag it would all stop. In the end to get this to work I had to edit the /assets/application.js and remove the entries for Bootstrap and jQuery using the local copies in /assets/javascripts, and instead reference these directly from the cdn in main.gsp.
So my working application.js looks like this - where I include my own LCM-app JS:
// This is a manifest file that'll be compiled into application.js.
//
// Any JavaScript file within this directory can be referenced here using a relative path.
//
// You're free to add application-wide JavaScript to this file, but it's generally better
// to create separate JavaScript files as needed.
//= require LCM-app
//= require popper.min
//= require_self
My LCM-app js just looks like this where my selector is using class selector - note you seem to have add the dot between group and date '.input-group.date' - but that becomes a space in your markup
// used by bootstrap date picker - matching class selector is for class class='.input-group date'
$(function(){
$('.input-group.date').datepicker({
calendarWeeks: true,
todayHighlight: true,
autoclose: true
});
});
I then went to main.gsp and edited in the cdn references that I had in the standlone page (asset plugin is now disabled for jQuery and Bootstrap)
main.gsp (head section) - you dont need jqueryUI for this to work
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>
<g:layoutTitle default="Grails"/>
</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<asset:link rel="icon" href="favicon.ico" type="image/x-ico"/>
<asset:stylesheet src="application.css"/>
<%-- <script type='text/javascript' src='//code.jquery.com/jquery-1.12.1.js'></script>--%>
<link href="//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script type='text/javascript' src='//code.jquery.com/jquery-3.3.1.js'></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker3.min.css">
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js"></script>
<g:layoutHead/>
<%-- header assets --%>
</head>
Now when you uncomment out the <meta>
tag in the standalone page and remove the direct refs - then the main.gsp site mesh adds these back exactly as they were in the standalone page
Point your browser at the test page and it all works as normal.
It even works when I put the basic code into a taglib (you have to stop the escaping using the static encodeAsForTags and set to 'none' for your tag).
JdtDateTimeTagLib
class JavaDateTimeTagLib {
static defaultEncodeAs = [taglib:'html']
//static encodeAsForTags = [tagName: [taglib:'html'], otherTagName: [taglib:'none']]
static encodeAsForTags = [ jdtScaffoldField:'none']
static namespace = "jdt" //java8 date time name space for tags
GrailsNameUtils grailsNameUtils = new GrailsNameUtils()
def jdtScaffoldField = {attrs, body ->
assert grailsNameUtils
String propertyName = attrs.propertyName ?: "unknown"
String property = attrs.property ?: "unknown"
String value = attrs.value
String naturalName = grailsNameUtils.getNaturalName(propertyName)
String label = attrs.label ?: 'unknown'
String ldtStr = attrs.ldt
String page = """
<g:set var="localDateTimePattern" value="${message(code: 'default.localDateTime.format',default: 'yyyy-MM-dd HH:mm')}"/>
<div class="fieldcontain" >
<div class="container">
<div class="row">
<div class='col-sm-6'>
<div class="form-group form-inline">
<label class='control-label' for="$propertyName"> ${label} </label>
<div class="input-group date" >
<input type='text' class="form-control" value="${ldtStr?.toString()}"/>
<span class="input-group-addon">
<i class="glyphicon glyphicon-calendar"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
"""
out << page
}
}
I can then simplify the _fields/localDateTime/_wrapper.gsp to use that tag like so
<%@ page import="java.time.LocalDateTime; java.time.format.DateTimeFormatter" %>
<g:set var="localDateTimePattern" value="${message(code: 'default.localDateTime.format',default: 'yyyy-MM-dd HH:mm')}"/>
<jdt:jdtScaffoldField propertyName="xyc" label="my label"></jdt:jdtScaffoldField>
Now when you go to a normal grails edit action my field is rendered and the calendar actions.
I've lost variables in this version as I'm not getting the values passed to the wrapper and putting them forward into my tag - but I think that's not too hard. See working screen shot 'as-is'
Sadly the lest of the Grails scaffolding is using its own rendering so the CSS styles don't match, alignment is a bit out etc - but its an improvement if I can just get the submit action etc to take the selected date and convert pack to LocalDateTime to persist in the database.
Basically it would be better if the grails <g:datePicker
could be 'fixed' to work with new Java time formats and use the Bootstrap picker - because the fields plugin invokes the <g:datePicker
for ordinary Java Date but completely doesn't handle the Java 8 temporal stuff.
I have tried to 'find' where the grails <g: taglib
is implemented (thought it was going to be in the GSP plugin or core projects - but I can't find it in either.
However whilst it's not ideal - its good enough for the mini demo using scaffolds I was going to show people - just looks a little clunky as it's not consistent.
Upvotes: 0