How use registerCustomEditors for multiple dates? - grails

Well, here is the code I've tried so far.
public class CustomDateEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy hh:mm a"), true))
registry.registerCustomEditor(Date.class, new StructuredDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true))
}
}
I am using a calendar jQuery plugin and a regular grails DatePicker, if I just use the first one, I always get an error with the regular grails Date Picker. If I use both, I don't get an error anymore.. However, the calendar saves just the date and not the time? Anyway to fix this? :(

You may want to take a look at this:
Grails Date unmarshalling
and this
Binding a Grails date from params in a controller

I don't think you can register multiple editors for the same type, so I would instead write my own MultiDateEditor subclass of CustomDateEditor (or implement PropertyEditorSupport directly) which tries to bind a date using multiple formats. Then you only need to register MultiDateEditor:
public class CustomDateEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new MultiDateEditor(
["MM/dd/yyyy hh:mm a", "MM/dd/yyyy"]))
}
}

Related

How to retrieve the client (browser) timezone in Vaadin Flow?

I need to determine the browser timezone. I tried to follow this post but does not work (Vaadin 20). Here is my code:
ZoneId myZoneId;
...
UI.getCurrent().getPage().retrieveExtendedClientDetails(extendedClientDetails -> {
myZoneId = ZoneId.of(extendedClientDetails.getTimeZoneId());
});
// here myZoneId has value null.
So I tried to do it myself, initially simply displaying it.
UI.getCurrent().getPage()
.executeJs("return Intl.DateTimeFormat().resolvedOptions().timeZone;")
.then(value -> Notification.show(value.asString()));
It works and I read "Europe/Rome", but its value does not seem something that I can map to a ZoneId in Java.
I could explore a little more the Javascript zone object but I also was unable to find where my code actually went to debug it with chrome debugger (there is no mention in Vaadin doc where the code goes).
I could work on the returned value and try to interpret it but I would like to avoid reinventing the wheel.
Do anybody has any code that works?
retrieveExtendedClientDetails is an asynchronous call, thus the result is available inside the callback and not right after firing the callback (on the place you commented).
Also, UI.getCurrent() could return null if called too soon, f.e. in a constructor.
retrieveExtendedClientDetails makes a client roundtrip the first time it is run (docs).
If already obtained, the callback is called directly. Otherwise, a client-side roundtrip will be carried out.
That means you could try to run it when initialising the UI and later you should have it ready right away after firing.
#SpringComponent
public class MyVaadinServiceInitListener implements VaadinServiceInitListener {
#Override
public void serviceInit(ServiceInitEvent event) {
event.getSource().addUIInitListener(uiEvent -> {
var ui = event.getUI();
ui.getPage().retrieveExtendedClientDetails(detail -> {});
});
}
}
Another solution is to read the zone inside the callback.
Europe/Rome is fine for Java.
You can simply call:
ZoneId.of("Europe/Rome");
Thanks to everybody!
Finally the main problem was not retrieving the time zone from the client but matching it against the list of available time zones returned by ZoneId.getAvailableZoneIds(). The fact is that there were multiple timezone for CEST (Central Europe) as well as any other country. I counted 7 for Brazil that only has 5 time zones!
Honestly I am little ignorant about time zones (world would be quite simpler if we could use only GMT and adjust our schedules accordingly). The solution was to digest the timezone list using the name. I do not know if this is right or wrong, but it is simple and works, despite the fact that if I choose CEST I do not know if it will be Rome, Berlin or whatever else. Does it matter?
Here is the complete code if somebody should one day need a
package net.cbsolution.scc.vaadin.comps;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.ItemLabelGenerator;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.data.provider.ListDataProvider;
import com.vaadin.flow.data.renderer.TemplateRenderer;
import java.time.ZoneId;
import java.time.format.TextStyle;
import java.util.*;
import java.util.stream.Collectors;
public class TimeZoneSelector extends ComboBox<ZoneId> {
private Locale locale = UI.getCurrent().getLocale();
public TimeZoneSelector() {
// Digest the timezone list
final Map<String, ZoneId> cleanMap = new HashMap<>();
ZoneId.getAvailableZoneIds().stream().map(z -> ZoneId.of(z)).forEach(z -> cleanMap.put(print(z), z));
cleanMap.values().stream().collect(Collectors.toList());
setWidth("20em");
setDataProvider(new ListDataProvider<>(cleanMap.values().stream().collect(Collectors.toList())));
setRenderer(TemplateRenderer.<ZoneId>of("<span>[[item.print]]</span>")
.withProperty("print", tz -> print(tz))
);
setItemLabelGenerator((ItemLabelGenerator<ZoneId>) item -> print(item));
}
protected String print(ZoneId zoneId) {
return zoneId.getDisplayName(TextStyle.FULL, UI.getCurrent().getLocale());
}
#Override
protected void onAttach(AttachEvent attachEvent) {
UI.getCurrent().getPage().retrieveExtendedClientDetails(details -> {
TimeZone uiTimeZone = TimeZone.getTimeZone(details.getTimeZoneId());
setValue(ZoneId.of(uiTimeZone.getID()));
});
}
}
try this
//Read the timezone and store in into the session class
if(UI.getCurrent() != null) {
UI.getCurrent().getPage().retrieveExtendedClientDetails(details -> {
// Set the time zone
TimeZone uiTimeZone = TimeZone.getTimeZone(details.getTimeZoneId());
});
};

