diff options
author | Ilia Motornyi <elmot@vaadin.com> | 2015-12-03 14:59:05 +0000 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2015-12-03 14:59:12 +0000 |
commit | 2af72ba9636bec70046394c41744f89ce4572e35 (patch) | |
tree | ccb3dc2d2239585f8c3f79eb5f131ff61ca9ce86 /documentation/components/components-table.asciidoc | |
parent | 8aa5fabe89f2967e966a64842a608eceaf80d08f (diff) | |
download | vaadin-framework-2af72ba9636bec70046394c41744f89ce4572e35.tar.gz vaadin-framework-2af72ba9636bec70046394c41744f89ce4572e35.zip |
Revert "Merge branch 'documentation'"7.6.0.beta2
This reverts commit f6874bde3d945c8b2d1b5c17ab50e2d0f1f8ff00.
Change-Id: I67ee1c30ba3e3bcc3c43a1dd2e73a822791514bf
Diffstat (limited to 'documentation/components/components-table.asciidoc')
-rw-r--r-- | documentation/components/components-table.asciidoc | 1164 |
1 files changed, 0 insertions, 1164 deletions
diff --git a/documentation/components/components-table.asciidoc b/documentation/components/components-table.asciidoc deleted file mode 100644 index 7ef31e29fb..0000000000 --- a/documentation/components/components-table.asciidoc +++ /dev/null @@ -1,1164 +0,0 @@ ---- -title: Table -order: 21 -layout: page ---- - -[[components.table]] -= [classname]#Table# - -((("[classname]#Table#", id="term.components.table", range="startofrange"))) - - -The [classname]#Table# component is intended for presenting tabular data -organized in rows and columns. The [classname]#Table# is one of the most -versatile components in Vaadin. Table cells can include text or arbitrary UI -components. You can easily implement editing of the table data, for example -clicking on a cell could change it to a text field for editing. - -The data contained in a [classname]#Table# is managed using the Data Model of -Vaadin (see -<<dummy/../../../framework/datamodel/datamodel-overview.asciidoc#datamodel.overview,"Binding -Components to Data">>), through the [classname]#Container# interface of the -[classname]#Table#. This makes it possible to bind a table directly to a data -source, such as a database query. Only the visible part of the table is loaded -into the browser and moving the visible window with the scrollbar loads content -from the server. While the data is being loaded, a tooltip will be displayed -that shows the current range and total number of items in the table. The rows of -the table are __items__ in the container and the columns are __properties__. -Each table row (item) is identified with an __item identifier__ (IID), and each -column (property) with a __property identifier__ (PID). - -When creating a table, you first need to define columns with -[methodname]#addContainerProperty()#. This method comes in two flavors. The -simpler one takes the property ID of the column and uses it also as the caption -of the column. The more complex one allows differing PID and header for the -column. This may make, for example, internationalization of table headers -easier, because if a PID is internationalized, the internationalization has to -be used everywhere where the PID is used. The complex form of the method also -allows defining an icon for the column from a resource. The "default value" -parameter is used when new properties (columns) are added to the table, to fill -in the missing values. (This default has no meaning in the usual case, such as -below, where we add items after defining the properties.) - - -[source, java] ----- -Table table = new Table("The Brightest Stars"); - -// Define two columns for the built-in container -table.addContainerProperty("Name", String.class, null); -table.addContainerProperty("Mag", Float.class, null); - -// Add a row the hard way -Object newItemId = table.addItem(); -Item row1 = table.getItem(newItemId); -row1.getItemProperty("Name").setValue("Sirius"); -row1.getItemProperty("Mag").setValue(-1.46f); - -// Add a few other rows using shorthand addItem() -table.addItem(new Object[]{"Canopus", -0.72f}, 2); -table.addItem(new Object[]{"Arcturus", -0.04f}, 3); -table.addItem(new Object[]{"Alpha Centauri", -0.01f}, 4); - -// Show exactly the currently contained rows (items) -table.setPageLength(table.size()); ----- - -In this example, we used an increasing [classname]#Integer# object as the Item -Identifier, given as the second parameter to [methodname]#addItem()#. The actual -rows are given simply as object arrays, in the same order in which the -properties were added. The objects must be of the correct class, as defined in -the [methodname]#addContainerProperty()# calls. - -.Basic Table Example -image::img/table-example1.png[] - -Scalability of the [classname]#Table# is largely dictated by the container. The -default [classname]#IndexedContainer# is relatively heavy and can cause -scalability problems, for example, when updating the values. Use of an optimized -application-specific container is recommended. Table does not have a limit for -the number of items and is just as fast with hundreds of thousands of items as -with just a few. With the current implementation of scrolling, there is a limit -of around 500 000 rows, depending on the browser and the pixel height of rows. - -Common selection component features are described in -<<dummy/../../../framework/components/components-selection#components.selection,"Selection -Components">>. - -[[components.table.selecting]] -== Selecting Items in a Table - -The [classname]#Table# allows selecting one or more items by clicking them with -the mouse. When the user selects an item, the IID of the item will be set as the -property of the table and a [classname]#ValueChangeEvent# is triggered. To -enable selection, you need to set the table __selectable__. You will also need -to set it as __immediate__ in most cases, as we do below, because without it, -the change in the property will not be communicated immediately to the server. - -The following example shows how to enable the selection of items in a -[classname]#Table# and how to handle [classname]#ValueChangeEvent# events that -are caused by changes in selection. You need to handle the event with the -[methodname]#valueChange()# method of the -[classname]#Property.ValueChangeListener# interface. - - -[source, java] ----- -// Allow selecting items from the table. -table.setSelectable(true); - -// Send changes in selection immediately to server. -table.setImmediate(true); - -// Shows feedback from selection. -final Label current = new Label("Selected: -"); - -// Handle selection change. -table.addValueChangeListener(new Property.ValueChangeListener() { - public void valueChange(ValueChangeEvent event) { - current.setValue("Selected: " + table.getValue()); - } -}); ----- - -.Table Selection Example -image::img/table-example2.png[] - -If the user clicks on an already selected item, the selection will deselected -and the table property will have [parameter]#null# value. You can disable this -behaviour by setting [methodname]#setNullSelectionAllowed(false)# for the table. - -The selection is the value of the table's property, so you can get it with -[methodname]#getValue()#. You can get it also from a reference to the table -itself. In single selection mode, the value is the item identifier of the -selected item or [parameter]#null# if no item is selected. In multiple selection -mode (see below), the value is a [classname]#Set# of item identifiers. Notice -that the set is unmodifiable, so you can not simply change it to change the -selection. - -=== Multiple Selection Mode - -A table can also be in __multiselect__ mode, where a user can select multiple -items by clicking them with left mouse button while holding the Ctrl key (or -Meta key) pressed. If Ctrl is not held, clicking an item will select it and -other selected items are deselected. The user can select a range by selecting an -item, holding the Shift key pressed, and clicking another item, in which case -all the items between the two are also selected. Multiple ranges can be selected -by first selecting a range, then selecting an item while holding Ctrl, and then -selecting another item with both Ctrl and Shift pressed. - -The multiselect mode is enabled with the [methodname]#setMultiSelect()# method -of the [classname]#AbstractSelect# superclass of [classname]#Table#. Setting -table in multiselect mode does not implicitly set it as __selectable__, so it -must be set separately. - -The [methodname]#setMultiSelectMode()# property affects the control of multiple -selection: [parameter]#MultiSelectMode.DEFAULT# is the default behaviour, which -requires holding the Ctrl (or Meta) key pressed while selecting items, while in -[parameter]#MultiSelectMode.SIMPLE# holding the Ctrl key is not needed. In the -simple mode, items can only be deselected by clicking them. - - - -[[components.table.features]] -== Table Features - -=== Page Length and Scrollbar - -The default style for [classname]#Table# provides a table with a scrollbar. The -scrollbar is located at the right side of the table and becomes visible when the -number of items in the table exceeds the page length, that is, the number of -visible items. You can set the page length with [methodname]#setPageLength()#. - -Setting the page length to zero makes all the rows in a table visible, no matter -how many rows there are. Notice that this also effectively disables buffering, -as all the entire table is loaded to the browser at once. Using such tables to -generate reports does not scale up very well, as there is some inevitable -overhead in rendering a table with Ajax. For very large reports, generating HTML -directly is a more scalable solution. - - -[[components.table.features.resizing]] -=== Resizing Columns - -You can set the width of a column programmatically from the server-side with -[methodname]#setColumnWidth()#. The column is identified by the property ID and -the width is given in pixels. - -The user can resize table columns by dragging the resize handle between two -columns. Resizing a table column causes a [classname]#ColumnResizeEvent#, which -you can handle with a [classname]#Table.ColumnResizeListener#. The table must be -set in immediate mode if you want to receive the resize events immediately, -which is typical. - - -[source, java] ----- -table.addColumnResizeListener(new Table.ColumnResizeListener(){ - public void columnResize(ColumnResizeEvent event) { - // Get the new width of the resized column - int width = event.getCurrentWidth(); - - // Get the property ID of the resized column - String column = (String) event.getPropertyId(); - - // Do something with the information - table.setColumnFooter(column, String.valueOf(width) + "px"); - } -}); - -// Must be immediate to send the resize events immediately -table.setImmediate(true); ----- - -See <<figure.component.table.columnresize>> for a result after the columns of a -table has been resized. - -[[figure.component.table.columnresize]] -.Resizing Columns -image::img/table-column-resize.png[] - - -[[components.table.features.reordering]] -=== Reordering Columns - -If [methodname]#setColumnReorderingAllowed(true)# is set, the user can reorder -table columns by dragging them with the mouse from the column header, - - -[[components.table.features.collapsing]] -=== Collapsing Columns - -When [methodname]#setColumnCollapsingAllowed(true)# is set, the right side of -the table header shows a drop-down list that allows selecting which columns are -shown. Collapsing columns is different than hiding columns with -[methodname]#setVisibleColumns()#, which hides the columns completely so that -they can not be made visible (uncollapsed) from the user interface. - -You can collapse columns programmatically with -[methodname]#setColumnCollapsed()#. Collapsing must be enabled before collapsing -columns with the method or it will throw an [classname]#IllegalAccessException#. - - -[source, java] ----- -// Allow the user to collapse and uncollapse columns -table.setColumnCollapsingAllowed(true); - -// Collapse this column programmatically -try { - table.setColumnCollapsed("born", true); -} catch (IllegalAccessException e) { - // Can't occur - collapsing was allowed above - System.err.println("Something horrible occurred"); -} - -// Give enough width for the table to accommodate the -// initially collapsed column later -table.setWidth("250px"); ----- - -See <<figure.component.table.columncollapsing>>. - -[[figure.component.table.columncollapsing]] -.Collapsing Columns -image::img/table-column-collapsing.png[] - -If the table has undefined width, it minimizes its width to fit the width of the -visible columns. If some columns are initially collapsed, the width of the table -may not be enough to accomodate them later, which will result in an ugly -horizontal scrollbar. You should consider giving the table enough width to -accomodate columns uncollapsed by the user. - - -[[components.table.features.components]] -=== Components Inside a Table - -The cells of a [classname]#Table# can contain any user interface components, not -just strings. If the rows are higher than the row height defined in the default -theme, you have to define the proper row height in a custom theme. - -When handling events for components inside a [classname]#Table#, such as for the -[classname]#Button# in the example below, you usually need to know the item the -component belongs to. Components do not themselves know about the table or the -specific item in which a component is contained. Therefore, the handling method -must use some other means for finding out the Item ID of the item. There are a -few possibilities. Usually the easiest way is to use the [methodname]#setData()# -method to attach an arbitrary object to a component. You can subclass the -component and include the identity information there. You can also simply search -the entire table for the item with the component, although that solution may not -be so scalable. - -The example below includes table rows with a [classname]#Label# in HTML content -mode, a multiline [classname]#TextField#, a [classname]#CheckBox#, and a -[classname]#Button# that shows as a link. - - -[source, java] ----- -// Create a table and add a style to allow setting the row height in theme. -final Table table = new Table(); -table.addStyleName("components-inside"); - -/* Define the names and data types of columns. - * The "default value" parameter is meaningless here. */ -table.addContainerProperty("Sum", Label.class, null); -table.addContainerProperty("Is Transferred", CheckBox.class, null); -table.addContainerProperty("Comments", TextField.class, null); -table.addContainerProperty("Details", Button.class, null); - -/* Add a few items in the table. */ -for (int i=0; i<100; i++) { - // Create the fields for the current table row - Label sumField = new Label(String.format( - "Sum is <b>$%04.2f</b><br/><i>(VAT incl.)</i>", - new Object[] {new Double(Math.random()*1000)}), - ContentMode.HTML); - CheckBox transferredField = new CheckBox("is transferred"); - - // Multiline text field. This required modifying the - // height of the table row. - TextField commentsField = new TextField(); - commentsField.setRows(3); - - // The Table item identifier for the row. - Integer itemId = new Integer(i); - - // Create a button and handle its click. A Button does not - // know the item it is contained in, so we have to store the - // item ID as user-defined data. - Button detailsField = new Button("show details"); - detailsField.setData(itemId); - detailsField.addClickListener(new Button.ClickListener() { - public void buttonClick(ClickEvent event) { - // Get the item identifier from the user-defined data. - Integer iid = (Integer)event.getButton().getData(); - Notification.show("Link " + - iid.intValue() + " clicked."); - } - }); - detailsField.addStyleName("link"); - - // Create the table row. - table.addItem(new Object[] {sumField, transferredField, - commentsField, detailsField}, - itemId); -} - -// Show just three rows because they are so high. -table.setPageLength(3); ----- -See the http://demo.vaadin.com/book-examples-vaadin7/book#component.table.components.components2[on-line example, window="_blank"]. - -The row height has to be set higher than the default with a style rule such as -the following: - - -[source, css] ----- -/* Table rows contain three-row TextField components. */ -.v-table-components-inside .v-table-cell-content { - height: 54px; -} ----- -See the http://demo.vaadin.com/book-examples-vaadin7/book#component.table.components.components2[on-line example, window="_blank"]. - -The table will look as shown in <<figure.components.table.components-inside>>. - -[[figure.components.table.components-inside]] -.Components in a Table -image::img/table-components.png[] - - -[[components.table.features.iterating]] -=== Iterating Over a Table - -As the items in a [classname]#Table# are not indexed, iterating over the items -has to be done using an iterator. The [methodname]#getItemIds()# method of the -[classname]#Container# interface of [classname]#Table# returns a -[classname]#Collection# of item identifiers over which you can iterate using an -[classname]#Iterator#. For an example about iterating over a [classname]#Table#, -please see -<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container,"Collecting -Items in Containers">>. Notice that you may not modify the [classname]#Table# -during iteration, that is, add or remove items. Changing the data is allowed. - - -[[components.table.features.filtering]] -=== Filtering Table Contents - -A table can be filtered if its container data source implements the -[classname]#Filterable# interface, as the default [classname]#IndexedContainer# -does. See -<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.filtered,"Filterable -Containers">>. ((("Container", -"Filterable"))) - - - -[[components.table.editing]] -== Editing the Values in a Table - -Normally, a [classname]#Table# simply displays the items and their fields as -text. If you want to allow the user to edit the values, you can either put them -inside components as we did earlier or simply call -[methodname]#setEditable(true)#, in which case the cells are automatically -turned into editable fields. - -Let us begin with a regular table with a some columns with usual Java types, -namely a [classname]#Date#, [classname]#Boolean#, and a [classname]#String#. - - -[source, java] ----- -// Create a table. It is by default not editable. -Table table = new Table(); - -// Define the names and data types of columns. -table.addContainerProperty("Date", Date.class, null); -table.addContainerProperty("Work", Boolean.class, null); -table.addContainerProperty("Comments", String.class, null); - -... ----- - -You could put the table in editable mode right away. We continue the example by -adding a check box to switch the table between normal and editable modes: - - -[source, java] ----- -CheckBox editable = new CheckBox("Editable", true); -editable.addValueChangeListener(valueChange -> // Java 8 - table.setEditable((Boolean) editable.getValue())); ----- - -Now, when you check to checkbox, the components in the table turn into editable -fields, as shown in <<figure.component.table.editable>>. - -[[figure.component.table.editable]] -.A Table in Normal and Editable Mode -image::img/table-editable3.png[] - -[[components.table.editing.fieldfactories]] -=== Field Factories - -The field components that allow editing the values of particular types in a -table are defined in a field factory that implements the -[classname]#TableFieldFactory# interface. The default implementation is -[classname]#DefaultFieldFactory#, which offers the following crude mappings: - -.Type to Field Mappings in [classname]#DefaultFieldFactory# -[options="header"] -|=============== -|Property Type|Mapped to Field Class -|[classname]#Date#|A[classname]#DateField#. -|[classname]#Boolean#|A[classname]#CheckBox#. -|[classname]#Item#|A[classname]#Form#(deprecated in Vaadin 7). The fields of the form are automatically created from the item's properties using a[classname]#FormFieldFactory#. The normal use for this property type is inside a[classname]#Form#and is less useful inside a[classname]#Table#. -|__other__|A[classname]#TextField#. The text field manages conversions from the basic types, if possible. - -|=============== - - - -Field factories are covered with more detail in -<<dummy/../../../framework/datamodel/datamodel-itembinding#datamodel.itembinding,"Creating -Forms by Binding Fields to Items">>. You could just implement the -[classname]#TableFieldFactory# interface, but we recommend that you extend the -[classname]#DefaultFieldFactory# according to your needs. In the default -implementation, the mappings are defined in the -[methodname]#createFieldByPropertyType()# method (you might want to look at the -source code) both for tables and forms. - - -ifdef::web[] -[[components.table.editing.navigation]] -=== Navigation in Editable Mode - -In the editable mode, the editor fields can have focus. Pressing Tab moves the -focus to next column or, at the last column, to the first column of the next -item. Respectively, pressing ShiftTab moves the focus backward. If the focus is -in the last column of the last visible item, the pressing Tab moves the focus -outside the table. Moving backward from the first column of the first item moves -the focus to the table itself. Some updates to the table, such as changing the -headers or footers or regenerating a column, can move the focus from an editor -component to the table itself. - -The default behaviour may be undesirable in many cases. For example, the focus -also goes through any read-only editor fields and can move out of the table -inappropriately. You can provide better navigation is to use event handler for -shortcut keys such as Tab, Arrow Up, Arrow Down, and Enter. - - -[source, java] ----- -// Keyboard navigation -class KbdHandler implements Handler { - Action tab_next = new ShortcutAction("Tab", - ShortcutAction.KeyCode.TAB, null); - Action tab_prev = new ShortcutAction("Shift+Tab", - ShortcutAction.KeyCode.TAB, - new int[] {ShortcutAction.ModifierKey.SHIFT}); - Action cur_down = new ShortcutAction("Down", - ShortcutAction.KeyCode.ARROW_DOWN, null); - Action cur_up = new ShortcutAction("Up", - ShortcutAction.KeyCode.ARROW_UP, null); - Action enter = new ShortcutAction("Enter", - ShortcutAction.KeyCode.ENTER, null); - public Action[] getActions(Object target, Object sender) { - return new Action[] {tab_next, tab_prev, cur_down, - cur_up, enter}; - } - - public void handleAction(Action action, Object sender, - Object target) { - if (target instanceof TextField) { - // Move according to keypress - int itemid = (Integer) ((TextField) target).getData(); - if (action == tab_next || action == cur_down) - itemid++; - else if (action == tab_prev || action == cur_up) - itemid--; - // On enter, just stay where you were. If we did - // not catch the enter action, the focus would be - // moved to wrong place. - - if (itemid >= 0 && itemid < table.size()) { - TextField newTF = valueFields.get(itemid); - if (newTF != null) - newTF.focus(); - } - } - } -} - -// Panel that handles keyboard navigation -Panel navigator = new Panel(); -navigator.addStyleName(Reindeer.PANEL_LIGHT); -navigator.addComponent(table); -navigator.addActionHandler(new KbdHandler()); ----- - -The main issue in implementing keyboard navigation in an editable table is that -the editor fields do not know the table they are in. To find the parent table, -you can either look up in the component container hierarchy or simply store a -reference to the table with [methodname]#setData()# in the field component. The -other issue is that you can not acquire a reference to an editor field from the -[classname]#Table# component. One solution is to use some external collection, -such as a [classname]#HashMap#, to map item IDs to the editor fields. - - -[source, java] ----- -// Can't access the editable components from the table so -// must store the information -final HashMap<Integer,TextField> valueFields = - new HashMap<Integer,TextField>(); ----- - -The map has to be filled in a [classname]#TableFieldFactory#, such as in the -following. You also need to set the reference to the table there and you can -also set the initial focus there. - - -[source, java] ----- -table.setTableFieldFactory(new TableFieldFactory () { - public Field createField(Container container, Object itemId, - Object propertyId, Component uiContext) { - TextField field = new TextField((String) propertyId); - - // User can only edit the numeric column - if ("Source of Fear".equals(propertyId)) - field.setReadOnly(true); - else { // The numeric column - // The field needs to know the item it is in - field.setData(itemId); - - // Remember the field - valueFields.put((Integer) itemId, field); - - // Focus the first editable value - if (((Integer)itemId) == 0) - field.focus(); - } - return field; - } -}); ----- - -The issues are complicated by the fact that the editor fields are not generated -for the entire table, but only for a cache window that includes the visible -items and some items above and below it. For example, if the beginning of a big -scrollable table is visible, the editor component for the last item does not -exist. This issue is relevant mostly if you want to have wrap-around navigation -that jumps from the last to first item and vice versa. - -endif::web[] - - -[[components.table.headersfooters]] -== Column Headers and Footers - -[classname]#Table# supports both column headers and footers; the headers are -enabled by default. - -[[components.table.headersfooters.headers]] -=== Headers - -The table header displays the column headers at the top of the table. You can -use the column headers to reorder or resize the columns, as described earlier. -By default, the header of a column is the property ID of the column, unless -given explicitly with [methodname]#setColumnHeader()#. - - -[source, java] ----- -// Define the properties -table.addContainerProperty("lastname", String.class, null); -table.addContainerProperty("born", Integer.class, null); -table.addContainerProperty("died", Integer.class, null); - -// Set nicer header names -table.setColumnHeader("lastname", "Name"); -table.setColumnHeader("born", "Born"); -table.setColumnHeader("died", "Died"); ----- - -The text of the column headers and the visibility of the header depends on the -__column header mode__. The header is visible by default, but you can disable it -with [methodname]#setColumnHeaderMode(Table.COLUMN_HEADER_MODE_HIDDEN)#. - - -[[components.table.headersfooters.footers]] -=== Footers - -The table footer can be useful for displaying sums or averages of values in a -column, and so on. The footer is not visible by default; you can enable it with -[methodname]#setFooterVisible(true)#. Unlike in the header, the column headers -are empty by default. You can set their value with -[methodname]#setColumnFooter()#. The columns are identified by their property -ID. - -The following example shows how to calculate average of the values in a column: - - -[source, java] ----- -// Have a table with a numeric column -Table table = new Table("Custom Table Footer"); -table.addContainerProperty("Name", String.class, null); -table.addContainerProperty("Died At Age", Integer.class, null); - -// Insert some data -Object people[][] = { {"Galileo", 77}, - {"Monnier", 83}, - {"Vaisala", 79}, - {"Oterma", 86}}; -for (int i=0; i<people.length; i++) - table.addItem(people[i], new Integer(i)); - -// Calculate the average of the numeric column -double avgAge = 0; -for (int i=0; i<people.length; i++) - avgAge += (Integer) people[i][1]; -avgAge /= people.length; - -// Set the footers -table.setFooterVisible(true); -table.setColumnFooter("Name", "Average"); -table.setColumnFooter("Died At Age", String.valueOf(avgAge)); - -// Adjust the table height a bit -table.setPageLength(table.size()); ----- - -The resulting table is shown in -<<figure.components.table.headersfooters.footer>>. - -[[figure.components.table.headersfooters.footer]] -.A Table with a Footer -image::img/table-footer.png[] - - -[[components.table.headersfooters.clicks]] -=== Handling Mouse Clicks on Headers and Footers - -Normally, when the user clicks a column header, the table will be sorted by the -column, assuming that the data source is [classname]#Sortable# and sorting is -not disabled. In some cases, you might want some other functionality when the -user clicks the column header, such as selecting the column in some way. - -Clicks in the header cause a [classname]#HeaderClickEvent#, which you can handle -with a [classname]#Table.HeaderClickListener#. Click events on the table header -(and footer) are, like button clicks, sent immediately to server, so there is no -need to set [methodname]#setImmediate()#. - - -[source, java] ----- -// Handle the header clicks -table.addHeaderClickListener(new Table.HeaderClickListener() { - public void headerClick(HeaderClickEvent event) { - String column = (String) event.getPropertyId(); - Notification.show("Clicked " + column + - "with " + event.getButtonName()); - } -}); - -// Disable the default sorting behavior -table.setSortDisabled(true); ----- - -Setting a click handler does not automatically disable the sorting behavior of -the header; you need to disable it explicitly with -[methodname]#setSortDisabled(true)#. Header click events are not sent when the -user clicks the column resize handlers to drag them. - -The [classname]#HeaderClickEvent# object provides the identity of the clicked -column with [methodname]#getPropertyId()#. The [methodname]#getButton()# reports -the mouse button with which the click was made: [parameter]#BUTTON_LEFT#, -[parameter]#BUTTON_RIGHT#, or [parameter]#BUTTON_MIDDLE#. The -[methodname]#getButtonName()# a human-readable button name in English: " -[parameter]#left#", " [parameter]#right#", or " [parameter]#middle#". The -[methodname]#isShiftKey()#, [methodname]#isCtrlKey()#, etc., methods indicate if -the Shift, Ctrl, Alt or other modifier keys were pressed during the click. - -Clicks in the footer cause a [classname]#FooterClickEvent#, which you can handle -with a [classname]#Table.FooterClickListener#. Footers do not have any default -click behavior, like the sorting in the header. Otherwise, handling clicks in -the footer is equivalent to handling clicks in the header. - - - -[[components.table.columngenerator]] -== Generated Table Columns - -A table can have generated columns which values can be calculated based on the -values in other columns. The columns are generated with a class implementing the -[interfacename]#Table.ColumnGenerator# interface. - -The [classname]#GeneratedPropertyContainer# described in -<<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.gpc,"GeneratedPropertyContainer">> -is another way to accomplish the same task at container level. In addition to -generating values, you can also use the feature for formatting or styling -columns. - -ifdef::web[] -[[components.table.columngenerator.generator]] -=== Defining a Column Generator - -Column generators are objects that implement the -[classname]#Table.ColumnGenerator# interface and its -[methodname]#generateCell()# method. The method gets the identity of the item -and column as its parameters, in addition to the table object, and has to return -a component. The interface is functional, so you can also define it by a lambda -expression or a method reference in Java 8. - -The following example defines a generator for formatting [classname]#Double# -valued fields according to a format string (as in -[classname]#java.util.Formatter#). - - -[source, java] ----- -/** Formats the value in a column containing Double objects. */ -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)) { - Label label = new Label(String.format(format, - new Object[] { (Double) prop.getValue() })); - - // Set styles for the column: one indicating that it's - // a value and a more specific one with the column - // name in it. This assumes that the column name - // is proper for CSS. - label.addStyleName("column-type-value"); - label.addStyleName("column-" + (String) columnId); - return label; - } - return null; - } -} ----- - -The column generator is called for all the visible (or more accurately cached) -items in a table. If the user scrolls the table to another position in the -table, the columns of the new visible rows are generated dynamically. - -Generated column cells are automatically updated when a property value in the -table row changes. Note that a generated cell, even if it is a field, does not -normally have a property value bound to the table's container, so changes in -generated columns do not trigger updates in other generated columns. It should -also be noted that if a generated column cell depends on values in other rows, -changes in the other rows do not trigger automatic update. You can get notified -of such value changes by listening for them with a -[interfacename]#ValueChangeListener# in the generated components. If you do so, -you must remove such listeners when the generated components are detached from -the UI or otherwise the listeners will accumulate in the container when the -table is scrolled back and forth, causing possibly severe memory leak. - -endif::web[] - -ifdef::web[] -[[components.table.columngenerator.adding]] -=== Adding Generated Columns - -You add new generated columns to a [classname]#Table# with -[methodname]#addGeneratedColumn()#. It takes a property ID of the generated -column as the first parameter and the generator as the second. - - -[source, java] ----- -// Define the generated columns and their generators -table.addGeneratedColumn("date", // Java 8: - this::generateNonEditableCell); -table.addGeneratedColumn("price", - new PriceColumnGenerator()); -table.addGeneratedColumn("consumption", - new ConsumptionColumnGenerator()); -table.addGeneratedColumn("dailycost", - new DailyCostColumnGenerator()); ----- - -Notice that the [methodname]#addGeneratedColumn()# always places the generated -columns as the last column, even if you defined some other order previously. You -will have to set the proper order with [methodname]#setVisibleColumns()#. - - -[source, java] ----- -table.setVisibleColumns("date", "quantity", "price", "total"); ----- - -endif::web[] - -ifdef::web[] -[[components.table.columngenerator.editable]] -=== Generators in Editable Table - -When you set a table as [parameter]#editable#, table cells change to editable -fields. When the user changes the values in the fields, the generated cells in -the same row are updated automatically. However, putting a table with generated -columns in editable mode has a few quirks. One is that the editable mode does -not affect generated columns. You have two alternatives: either you generate the -editing fields in the generator or, in case of formatter generators, remove the -generators in the editable mode to allow editing the values. The following -example uses the latter approach. - - -[source, java] ----- -// Have a check box that allows the user -// to make the quantity and total columns editable. -final CheckBox editable = new CheckBox( - "Edit the input values - calculated columns are regenerated"); - -editable.setImmediate(true); -editable.addClickListener(new ClickListener() { - public void buttonClick(ClickEvent event) { - table.setEditable(editable.booleanValue()); - - // The columns may not be generated when we want to - // have them editable. - if (editable.booleanValue()) { - table.removeGeneratedColumn("quantity"); - table.removeGeneratedColumn("total"); - } else { // Not editable - // Show the formatted values. - table.addGeneratedColumn("quantity", - new ValueColumnGenerator("%.2f l")); - table.addGeneratedColumn("total", - new ValueColumnGenerator("%.2f e")); - } - // The visible columns are affected by removal - // and addition of generated columns so we have - // to redefine them. - table.setVisibleColumns("date", "quantity", - "price", "total", "consumption", "dailycost"); - } -}); ----- - -You will also have to set the editing fields in [parameter]#immediate# mode to -have the update occur immediately when an edit field loses the focus. You can -set the fields in [parameter]#immediate# mode with the a custom -[classname]#TableFieldFactory#, such as the one given below, that just extends -the default implementation to set the mode: - - -[source, java] ----- -public class ImmediateFieldFactory extends DefaultFieldFactory { - public Field createField(Container container, - Object itemId, - Object propertyId, - Component uiContext) { - // Let the DefaultFieldFactory create the fields... - Field field = super.createField(container, itemId, - propertyId, uiContext); - - // ...and just set them as immediate. - ((AbstractField)field).setImmediate(true); - - return field; - } -} -... -table.setTableFieldFactory(new ImmediateFieldFactory()); ----- - -If you generate the editing fields with the column generator, you avoid having -to use such a field factory, but of course have to generate the fields for both -normal and editable modes. - -<<figure.ui.table.generated>> shows a table with columns calculated (blue) and -simply formatted (black) with column generators. - -[[figure.ui.table.generated]] -.Table with Generated Columns in Normal and Editable Mode -image::img/table-generatedcolumns1.png[] - -endif::web[] - - -[[components.table.columnformatting]] -== Formatting Table Columns - -The displayed values of properties shown in a table are normally formatted using -the [methodname]#toString()# method of each property. Customizing the format in -a table column can be done in several ways: - -* Using [classname]#ColumnGenerator# to generate a second column that is formatted. The original column needs to be set invisible. See <<components.table.columngenerator>>. -* Using a [classname]#Converter# to convert between the property data model and its representation in the table. -* Using a [classname]#GeneratedPropertyContainer# as a wrapper around the actual container to provide formatting. -* Overriding the default [methodname]#formatPropertyValue()# in [classname]#Table#. - -As using a [classname]#PropertyFormatter# is generally much more awkward than -overriding the [methodname]#formatPropertyValue()#, its use is not described -here. - -You can override [methodname]#formatPropertyValue()# as is done in the following -example: - - -[source, java] ----- -// Create a table that overrides the default -// property (column) format -final Table table = new Table("Formatted Table") { - @Override - protected String formatPropertyValue(Object rowId, - Object colId, Property property) { - // Format by property type - if (property.getType() == Date.class) { - SimpleDateFormat df = - new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); - return df.format((Date)property.getValue()); - } - - return super.formatPropertyValue(rowId, colId, property); - } -}; - -// The table has some columns -table.addContainerProperty("Time", Date.class, null); - -... Fill the table with data ... ----- - -You can also distinguish between columns by the [parameter]#colId# parameter, -which is the property ID of the column. [classname]#DecimalFormat# is useful for -formatting decimal values. - - -[source, java] ----- -... in formatPropertyValue() ... -} else if ("Value".equals(pid)) { - // Format a decimal value for a specific locale - DecimalFormat df = new DecimalFormat("#.00", - new DecimalFormatSymbols(locale)); - return df.format((Double) property.getValue()); -} -... -table.addContainerProperty("Value", Double.class, null); ----- - -A table with the formatted date and decimal value columns is shown in -<<figure.components.table.columnformatting>>. - -[[figure.components.table.columnformatting]] -.Formatted Table Columns -image::img/table-columnformatting.png[] - -You can use CSS for further styling of table rows, columns, and individual cells -by using a [classname]#CellStyleGenerator#. It is described in -<<components.table.css>>. - - -[[components.table.css]] -== CSS Style Rules - -Styling the overall style of a [classname]#Table# can be done with the following -CSS rules. - - -[source, css] ----- -.v-table {} - .v-table-header-wrap {} - .v-table-header {} - .v-table-header-cell {} - .v-table-resizer {} /* Column resizer handle. */ - .v-table-caption-container {} - .v-table-body {} - .v-table-row-spacer {} - .v-table-table {} - .v-table-row {} - .v-table-cell-content {} ----- - -Notice that some of the widths and heights in a table are calculated dynamically -and can not be set in CSS. - -ifdef::web[] -[[components.table.css.cellstylegenerator]] -=== Generating Cell Styles With [interfacename]#CellStyleGenerator# - -The [classname]#Table.CellStyleGenerator# interface allows you to set the CSS -style for each individual cell in a table. You need to implement the -[methodname]#getStyle()#, which gets the row (item) and column (property) -identifiers as parameters and can return a style name for the cell. The returned -style name will be concatenated to prefix " -[literal]#++v-table-cell-content-++#". - -The [methodname]#getStyle()# is called also for each row, so that the -[parameter]#propertyId# parameter is [literal]#++null++#. This allows setting a -row style. - -Alternatively, you can use a [classname]#Table.ColumnGenerator# (see -<<components.table.columngenerator>>) to generate the actual UI components of -the cells and add style names to them. - - -[source, java] ----- -Table table = new Table("Table with Cell Styles"); -table.addStyleName("checkerboard"); - -// Add some columns in the table. In this example, the property -// IDs of the container are integers so we can determine the -// column number easily. -table.addContainerProperty("0", String.class, null, "", null, null); -for (int i=0; i<8; i++) - table.addContainerProperty(""+(i+1), String.class, null, - String.valueOf((char) (65+i)), null, null); - -// Add some items in the table. -table.addItem(new Object[]{ - "1", "R", "N", "B", "Q", "K", "B", "N", "R"}, new Integer(0)); -table.addItem(new Object[]{ - "2", "P", "P", "P", "P", "P", "P", "P", "P"}, new Integer(1)); -for (int i=2; i<6; i++) - table.addItem(new Object[]{String.valueOf(i+1), - "", "", "", "", "", "", "", ""}, new Integer(i)); -table.addItem(new Object[]{ - "7", "P", "P", "P", "P", "P", "P", "P", "P"}, new Integer(6)); -table.addItem(new Object[]{ - "8", "R", "N", "B", "Q", "K", "B", "N", "R"}, new Integer(7)); -table.setPageLength(8); - -// Set cell style generator -table.setCellStyleGenerator(new Table.CellStyleGenerator() { - public String getStyle(Object itemId, Object propertyId) { - // Row style setting, not relevant in this example. - if (propertyId == null) - return "green"; // Will not actually be visible - - int row = ((Integer)itemId).intValue(); - int col = Integer.parseInt((String)propertyId); - - // The first column. - if (col == 0) - return "rowheader"; - - // Other cells. - if ((row+col)%2 == 0) - return "black"; - else - return "white"; - } -}); ----- - -You can then style the cells, for example, as follows: - - -[source, css] ----- -/* Center the text in header. */ -.v-table-header-cell { - text-align: center; -} - -/* Basic style for all cells. */ -.v-table-checkerboard .v-table-cell-content { - text-align: center; - vertical-align: middle; - padding-top: 12px; - width: 20px; - height: 28px; -} - -/* Style specifically for the row header cells. */ -.v-table-cell-content-rowheader { - background: #E7EDF3 - url(../default/table/img/header-bg.png) repeat-x scroll 0 0; -} - -/* Style specifically for the "white" cells. */ -.v-table-cell-content-white { - background: white; - color: black; -} - -/* Style specifically for the "black" cells. */ -.v-table-cell-content-black { - background: black; - color: white; -} ----- - -The table will look as shown in <<figure.components.table.cell-style>>. - -[[figure.components.table.cell-style]] -.Cell Style Generator for a Table -image::img/table-cellstylegenerator1.png[] - -endif::web[] - - -(((range="endofrange", startref="term.components.table"))) - - |