How to bind from Vaadin RadioButtonGroup country to Country country DAO object? Country is enum and DateField(vaadin) to LocalDate(DAO)
public class Person {
#Id
String id;
LocalDate dateBorn;
Country country;
......
public class PersonFormUI extends GridLayout {
RadioButtonGroup<Country> country;
DateField dateBorn;
........
public enum Country {
EN, DE, IT }
with this binding all fields are binded and runs well but how to bind enums and dates withConverter?
binder.bindInstanceFields(this);
binder.setBean(personDAO);
If I may add a couple of suggestions before going straight to code:
If you want a nicely aligned form you can use FormLayout
I'd suggest using a ComboBox instead of RadioButtonGroup to display the countries, as it uses less space and also allows you to quickly find what you want by typing in it. But if you really want the radio, replace the combo line with RadioButtonGroup<Country> countries = new RadioButtonGroup<>("Country", DataProvider.ofItems(Country.values()));
You can find below a sample of binding based on the suggestions above and Vaadin 8.3.1. I don't see the need for any conversions, since the framework will take care of it for you.
The only extra thing I did, was to add an ItemCaptionGenerator for the combo to display the full country name instead of the default, which uses the Enum name (same thing can be used with RadioButtonGroup).
Code:
public class PersonForm extends FormLayout {
public PersonForm() {
// form components
DateField birthDate = new DateField("Birth date");
ComboBox<Country> countries = new ComboBox<>("Country", Arrays.asList(Country.values()));
// use full country name instead of ugly Enum name
countries.setItemCaptionGenerator(Country::getFullName);
// do not allow the user to select "nothing"
countries.setEmptySelectionAllowed(false);
// binder setup
Binder<Person> userBinder = new Binder<>();
// birth date binding
userBinder.forField(birthDate)
.asRequired("Please provide a birth date")
.bind(Person::getDateBorn, Person::setDateBorn);
// country binding
userBinder.forField(countries)
.asRequired("Please select the country of residence")
.bind(Person::getCountry, Person::setCountry);
// bind to bean with some existing value (eg, loaded from DB for editing)
userBinder.setBean(new Person(LocalDate.now(), Country.RO));
// simulate a save action
Button saveButton = new Button("Save", event -> Notification.show("Saved new user info: " + userBinder.getBean()));
// add fields to the UI
addComponents(birthDate, countries, saveButton);
}
// beans
public class Person {
private LocalDate dateBorn;
private Country country;
public Person(LocalDate dateBorn, Country country) {
this.dateBorn = dateBorn;
this.country = country;
}
public LocalDate getDateBorn() {
return dateBorn;
}
public void setDateBorn(LocalDate dateBorn) {
this.dateBorn = dateBorn;
}
public Country getCountry() {
return country;
}
public void setCountry(Country country) {
this.country = country;
}
#Override
public String toString() {
return "Person{" +
"dateBorn=" + dateBorn +
", country=" + country +
'}';
}
}
public enum Country {
RO("Romania"), DE("Deutschland"), IT("Italy");
private String fullName;
Country(String fullName) {
this.fullName = fullName;
}
public String getFullName() {
return fullName;
}
}
}
Result:
You can go even further and disable the button based on the binder status (maybe you want to use the same form to add new people where there are no initial values):
// disable saving until all required data is available
userBinder.addStatusChangeListener(event -> saveButton.setEnabled(userBinder.isValid()));`
Result:
Related
I am able to see the primitive data type used in java class in the event browser's table of Java Mission Control. But it is not showing the composite data types.
My code is:
package com.foo.bar;
public class AB {
#Name("com.foo.bar.Author")
#Label("Author Event")
public static class Author extends Event {
String authorName;
int age;
String place;
Author(String name, int age, String place)
{
this.authorName = name;
this.age = age;
this.place = place;
}
}
#Name("com.foo.bar.Book")
#Label("Book Event")
public static class Book extends Event
{
String name;
int price;
// author details
Author auther;
Book(String n, int p, Author auther)
{
this.name = n;
this.price = p;
this.auther = auther;
}
}
public static void main(String[] args) throws InterruptedException {
Author author = new Author("John", 42, "USA");
Book b = new Book("Java for Begginer", 800, author);
b.begin();
author.begin();
author.commit();
b.commit();
}
}
And I am getting something like this in JMC:
How can I get the Author details in Book event? Is there a way in JMC to get the composite data in event browser's column?
Thanks
You don't. You relate the events to each other. I.e. the author event and the book event should probably both have an authorId in them.
See, for example, how the gc id is used to relate garbage collection related events to each other.
Jave events can't store composite data in fields, but you can link events together as suggested by Hirt.
#MetadataDefinition
#Name("com.example.BookAuthor")
#Label("Book Author")
#Relational
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD})
public #interface BookAuthor {
}
#Name("com.example.Author")
#Label("Author")
public class Author extends Event {
#Label("Author");
String authorName;
#Label("Age");
int age;
#Label("Place");
String place;
#Label("Author ID");
#BookAuthor
long id;
}
#Name("com.example.Book")
#Label("Book")
public class Book extends Event {
#Label("Name")
String name;
#Label("Price")
int price;
#Label("Author ID")
#BookAuthor
long authorId;
}
JDK Mission Control GUI, as of today, won't group them together, but that is how a relation is expressed.
This question already has answers here:
JavaFX: ComboBox using Object property
(2 answers)
Closed 1 year ago.
I have a list of Country objects that have the instance variable String countryName;.
I can't figure out how to populate the ComboBox with a list of Country objects.
I tried doing it using a workaround by creating another list called
ObservableList<String> listCountriesString = FXCollections.observableArrayList();
and looping over it and adding every instance variable countryName to a new list:
private ObservableList<Country> listCountries = FXCollections.observableArrayList();
for (Country country : listCountries) {
listCountriesString.add(country.getCountryName());
}
How can I use a ComboBox with my Country object, and displaying the country names only?
#FXML
ComboBox<Country> comboBoxCountry;
public class Country {
private int countryId;
private String countryName;
private String createDate;
private String lastUpdate;
public Country(int countryId, String countryName, String createDate, String lastUpdate) {
this.countryId = countryId;
this.countryName = countryName;
this.createDate = createDate;
this.lastUpdate = lastUpdate;
}
... getters and setters
It's pretty straightforward and it is part of the documentation of course.
First you would need to create a cellFactory that takes care of setting the text of your ComboBox items.
Callback<ListView<Country>, ListCell<Country>> cellFactory = lv -> new ListCell<Country>() {
#Override
protected void updateItem(Country item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? "" : item.getCountryName());
}
};
And then use it like this:
comboBoxCountry.setButtonCell(cellFactory.call(null));
comboBoxCountry.setCellFactory(cellFactory);
Then you could add your Countries like this:
comboBoxCountry.getItems().add(new Country("Germany"...));
Good luck!
I have 3 entities : Country, State, City
I have written their POJOs as :
class Country{
String countryName;
List<State> states;
}
class State{
String stateName;
List<City> cities;
}
class City{
String cityName;
}
My database table is as below :
Goegraphy
---------
countryName | stateName | cityName
Now, for fetching data from database, I made another POJO :
class Geography{
String countryName;
String stateName;
String cityName;
}
I have a List of Geography objects.
But my requirement is to convert this list into the earlier Country-State-City model now. Can someone please help how this can be achieved.
What you are looking for is the One to Many association of relation databases. All JPA implementations can do it nicely for you, without the need of implementing your Geography Pojo.
But if you are stuck to do it manually, here is a not very optimized way of doing it with java 8 streams
class Country {
public Country(String countryName) {
this.countryName = countryName;
this.states = new ArrayList<>();
}
String countryName;
List<State> states;
}
class State {
public State(String stateName) {
this.stateName = stateName;
this.cities = new ArrayList<>();
}
String stateName;
List<City> cities;
}
class City {
public City(String cityName) {
this.cityName = cityName;
}
String cityName;
}
class Geography {
String countryName;
String stateName;
String cityName;
}
List<Country> buildFromGeographies(List<Geography> geos) {
List<Country> result = new ArrayList<>();
for (Geography geo : geos) {
Optional<Country> country1 = result.stream().filter(country -> country.countryName.equals(geo.countryName)).findFirst();
Country country = country1.orElseGet(() -> {
Country newOne = new Country(geo.countryName);
result.add(newOne);
return newOne;
});
Optional<State> state1 = country.states.stream().filter(state -> state.stateName.equals(geo.stateName)).findFirst();
State state = state1.orElseGet(() -> {
State newOne = new State(geo.stateName);
country.states.add(newOne);
return newOne;
});
// taking for granted there is no duplicates in your data set
state.cities.add(new City(geo.cityName));
}
return result;
}
If I understand it correctly you have List from table and you want it to map on you POJO classes. But the problem is that you have duplicated values in table if I understand it correctly. I think you have this data in table
1. USA, Florida, Miami
2. USA, Florida, Sarasota
The Problem will be with dupplication in table. I think you should split each column in separate table and join via foreing key and it will be better. Than you can create select and join to another table and result add to your list. Now you need to create loop.
I have an enum:
public enum Roles {
USER,ADMIN;
}
And a CheckBoxGroup on my form:
private CheckBoxGroup<String> rolesCheckbox = new CheckBoxGroup<>("User roles");
Instance of a User class binded to this form has a field:
private List<Roles> roles;
And what I want to do now is just check correct checkboxes when opening form for a specific User instance. I probably has to do something insinde binder.forField but I don't really know how:
binder.forField(rolesCheckbox).bind(...
The example from the Vaadin docs uses String for brevity, but you can easily use objects. Suppose you have a Role enum and a User like below:
// possible roles
public enum Role {
ADMIN, USER, GUEST
}
// bean for binding
public class User {
private Set<Role> roles;
private String name;
public User(String name, Set<Role> roles) {
this.roles = roles;
this.name = name;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "User{" +
"roles=" + roles +
", name='" + name + '\'' +
'}';
}
}
... you could create your form similar to:
public class CheckboxEnumBinding extends VerticalLayout {
public CheckboxEnumBinding() {
// form components
TextField nameTextField = new TextField("Name");
CheckBoxGroup<Role> roleCheckBoxGroup = new CheckBoxGroup<>("Roles", DataProvider.ofItems(Role.values()));
// binder setup
Binder<User> userBinder = new Binder<>();
// name binding
userBinder.forField(nameTextField)
.asRequired("Please provide a user name")
.bind(User::getName, User::setName);
// role binding
userBinder.forField(roleCheckBoxGroup)
.asRequired("Please select at least one role")
.bind(User::getRoles, User::setRoles);
// bind to bean
userBinder.setBean(new User("Morfic", EnumSet.of(Role.ADMIN, Role.GUEST)));
// simulate a save action
Button saveButton = new Button("Save", event -> Notification.show("Saved new user info: " + userBinder.getBean()));
// add fields to the UI
addComponents(nameTextField, roleCheckBoxGroup, saveButton);
}
}
Result:
Bonus: By default Vaadin will use the enum name to display the values. If you want something different, like capitalizing only the first letter you can use an ItemCaptionGenerator:
// spring boot app, don't reinvent the wheel
import org.springframework.util.StringUtils;
...
roleCheckBoxGroup.setItemCaptionGenerator(role -> StringUtils.capitalize(role.name().toLowerCase()));
I would like to use random 8 digit number id for model in play! 1.2.5 instead of auto_increment value and also without creating a random value, assigning it to id by myself. I do not know it is possible or not. I really googled about it but I could not find anything.
for the answer below.
Here is what i need in my project. Suppose that i have a user object which has 2 attr. name and surname. With the defult creation of this object, JPA assing a auto_inc value for the id of this object.
#Entity
public class User extends Model{
public String name;
public String surname;
}
And here i have createUser method in my controller.
public static void createUser(String name, String surname){
User user = new User();
user.name = name;
user.surname = surname;
/* it seems to me that the answer below can be a solution for what i want like that
* user.id = javaGenereted8digitNumId();
* But I dont want this. I want it is handled in model class
* and I guess it can be with JPA GenericModel. am I right?
*/
user.save();
}
use:
int ID = (int) (Math.Random()*(99999999-a)+a); //a being the smallest value for the ID
and if you want to include the 0's on the left, use:
import java.text.DecimalFormat;
and in your code:
DecimalFormat fmt = new DecimalFormat("00000000");
int ID = Integer.parseInt(fmt.format((int) (Math.Random()*(99999999-a)+a)));
EDIT:
This is an update to your update of the question.
If you want the model class of User to create its own unique 8digit ID everytime, i suggest you make a static variable that saves the last currently used ID for all User objects created. This way on creation of a new user, it will simply create a new user with the next available ID, this will of course be limited to 99999999 ID's.
If you want to go further then this, you will need to create a very large static String containing all the used ID's seperated by spaces, and everytime you want to add a user, it will check for the ID availability by using .contains("ID") method
Here's an example of what your User class should look like i believe:
public class User extends Model
{
public String name, surname;
public int ID;
public static int lastID;
public static String usedIDs;
public User(String name, String surname) //regular User objects
{
this.name = name;
this.surname = surname;
ID = ++lastID;
usedID's += ID + " ";
}
public User(String name, String surname, int n) //first User object created
{
this.name = name;
this.surname = surname;
ID = 1;
lastID = 1;
usedID's = ID + " ";
}
//rest of your methods