The Vaadin Framework guide has a page describing how to use Column Renderers in a Vaadin Grid. And this page describes implementing renderers, but all too briefly.
I want to implement a InstantRenderer
to complement the partial set of java.time renderers added in Vaadin 8.1. Renderers were added for LocalDate
& LocalDateTime
but not for Instant
, OffsetDateTime
, and ZonedDateTime
. For my Instant
renderer I am currently simply applying the current default time zone (ZoneId
) to get a ZonedDateTime
on which I call the toString
method. More could be done, but this is just to get started.
So my code should be very similar to the provided LocalDateTimeRenderer
. I am trying to follow that code as an guide.
In searching the Vaadin source code and reading the doc, it seems I need three pieces of source code:
InstantRenderer
( similar toLocalDateTimeRenderer
)InstantRendererConnector
( similar toLocalDateTimeRendererConnector
)InstantRendererState
( similar toLocalDateTimeRendererState
)
I have done this, and it all compiles. But my table fails to render, all I get is a white empty box on the page. No errors appear on the console or logs. If I remove my use of my InstantRenderer
, and fall back to letting my Instant
objects be rendered by the default of their own toString
methods, all is well and the table appears as expected. So I know my custom renderer is at fault.
I am a newbie when it comes to "server-side" vs "client-side" Vaadin.
➠ Is there some kind of packaging I need to perform? Currently I have my three classes in my Vaadin project alongside the MyUI
source file.
➠ Am I missing some other piece?
I instantiate my renderer by calling the no-arg constructor:
this.entriesGrid
.addColumn( Entry::getStart )
.setCaption( "Start" )
.setRenderer( new InstantRenderer( ) )
;
Here are my three files listed above, taken almost entirely from the Vaadin source code.
InstantRenderer
/*
* By Basil Bourque. Taken almost entirely from source code published by Vaadin Ltd.
*
* --------
*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.basil.timepiece;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
import elemental.json.JsonValue;
/**
* A renderer for presenting {@code Instant} objects.
*
* @author Vaadin Ltd
* @since 8.1
*/
public class InstantRenderer
extends com.vaadin.ui.renderers.AbstractRenderer< Object, Instant >
{
private DateTimeFormatter formatter;
private boolean getLocaleFromGrid;
private ZoneId zoneId = ZoneId.systemDefault(); // Basil Bourque.
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the grid's locale it is
* attached to, with the format style being {@code FormatStyle.LONG} for the
* date and {@code FormatStyle.SHORT} for time, with an empty string as its
* null representation.
*
* @see <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html#LONG">
* FormatStyle.LONG</a>
* @see <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/time/format/FormatStyle.html#SHORT">
* FormatStyle.SHORT</a>
*/
public InstantRenderer ()
{
this( DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG , FormatStyle.SHORT ) , "" );
getLocaleFromGrid = true;
}
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the given formatter, with the
* empty string as its null representation.
*
* @param formatter the formatter to use, not {@code null}
* @throws IllegalArgumentException if formatter is null
*/
public InstantRenderer ( DateTimeFormatter formatter )
{
this( formatter , "" );
}
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the given formatter.
*
* @param formatter the formatter to use, not {@code null}
* @param nullRepresentation the textual representation of the {@code null} value
* @throws IllegalArgumentException if formatter is null
*/
public InstantRenderer ( DateTimeFormatter formatter , String nullRepresentation )
{
super( Instant.class , nullRepresentation );
if ( formatter == null )
{
throw new IllegalArgumentException( "formatter may not be null" );
}
this.formatter = formatter;
}
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the given string format, as
* displayed in the grid's locale it is attached to, with an empty string as
* its null representation.
*
* @param formatPattern the format pattern to format the date with, not {@code null}
* @throws IllegalArgumentException if format pattern is null
* @see <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns">
* Format Pattern Syntax</a>
*/
public InstantRenderer ( String formatPattern )
{
this( formatPattern , Locale.getDefault() );
getLocaleFromGrid = true;
}
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the given string format, as
* displayed in the given locale, with an empty string as its null
* representation.
*
* @param formatPattern the format pattern to format the date with, not {@code null}
* @param locale the locale to use, not {@code null}
* @throws IllegalArgumentException if format pattern is null
* @throws IllegalArgumentException if locale is null
* @see <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns">
* Format Pattern Syntax</a>
*/
public InstantRenderer ( String formatPattern , Locale locale )
{
this( formatPattern , locale , "" );
}
/**
* Creates a new InstantRenderer.
* <p>
* The renderer is configured to render with the given string format, as
* displayed in the given locale.
*
* @param formatPattern the format pattern to format the date with, not {@code null}
* @param locale the locale to use, not {@code null}
* @param nullRepresentation the textual representation of the {@code null} value
* @throws IllegalArgumentException if format pattern is null
* @throws IllegalArgumentException if locale is null
* @see <a href=
* "https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#patterns">
* Format Pattern Syntax</a>
*/
public InstantRenderer ( String formatPattern , Locale locale , String nullRepresentation )
{
super( Instant.class , nullRepresentation );
if ( formatPattern == null )
{
throw new IllegalArgumentException( "format pattern may not be null" );
}
if ( locale == null )
{
throw new IllegalArgumentException( "locale may not be null" );
}
formatter = DateTimeFormatter.ofPattern( formatPattern , locale );
}
@Override
public JsonValue encode ( Instant value )
{
String dateString;
if ( value == null )
{
dateString = this.getNullRepresentation();
} else if ( this.getLocaleFromGrid )
{
if ( null == this.getParentGrid() )
{
throw new IllegalStateException(
"Could not find a locale to format with: "
+ "this renderer should either be attached to a grid "
+ "or constructed with locale information" );
}
ZonedDateTime zdt = value.atZone( this.zoneId ); // Basil Bourque.
Locale locale = this.getParentGrid().getLocale();
dateString = zdt.format( formatter.withLocale( locale ) );
} else
{
ZonedDateTime zdt = value.atZone( this.zoneId ); // Basil Bourque.
dateString = zdt.format( formatter );
}
return encode( dateString , String.class );
}
@Override
protected InstantRendererState getState ()
{
InstantRendererState s = ( InstantRendererState ) super.getState();
return s;
}
@Override
protected InstantRendererState getState ( boolean markAsDirty )
{
InstantRendererState s = ( InstantRendererState ) super.getState( markAsDirty );
return s;
}
}
InstantRendererConnector
/*
* By Basil Bourque. Taken almost entirely from source code published by Vaadin Ltd.
*
* --------
*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.basil.timepiece;
import com.vaadin.shared.ui.Connect;
/**
* A connector for InstantRenderer.
* <p>
* The server-side Renderer operates on {@code Instant}s, but the data is
* serialized as a string, and displayed as-is on the client side. This is to be
* able to support the server's locale.
*
* @author Vaadin Ltd
* @since 8.1
*/
@Connect( InstantRenderer.class )
public class InstantRendererConnector extends com.vaadin.client.connectors.grid.TextRendererConnector
{
@Override
public InstantRendererState getState ()
{
return ( InstantRendererState ) super.getState();
}
}
InstantRendererState
/*
* By Basil Bourque. Taken almost entirely from source code published by Vaadin Ltd.
*
* --------
*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.basil.timepiece;
/**
* Shared state of InstantRenderer.
*
* @author Vaadin Ltd
* @since 8.1
*/
public class InstantRendererState extends com.vaadin.shared.ui.grid.renderers.TextRendererState
{
// This code intentionally left blank.
}
Source on BitBucket
I have posted my complete Maven-driven project on BitBucket, all the necessary files for column renderers for Instant
, OffsetDateTime
, and ZonedDateTime
.
Feature Request
I posted Issue # 10208 Implement column renderers for Vaadin Grid for other java.time types (Instant, OffsetDateTime, ZonedDateTime) to complement the LocalDate & LocalDateTime renderers in Vaadin 8.1. on the Vaadin Framework GitHub page.
Special packaging required
Yes, special packaging is required. You cannot simply toss the Vaadin Grid column renderer implementation classes into a regular Vaadin app.
Two of the three classes needed for a column renderer implementation involve client-side development, rather than the usual server-side development we do commonly in Vaadin app work.
Fortunately, this is easier than it might sound. To just do a simple column renderer, Vaadin fortunately provides some super-classes that do most of the heavy-lifting. So we need not learn about all the gory details of the GWT and JavaScript magic that goes on under the covers in Vaadin.
The path to success involves:
vaadin-archetype-widget
Start a new project using a multi-module Maven archetype provided by the Vaadin team:
vaadin-archetype-widget
seen in this list.addon
moduleOnce you have created a project from that archetype in your IDE, add your three column renderer classes as shown in this screen shot for an
Instant
renderer.Renderer
class goes in the 'addon' module’s main package.RendererConnector
&RendererState
class files go in the 'addon' module’s nestedclient
package.Of course, in real work you would delete the example
MyComponent…
files created by the archetype.demo
moduleOnce built you can try your column renderer in the 'demo' module’s Vaadin app by importing the package of the 'addon' module. In this case:
GitHub source
My successful implementation of a
Instant
column renderer was take entirely from threeLocalDateTimeRenderer
related classes provided with Vaadin 8.1.3 source code. You can find the current version of these classes by typingLocalDateTimeRenderer
in the GitHub find file feature.shared/src/main/java/com/vaadin/shared/ui/grid/renderers/LocalDateTimeRendererState.java
server/src/main/java/com/vaadin/ui/renderers/LocalDateTimeRenderer.java
client/src/main/java/com/vaadin/client/connectors/grid/LocalDateTimeRendererConnector.java