Basic problems to use Vaadin 7 + Grails 2.3 (Persistence, domain class design, get Hibernate Session)

I'm using Vaadin 7 + Grails 2.3, there is some questions
My domain classes
class Base {
private static final Date NULL_DATE = new Date(0)
Date createdAt = NULL_DATE;
Date updatedAt = NULL_DATE;
def beforeInsert(){
createdAt = new Date();
updatedAt = new Date();
}
def beforeUpdate(){
updatedAt = new Date();
}
static mapping = {
autoTimestamp true
}
}
abstract class Person extends Base{
String name;
String name2;
String phone1;
String phone2;
static constraints = {
name2 nullable:true
phone1 nullable:true
phone2 nullable:true
}
}
class Customer extends Person {
double credit;
}
THE PROBLEMS
PROBLEM 1
In my Vaadin class UI, if I try this
class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
Customer customer = new Customer()
customer.name="RODRIGO"
customer.save()
}
}
Show this error
Servlet.service() for servlet [VaadinServlet 0] in context with path [/AgileWeb] threw exception [com.vaadin.server.ServiceException: groovy.lang.MissingPropertyException: No such property: name for class: agileweb.Customer
Possible solutions: all] with root cause
Message: No such property: name for class: agileweb.Customer
Possible solutions: all
there is no "name" property? The class Customer extends Person that has this property.
PROBLEM 2
If I try this
Customer customer = new Customer()
Customer.setName("RODRIGO")
Customer.save()
Show thos error : Message: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I have seach about this error but I didn't understand to fix it, maybe I'm new with Grails
PROBLEM 3 - ACTUALLY IS A QUESTION
I know that I can use BeanItemContainer, just from List object, that is possible with no problem, but BeanItemContainer is not lazy load, I'd like to use HbnContainer addon (https://vaadin.com/directory#addon/hbncontainer) becase it just need a hibernate session, so How Can I get the "Hibernante session", is there any example and how to do it?
PROBLEM 4 - ACTUALLY IS A QUESTION (AGAIN)
Following this tutorial https://vaadin.com/wiki/-/wiki/Main/Vaadin%20on%20Grails%20-%20Database%20access
It works to save object in the database, but the questions
- Is it really necessary to create a service for each domain class? I have read that it's recomend to put domain logical in the services, I agree with this, but what about simple domain that no need any logical?
so, is there possible to create something like DAO for services? is there any service design to avoid repeted code just to save objects?
I know that are many questions but I think these questions are the same of others, I really want to use Vaadin + Grails to enjoy the better of both, but is not easy to me at the moment!
Before I start answering your question, let me question you domain model. Base class is generally fine, but I want to talk about Person and Customer. You may have good reasons why you picked up inheritance, but please think of composition. Maybe you could have Person that contains a reference to an enum, that states type of the person. You could start here with that: http://en.wikipedia.org/wiki/Composition_over_inheritance
I think you have a typo there. Call save() method on 'customer' not 'Customer', which is a class
When there is a request coming to Grails application, it opens a session and the session is available during that request. There is not this kind of behavior like that in Vaadin. You need to put it into a Service. Yes, you can make generic service to save an object
class GenericService { def save(def domain) { domain.save(failOnError:true) }}
You can get the session factory like this
import org.codehaus.groovy.grails.commons.ApplicationHolder as AH
def ctx = AH.application.mainContext
def sessionFactory = ctx.sessionFactory
or
ApplicationContext applicationContext = grailsApplication.mainContext
ConfigurableLocalSessionFactoryBean factory = applicationContext.getBean('&sessionFactory')
As I wrote, you could create GenericService or a service per domain object. Just to keep in mind that GenericService should only save an object and contain no other logic that would be specific for a domain object.

