Implement a column renderer for Vaadin 8 Grid - datagrid

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 to LocalDateTimeRenderer )
InstantRendererConnector ( similar to LocalDateTimeRendererConnector )
InstantRendererState ( similar to LocalDateTimeRendererState )
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:
Creating a separate project using a Vaadin-provided template to build your own Vaadin Add-On.
Populating that project with source code taken from the Vaadin Framework GitHub project.
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 module
Once 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 nested client package.
Of course, in real work you would delete the example MyComponent… files created by the archetype.
demo module
Once 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:
import org.basilbourque.timecolrenderers.InstantRenderer;
GitHub source
My successful implementation of a Instant column renderer was take entirely from three LocalDateTimeRenderer related classes provided with Vaadin 8.1.3 source code. You can find the current version of these classes by typing LocalDateTimeRenderer 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

Related

Change Browser language or project language to test localisation

this is my first time of developing with translation service.
I implemented it, seems to be working, but how can I test other languages?
Can I change the default language in my symfony project?
Or can I change the language transfered to my project via the browser?
(I only found the settings to change browser GUI language)
Regards
n00n
you can also do it inside controller using the session like this:
/**
* Switch language
*
* #Route("/switchLanguage/{locale}/", name="switch_language")
*
* #param Request $request
* #param string $locale
*
* #return RedirectResponse
*/
public function switchLanguageAction(Request $request, $locale): RedirectResponse
{
$request->attributes->set('_locale', null);
$this->get('session')->set('_locale', $locale);
return $this->redirect($request->headers->get('referer'));
}
Yes of course you can there is few approach for translation.
If you want save your project translation data in your database you must create entities for data translation and after that you can use global sql filter for selecting data each language.
And you can read about Symfony Translation Component.
For changing project language you can write listener.
like this
public function onKernelRequest(GetResponseEvent $event)
{
/** Set language parameter*/
$lang = $event->getRequest()->query->get('lang', 'en'); //this is optional you can write another code for getting language.
$event->getRequest()->setLocale($lang);
}
This example if you want to set each request language.
Read symfony translation component documentation there are more useful things.

Symfony4 Doctrine ODM - ManyToMany get empty

I have a problem with my many to many relations
I have a User class that can be on multiple Teams
and a class Team that can have multiple Users
class User
/**
* #var Collection
* #ODM\ReferenceMany(
* targetDocument="App\Model\Document\Team",
* mappedBy="members",
* strategy="setArray"
* )
*/
protected $teams;
class Team
/**
* #var Collection|null
* #ODM\ReferenceMany(
* targetDocument="App\Model\Document\User",
* inversedBy="teams",
* strategy="setArray",
* sort={"username": "asc"},
* cascade={"persist"}
* )
*/
protected $members;
With these annotations, when I add members User to the team and I do a getMembers on an instance of Team, it works.
BUT, when I have an instance of a User (that's a member of a Team) the getTeams return nothing (empty PersistentCollection)
The reference is stored on the Teams Document, as an array of references
"members" : [
{
"$ref" : "User",
"$id" : ObjectId("XXX"),
"$db" : "readDat"
}
],
I don't understand, with the annotations as they are now, I thought Doctrine would do something like this db.getCollection('Team').find({"members.$id":ObjectId("XXX")})
to have the teams but it seems not.
On the other hand, I tried to inverse the mappedBy and inversedBy, I can't even getMembers (on a Team object) or getTeams (on a User object) anymore, both are empty Collection.
How can I have a good Many to Many (without duplicate the references in both Document) ?

Symfony Validation dependent on another property

Trying to validate if one field is not empty (length > 0) then the length of the field being validated must be a certain length (2 characters). It seems like an "Assert\Expression" might work in this situation but I am having trouble trying to find the length of the properties. It seems like you cannot call php functions within the Expression. The expression documentation mentions functions but maybe I do not understand it... Do I need to register my own function that simply return a strlen(). If so how do you register your own functions? Can someone explain if there is a way to do this, or maybe there is a better way than using Expression that I am overlooking...
/**
* #var string
*
* #ORM\Column(name="plate", type="string", length=10)
*/
private $plate;
/**
* #var string
*
* #ORM\Column(name="state", type="string", length=2)
* #Assert\Expression(
* "strlen(this.getPlate()) == 0 or (strlen(this.getPlate()) > 0 and strlen(value) == 2)",
* message="Must be 2 characters"
* )
*/
private $state;
In the above case I get an error The function "strlen" does not exist around position 1
Looks like you will need to register your own function. Have a look at the docs: https://symfony.com/doc/current/components/expression_language/extending.html#registering-functions
There is an example on lowercase, strlen should be very similar.
EDIT:
You can also use a callback validator.
/**
* #Assert\Callback()
*/
public function validateState(ExecutionContextInterface $context)
{
if (!empty($this->plate) && mb_strlen($this->state) !== 2) {
$context->buildViolation('State must be 2 characters long')
->atPath('state')
->addViolation();
}
}
But if you are planning to using this kind of validation in multiple places, you can write and register your own validator.

deactivate_plugins function not working for me at all

So I'm using the WordPress Plugin boilerplate and trying to remove a lite version of my plugin before going ahead with the activation of my new one. But for some reason this code simply does not execute. I have included an example below of what I am trying to do:
class Wp_Content_Calendar_Activator {
/**
* Short Description. (use period)
*
* Long Description.
*
* #since 1.0.0
*/
public static function activate() {
if( is_plugin_active('hello.php') ){
deactivate_plugins('hello.php');
}
}
}

Inserting file content into MySQL database via FormBuilder in Symfony2

In my Symfony2 app I have a need to provide an option to upload a text file. I've already achieved this by using the FormBuilderInterface to create a field with the 'file' type. The file gets uploaded properly but all I really need is to fetch the text from the file and insert it into the proper db table column. Is there a way to fetch the text from an uploaded file on POST? Thanks.
There are at least two ways.
Using Form Events
Using lifecycle callbacks
In both cases the main point is to open uploaded and fill its content to the entity property (after form posted but before data persisted to db). So you need to have 2 entity properties for this: uploaded file handler (not mapped to the db) and file text itself (mapped to the db).
Here is an example with the lifecycle callbacks:
Entity:
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks
*/
class SomeEntity {
/**
* Virtual field used for handling the file
*
* #Assert\File()
*/
private $fileHandler;
/**
* #var string
*
* #ORM\Column(type="text")
*/
private $file;
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function saveFileContent()
{
$tmpName = md5(uniqid(mt_rand(), true)) . '.' . $this->fileHandler->guessExtension();
try
{
$this->fileHandler->move(
'../../../tmp/',
$tmpName
);
} catch (\Exception $e) {}
$this->setFile(file_get_contents('../../../tmp/' . $tmpName));
unlink('../../../tmp/' . $tmpName);
}
}
This also might be helpfull: How to handle File Uploads with Doctrine

Resources