I want to ask about the UI of the vaadin, which is Table.
If I used this component, then I have to create a field using this command:
userTable.addContainerProperty("Status", String.class, "Active");
If I want to create link into this field, then I have to do like this:
userTable.addContainerProperty("Action", Link.class, new Link("Remove", new ExternalResource("#")));
My question is, the example above, only display single link in one field which is REMOVE Link. I want to create two links in one field of that table. For example link for EDIT and DELETE below the "Action" field, how can I do that?
Use a generated column to add the components to each row. Create an Horizontal Layout and two Buttons as the content.
class ValueColumnGenerator implements Table.ColumnGenerator {
String format; /* Format string for the Double values. */
/**
* Creates double value column formatter with the given
* format string.
*/
public ValueColumnGenerator(String format) {
this.format = format;
}
/**
* Generates the cell containing the Double value.
* The column is irrelevant in this use case.
*/
public Component generateCell(Table source, Object itemId,
Object columnId) {
// Get the object stored in the cell as a property
Property prop =
source.getItem(itemId).getItemProperty(columnId);
if (prop.getType().equals(Double.class)) {
HorizontalLayout hbox = new HorizontalLayout()
hbox.addComponent(new Button("Status"))
hbox.addComponent(new Button("Remove"))
return hbox;
}
return null;
}
}
See Section 5.14.5 of the Book of Vaadin for more info:
https://vaadin.com/book/-/page/components.table.html
You can add this buttons to HorizontalLayout or any other container component. Then add this layout to the container property in your table.
Related
Later Edit: I noticed that by returning one of the options in ValueProvider's apply method leads to having the check mark present, but appears to show the previous select too. I.e. if the current and previous values are distinct, two check marks are shown.
I am having troubles with ComboBox binding. I cannot get the com.vaadin.flow.data.binder.Binder properly select an option inside the combobox - i.e. tick the check mark in the dropdown.
My binder is a "generic", i.e. I am using it along with a Map, and I provide dynamic getters/setters for various map keys. So, consider Binder<Map>, while one of the properites inside the Map should be holding a Person's id.
ComboBox<Person> combobox = new ComboBox<>("Person");
List<Person> options = fetchPersons();
combobox.setItems(options);
combobox.setItemLabelGenerator(new ItemLabelGenerator<Person>() {
#Override
public String apply(final Person p) {
return p.getName();
}
});
binder.bind(combobox, new ValueProvider<Map, Person>() {
#Override
public Person apply(final Map p) {
return new Person((Long)p.get("id"), (String)p.get("name"));
}
}, new Setter<Map, Person>() {
#Override
public void accept(final Map bean, final Person p) {
bean.put("name", p.getName());
}
});
Wondering what could I possibly do wrong...
Later edit: Adding a screenshot for the Status ComboBox which has a String for caption and Integer for value.
Your problem is that you are creating a new instance in your binding, which is not working. You probably have some other bean, (I say here Bean) where Person is a property. So you want to use Binder of type Bean, to bind ComboBox to the property, which is a Person. And then populate your form with the Bean by using e.g. binder.readBean(bean). Btw. using Java 8 syntax makes your code much less verbose.
Bean bean = fetchBean();
Binder<Bean> binder = new Binder();
ComboBox<Person> combobox = new ComboBox<>("Person");
List<Person> options = fetchPersons();
combobox.setItems(options);
combobox.setItemLabelGenerator(Person::getName);
binder.forField(combobox).bind(Bean::getPerson, Bean::setPerson);
binder.readBean(bean);
In my application i have a class like:
public class Team {
private Country teamId;
private Set<Player> playerSet;
private Set<Player> substitutes;
private Set<Coach> coachSet;
}
When i instantiate a grid like:
Grid<Team> grid = new Grid<>(Team.class);
and set allTeam() from database it shows object for playerSet and coachSet.
My question is i just want to show players name and coach name concate by ,or \n.
Any idea how can i do that?As a beginner it is complicated for me
I see three options.
The first option is the one you already found yourself: concatenate their names in a single String. This can be done like this:
grid.addColumn(team -> {
Set<String> coachNames = new HashSet<>();
for (Coach coach : team.getCoaches()){
coachNames.add(coach.getName());
}
return String.join(", ", coachNames);
});
The second one would be to make use of the Grid item Detail - you could show a coaches grid in the item details. Since you want to display both coaches and players, this option is probably not the best but I wanted to mention the possibility. (Placing two grids inside the item details is possible, but quite strange. Not optimal user experience.)
grid.setItemDetailsRenderer(new ComponentRenderer<>(team -> {
Grid<Coach> coachGrid = new Grid<>(Coach.class);
coachGrid.setItems(team.getCoaches());
return coachGrid;
}));
A third option would be to have the team grid on one side of the view, and on the other you show some relevant stuff of the selected item of the team grid. You can have a separate Grid for the coaches, one for the players, one for the substitutes. You could implement this team detail layout also as a separate view if you wish. If your Team object will get more complicated with more sets, collections and other relative properties, the more will this option become appealing, as this is quite scalable/expandable.
grid.addSelectionListener(event -> {
if(event.getFirstSelectedItem().isPresent()){
buildTeamDetails(event.getFirstSelectedItem().get())
}
})
private void buildTeamDetails(Team team){
// build your team detail layouts here
}
You can configure which columns are shown in the grid by using grid.removeAllColumns() and then adding all columns you want to have in the grid with grid.addColumn(). Within addColumn() you can create a renderer that defines how the fields (coachName and playerSet) are displayed in the grid.
Let's have a class Team like
public class Team {
private String coachName;
private Set<Player> playerSet;
private Set<Object> objects;
//getters and setters
}
and a class Player like
public class Player {
private String firstName;
private String lastName;
// getters and setters
}
Now you want to only have coach and player names in the grid. So (in my example) for coachName we can just use the field's getter and we can create a comma separated String for the playerSet with java streams easily.
Configure the grid like:
grid.setItems(team);
grid.removeAllColumns();
grid.addColumn(new TextRenderer<>((ItemLabelGenerator<Team>) Team::getCoachName))
.setHeader("Coach");
grid.addColumn(new TextRenderer<>((ItemLabelGenerator<Team>) team1 -> team1.getPlayerSet().stream()
.map(player1 -> player1.getFirstName() + " " + player1.getLastName())
.collect(Collectors.joining(", "))))
.setHeader("Players")
.setFlexGrow(1);
Then the result looks like:
I am new To Vaadin, I have created a Table and i can able to populate the data in that through the BeanItemCOntainer, bellow is the code for this.
public Component getMainContent(ViewChangeListener.ViewChangeEvent event) {
List<ExecutionPlanVO> executionPlanVOs = executionPlanDelegate.getExecutionPlans(getSearchVO());
Table table = new Table();
BeanItemContainer<ExecutionPlanVO> container = new BeanItemContainer<ExecutionPlanVO>(ExecutionPlanVO.class, executionPlanVOs);
container.addBean(new ExecutionPlanVO());
table.setContainerDataSource(container);
table.setVisibleColumns(
new Object[] {"billOfladingNo" , "containerNo" , "houseBill" , "carrier" , "customer" , "origin" , "pol" , "transshipment" ,
"pod" , "destination" , "start" , "completion" , "status"});
table.setColumnHeaders(
new String[] {"Bill Of Lading" , "Container No." , "House Bill" , "Carrier" , "Customer" , "Origin" , "POL" , "Transshipment" ,
"POD" , "Destination" , "Start (LT)" , "Completion (LT)" , "Status"});
table.setStyleName("ep-list-table");
return table;
}
I Have two questions here,
1. I would want to change billOfladingNo column as a link, which will permorm some action when i click ?
2. i wanted to add one more column with couple of link Icons?
can you help me how can i add the columns ?
Thanks in advance
Kiran.
You can Create a class to implement ColumnGenerator to return the link and the icon. I have done a sample below
class Linker implements ColumnGenerator{
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Object generateCell(Table source, Object itemId, Object columnId) {
// TODO Auto-generated method stub
Item item = source.getItem(itemId);
Link link = new Link();
String linkCaption = item.getItemProperty("billOfladingNo").toString();
link.setCaption(linkCaption);
link.setResource(new ExternalResource("http:/www.domain.com/"+linkCaption));
return link;
}
}
class LinkIcons implements ColumnGenerator{
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Object generateCell(Table source, Object itemId, Object columnId) {
// TODO Auto-generated method stub
Label icons = new Label();
icons.setIcon(FontAwesome.LINK);
return icons;
}
}
table.addGeneratedColumn("billOfLading", new Linker());
table.addGeneratedColumn("link", LinkIcons);
be sure to make your billoflading from the bean invisible and add the generated column ids to the table visible ids.
Depends what vaadin version Your using if 7.X.X then:
You can set on the container what type is the chosen column:
container.addContainerProperty("billOfladingNo", new Link, null);
1st parameter is the property of Your column
2nd is the type You want to have
3rd is the default value
that will make Your column type: com.vaadin.ui.Link
to make a new Link with possible navigation:
Link link = new Link("CAPTION", new ExternalResource("URL"));
For the second question u need to set the link caption to accept HTML and then set the FontAwesome icon html:
link.setCaptionAsHtml(true);
link.setCaption(FontAwesome.ANCHOR.getHtml());
Is there a way to implent TableFactory interface with specific fields related to propertyId ?
I only get one type of field since i'm using a generic class for all my tables, and the i'm missing CheckBox boolean value (groovy code):
class DefaultTableFieldFactory implements TableFieldFactory {
#Override
public Field<?> createField(Container container, Object itemId, Object propertyId, Component component) {
TextField t = new TextField()
switch(propertyId) {
case "firstname": t.setNullRepresentation("");
case "lastname": t.setNullRepresentation("");
case "mobile": t.setNullRepresentation("");
case "tel": t.setNullRepresentation("");
case "email": t.setNullRepresentation("");
default: break;
}
t.setWidth("95px")
return t
}
}
So i need to use this class above which implments DefaultTableFieldfactory in order to have the null representation as "" (instead of "null" ) in my whole application.
The goal is to provide for my custom components (more than 30) this null representation in a single place, I want to use this class as my default factory for every table, and connect it like i've done before:
def contacts = (Grails.get(FundService)).getAllContacts(fundId)
def cContainer = new BeanItemContainer<Contact>(Contact.class,contacts)
def t = new Table()
t.containerDataSource = cContainer
t.setTableFieldFactory(new DefaultTableFieldFactory())
Vaadin provides a DefaultTableFieldFactory which does map
Date to a DateField
Boolean to a CheckBox
other to TextField
The DefaultTableFieldFactory is already set on the table. So in your case, if you just want to have CheckBoxes for your boolean fields, I wouldn't implement an own TableFieldFactory. Here's an example:
Table table = new Table();
table.addContainerProperty("text", String.class, "");
table.addContainerProperty("boolean", Boolean.class, false);
table.setEditable(true);
Object itemId = table.addItem();
table.getItem(itemId).getItemProperty("text").setValue("has accepted");
table.getItem(itemId).getItemProperty("boolean").setValue(true);
If you really need to have your own TableFieldFactory then Vaadin recommends:
You could just implement the TableFieldFactory interface, but we
recommend that you extend the DefaultFieldFactory according to your
needs. In the default implementation, the mappings are defined in the
createFieldByPropertyType() method (you might want to look at the
source code) both for tables and forms.
In your code given in the question you always return a TextField. For your missing CheckBoxes you need to return in the specific case a CheckBox.
Don't forget to setEditable(true) when using FieldFactories.
More information here under 5.16.3. Editing the Values in a Table.
I got a simple POJO class that i wish to display / update in a form
Using the BeanItem class and the binding of component data, i was able to quickly display the first attributes of may data class. However i've hit a wall for tow related attributes :
my class posses a set of available status, as a list of object 'AppStatus'. it also possess a current status, that is one of the status in the 'available' list.
I would like to display the list in the form as a combobox, with the current status selected.
I'we managed to associate the 'available' attribute with a combobox, but i can't seem to be able to fill this combobox when setting the data source (method setItemDataSource). How do i get the avalaible status list and the current status from my Item ?
I could always use a workaround and add a parameter to the method to get the source objet in addition to the BeanItem, but i would prefer to avoid this if the Item properties can give me my attribute.
Regards
Edit : shortened exemple, with code from Eric R.
class Status {
String id;
Sting label
+ setter /getter
}
class App {
String AppId;
String AppLabel
ArrayList<Status> availablestatus;
Status currentStatus
+setter/getter
}
in the form extension, in the createField of the fieldfactory i added the following lines
if ("status".equals(propertyId)) {
// create the combobox
ComboBox status = new ComboBox(
texts.getString("application.label.status"));
status.setItemCaptionMode(AbstractSelect.ITEM_CAPTION_MODE_PROPERTY);
status.setItemCaptionPropertyId("label");
status.setImmediate(true);
status.setNullSelectionAllowed(false);
IndexedContainer container = new IndexedContainer(
(Collection<ApplicationStatus>) item.getItemProperty(
"availableStatus").getValue());
status.setContainerDataSource(container);
status.setPropertyDataSource(item.getItemProperty("currentStatus"));
return status;
} else...
this didn't work, i do get a combobox, with the correct number of lines, but all empties.
i tried to use a beanContainer instead of a IndexedContainer
BeanContainer<String, ApplicationStatus> container =
new BeanContainer<String, ApplicationStatus>(ApplicationStatus.class);
container.addAll((Collection<ApplicationStatus>) item
.getItemProperty("availableStatus").
container.setBeanIdProperty("id");
the result is slightly better, since i do have the available values in the combobox.
only the currentValue is not selected...
I also tried to use a nestedbean property to get the id of the currentstatus, but the result is still not valid... i get a combobox, with the correct value selected, but i can not see others values anymore, since the combobox is readonly ?(even with setReadOnly(false);)
I suggest my way to resolve this. I don't think this is the nicest way, but it's works.
The beanItem class contains all you need.
I did the following in a simple project and it's work verry well :
ComboBox status = new ComboBox("ComboBox");
status.setImmediate(true);
status.setNullSelectionAllowed(false);
for(Status st : (Collection<Status>)item.getItemProperty("availableStatus").getValue()) {
status.addItem(st);
status.setItemCaption(st, st.getLabel());
}
status.setPropertyDataSource(item.getItemProperty("currentStatus"));
Hope it's works.
Regards Éric
From the vaadin demo site you can get this sample that show how to fill a combobox with countries. You could do the same i would guess (not sure I understand your problem 100%):
myForm.setFormFieldFactory(new MyFormFieldFactory ());
private class MyFormFieldFactory extends DefaultFieldFactory {
final ComboBox countries = new ComboBox("Country");
public MyFormFieldFactory () {
countries.setWidth(COMMON_FIELD_WIDTH);
countries.setContainerDataSource(ExampleUtil.getISO3166Container());
countries
.setItemCaptionPropertyId(ExampleUtil.iso3166_PROPERTY_NAME);
countries.setItemIconPropertyId(ExampleUtil.iso3166_PROPERTY_FLAG);
countries.setFilteringMode(ComboBox.FILTERINGMODE_STARTSWITH);
}
#Override
public Field createField(Item item, Object propertyId,
Component uiContext) {
Field f = (Field)item;
if ("countryCode".equals(propertyId)) {
// filtering ComboBox w/ country names
return countries;
}
return f;
}
}