Wicket and Reusable Link

'm trying to create a reusable link class that extends Link. I have a webpage with about 7 menu items and I'm using inheritance for my application. I want to create a reusable link class to shorten the length of my code..
As of now the link creates and runs fine when I add(new Link....) as an anonymous class inside oninitialize().
The custom link class (which is an inner class of the base page) works fine when I hard code the instance of the new page to go to, and assign it to a "Page" reference, then pass it into setResponsePage();
The problem is, I'm passing trying to be able to pass object through the constructor generically. When I pass it through the constructor, and try to travel to the new page, I get a session has expired.
I've tried using generics for the class, and I've also tried just declaring a Page reference as a parameter value. Am I supposed to use some sort of Model? Or can someone provide an example of how to do this? I want to be able to use this custom link class to add new links for the 7 menu items, which each have there own class...
Code that works:
add(new Link("userPageLink")
{
public void onClick()
{
pageTitle = "User";
Page next = new UserPage();
setResponsePage(next);
}
});
Modified code that gives page expired upon click:
public class CustomLinkToNewPage extends Link
{
private String title;
private Page next;
public CustomLinkToNewPage(String id, String title, Page newPage)
{
super(id);
next = newPage;
this.title = title;
}
#Override
public void onClick()
{
SSAPage.pageTitle = title;
setResponsePage(next);
}
}
This might be due to the fact that in the first version you crate the Page object when the onClick method of the Link object is called and in the second version, the Page object is created on Page-construction (way earlier).
You might get the result if you pass the Pageclass of the responsepage instead on an instance.
Component features setters for these either with
public final <C extends IRequestablePage> void setResponsePage(java.lang.Class<C> cls, PageParameters parameters)
or without parameters.
public final <C extends IRequestablePage> void setResponsePage(java.lang.Class<C> cls)
See Javadoc for more information.
I ended up doing:
public class CustomLinkToNewPage<T extends SSAPage> extends Link
SSAPage is my base page that extends WebPage... So any object passed in to this class's constructor must extend SSAPage as well.
public CustomLinkToNewPage(String id, Class<T> name)
Then I passed in the .class reference to the object, and created a new instance of the object using reflection.. then set that instance to Page, and passed it to setResponsePage in my onClick. Worked nicely, as I couldn't figure out how to do Nicktar's way. So this an alternative in case anyone else runs into this issue.

Overriding Joda DateTime toString in Groovy

So I'm using the JodaTime plugin in a grails project I'm implementing and I really don't like that it spits out the ISO8601 date format when I do a toString. I've been constantly putting toString and passing in the default.date.format from the messages file, but that's cumbersome. The majority of cases I just want it to do that automatically. So naturally it makes sense to take advantage of Groovy's fabulous metaprogramming to override toString on the DateTime class. But alas it doesn't work. Hence this discussion:
http://jira.codehaus.org/browse/GROOVY-4210
So according to said discussion, if our class implements an interface to implement the toString method we need to override the interface's metaclass. Looking at the joda code base, DateTime implements the ReadableDateTime interface which in turn inherits from ReadableInstant which is where the method signature is defined. The actual implementation is done 4 classes up in the class hierarchy for DateTime (DateTime inherits from BaseDateTime inherits from AbstractDateTime inherits from AbstractInstant which implements toString without parameters). With me so far?
So in theory this means I should override either the ReadableDateTime interface which doesn't actually have the toString signature or the ReadableInstant one which does. The following code to override toString on ReadableDateTime does nothing.
ReadableDateTime.metaClass.toString = { ->
delegate.toString(messageSource.getMessage(
'default.date.format', null, LCH.getLocale()))
}
So then trying with ReadableInstant:
ReadableInstant.metaClass.toString = { ->
delegate.toString(messageSource.getMessage(
'default.date.format', null, LCH.getLocale()))
}
also does not have the desired result for the DateTime.toString method. However, there are some interesting affects here. Take a look at the following code:
def aiToString = AbstractInstant.metaClass.getMetaMethod("toString", [] as Class[])
def adtToString = AbstractDateTime.metaClass.getMetaMethod("toString", [] as Class[])
def bdtToString = BaseDateTime.metaClass.getMetaMethod("toString", [] as Class[])
def dtToString = DateTime.metaClass.getMetaMethod("toString", [] as Class[])
def date = new DateTime()
println "ai: ${aiToString.invoke(date)} "
println "adt: ${adtToString.invoke(date)} "
println "bdt: ${bdtToString.invoke(date)} "
println "dt: ${dtToString.invoke(date)} "
The first 3 methods show my date formatted just how I'd like it. The last one is still showing the ISO8601 formatted date. I thought maybe the JodaTime plugin for grails might be overriding the toString and they do add a few methods to these interfaces but nothing to do with toString. At this point, I'm at a loss. Anyone have ideas?
Thanks
You cann't override DateTime#toString(), becouse DateTime class is final
public final class DateTime
But if you want another date format, you can use toString(org.joda.time.format.DateTimeFormatter)
for example
def date = new DateTime();
date.toString(ISODateTimeFormat.basicDate()); // format yyyyMMdd

Grails i18n from property files backed up by a DB?

i am trying to get a situation where i can use i18n property files which are backed up with a database?
So for some standard stuff i would like to use the property files, but some fields must be editable by the end-user so i was planning to use i18n in the database for that. So a real combination would be great. If the i18n code cannot be found in the property files then do a lookup in the DB.
Any idea how i can tackle this? I have seen the post Grails i18n From Database but Default Back To File
But there is no real answer to the problem, any other suggestions on how to tackle this?
Put a new domain class into your project:
class Message {
String code
Locale locale
String text
}
Add the following lines to your resources.groovy:
// Place your Spring DSL code here
beans = {
messageSource(DatabaseMessageSource) {
messageBundleMessageSource = ref("messageBundleMessageSource")
}
messageBundleMessageSource(org.codehaus.groovy.grails.context.support.PluginAwareResourceBundleMessageSource) {
basenames = "WEB-INF/grails-app/i18n/messages"
}
}
And add the following class to your src/groovy folder:
class DatabaseMessageSource extends AbstractMessageSource {
def messageBundleMessageSource
protected MessageFormat resolveCode(String code, Locale locale) {
Message msg = messageBundleMessageSource.resolveCode(code, locale)
def format
if(msg) {
format = new MessageFormat(msg.text, msg.locale)
}
else {
format = Message.findByCodeAndLocale(code, locale)
}
return format;
}
}
Now grails will try to resolve the message from the message bundle. If it is not available, it will look it up from database. You could add some error-handling, but this version works, if all messages are available at least in one place.
See http://graemerocher.blogspot.com/2010/04/reading-i18n-messages-from-database.html for some more details.
Some details on the changes done in resources.groovy:
In this file you can define injectable groovy classes, which can be included by just defining a variable having the same name as defined in the resources.groovy. E.g. in this file, there are messageSource and messageBundleMessageSource, which you can be include in any controller or service files. If this variable is defined, an instance of the class in the brackets is created.
In this case, we overwrite the general messageSource to use our custom implementation DatabaseMessageSource. So the I18n function message will now use our custom implementation.
Since our custom implementation requires to check the message.properties-files we keep the original message source in the second bean. By defining this instance in our custom implementation, we can still use the old implementation (and therefore looking up messages the usual way).
I'm not sure I know what you mean by
i18n property files which are backed up with a database
But if you simply mean that you want the message keys to be resolved using a database table (instead of a .properties file), then you can do this by writing your own implementation of the MessageSource interface
class DBMessageSource implements MessageSource {
String getMessage(MessageSourceResolvable resolvable, Locale locale) {
// IMPLEMENT ME
}
String getMessage(String code, Object[] args, Locale locale) {
// IMPLEMENT ME
}
String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
// IMPLEMENT ME
}
}
Then simply replace the default implementation of the messageSource bean with your implementation by adding the following to resources.groovy
messageSource(DBMessageSource)
In followup of the answer of #crudolf i implemented the following method to achieve my goal.
class DatabaseMessageSource extends AbstractMessageSource {
// the message bundle resource that holds all of the messages
def messageBundleMessageSource
// the default locale used when there is no correct results found
// if a visitor (x) comes along with an unknown locale in the DB
// then this locale will be used as fallback!
Locale fallbackLocale = new Locale("nl", "NL")
protected MessageFormat resolveCode(String code, Locale locale) {
// first try to find the message in the messagebundles
MessageFormat messageFormat = messageBundleMessageSource.resolveCode(code, locale)
if(!messageFormat) {
// no message found so lets find one in the database
def message = Message.findByCodeAndLocale(code, locale) ?: Message.findByCodeAndLocale(code, fallbackLocale)
if (message) {
// found one create a message format!
messageFormat = new MessageFormat(message.text, message.locale)
} else {
// not found! create a standard message format
messageFormat = new MessageFormat(code, locale)
}
}
return messageFormat
}
}
Take a look at https://github.com/goeh/grails-i18n-db and https://github.com/halfbaked/grails-localizations. Both offer also a gui to manage localizations.

Resources