You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

components-table.asciidoc 44KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166
  1. ---
  2. title: Table
  3. order: 21
  4. layout: page
  5. ---
  6. [[components.table]]
  7. = [classname]#Table#
  8. ifdef::web[]
  9. [.sampler]
  10. image:{live-demo-image}[alt="Live Demo", link="http://demo.vaadin.com/sampler/#ui/grids-and-trees/table"]
  11. endif::web[]
  12. ((("[classname]#Table#", id="term.components.table", range="startofrange")))
  13. [NOTE]
  14. ====
  15. [classname]#Table# is largely obsolete and has been replaced by the newer [classname]#Grid# component.
  16. Nevertheless, it still has some features, such as drag and drop, which have no counterpart in Grid.
  17. Table also still uses the old data API.
  18. Hence, only a short introduction is given below; see the online Vaadin Docs for more details.
  19. ====
  20. The [classname]#Table# component is intended for presenting tabular data
  21. organized in rows and columns. The [classname]#Table# is one of the most
  22. versatile components in Vaadin. Table cells can include text or arbitrary UI
  23. components. You can easily implement editing of the table data, for example
  24. clicking on a cell could change it to a text field for editing.
  25. .Basic Table Example
  26. image::img/table-example1.png[width=35%, scaledwidth=50%]
  27. The data contained in a [classname]#Table# is managed using the Vaadin data model (see <<dummy/../../../framework/datamodel/datamodel-overview.asciidoc#datamodel.overview,"Binding Components to Data">>), through the [classname]#Container# interface of the [classname]#Table#.
  28. This makes it possible to bind a table directly to a data source, such as a database query.
  29. Only the visible part of the table is loaded
  30. into the browser and moving the visible window with the scrollbar loads content
  31. from the server. While the data is being loaded, a tooltip will be displayed
  32. that shows the current range and total number of items in the table. The rows of
  33. the table are __items__ in the container and the columns are __properties__.
  34. Each table row (item) is identified with an __item identifier__ (IID), and each
  35. column (property) with a __property identifier__ (PID).
  36. When creating a table, you first need to define columns with
  37. [methodname]#addContainerProperty()#. This method comes in two flavors. The
  38. simpler one takes the property ID of the column and uses it also as the caption
  39. of the column. The more complex one allows differing PID and header for the
  40. column. This may make, for example, internationalization of table headers
  41. easier, because if a PID is internationalized, the internationalization has to
  42. be used everywhere where the PID is used. The complex form of the method also
  43. allows defining an icon for the column from a resource. The "default value"
  44. parameter is used when new properties (columns) are added to the table, to fill
  45. in the missing values. (This default has no meaning in the usual case, such as
  46. below, where we add items after defining the properties.)
  47. [source, java]
  48. ----
  49. Table table = new Table("The Brightest Stars");
  50. // Define two columns for the built-in container
  51. table.addContainerProperty("Name", String.class, null);
  52. table.addContainerProperty("Mag", Float.class, null);
  53. // Add a row the hard way
  54. Object newItemId = table.addItem();
  55. Item row1 = table.getItem(newItemId);
  56. row1.getItemProperty("Name").setValue("Sirius");
  57. row1.getItemProperty("Mag").setValue(-1.46f);
  58. // Add a few other rows using shorthand addItem()
  59. table.addItem(new Object[]{"Canopus", -0.72f}, 2);
  60. table.addItem(new Object[]{"Arcturus", -0.04f}, 3);
  61. table.addItem(new Object[]{"Alpha Centauri", -0.01f}, 4);
  62. // Show exactly the currently contained rows (items)
  63. table.setPageLength(table.size());
  64. ----
  65. In this example, we used an increasing [classname]#Integer# object as the Item
  66. Identifier, given as the second parameter to [methodname]#addItem()#. The actual
  67. rows are given simply as object arrays, in the same order in which the
  68. properties were added. The objects must be of the correct class, as defined in
  69. the [methodname]#addContainerProperty()# calls.
  70. ifdef::vaadin7[]
  71. Scalability of the [classname]#Table# is largely dictated by the container. The
  72. default [classname]#IndexedContainer# is relatively heavy and can cause
  73. scalability problems, for example, when updating the values. Use of an optimized
  74. application-specific container is recommended. Table does not have a limit for
  75. the number of items and is just as fast with hundreds of thousands of items as
  76. with just a few. With the current implementation of scrolling, there is a limit
  77. of around 500 000 rows, depending on the browser and the pixel height of rows.
  78. Common selection component features are described in
  79. <<dummy/../../../framework/components/components-selection#components.selection,"Selection Components">>.
  80. [[components.table.selecting]]
  81. == Selecting Items in a Table
  82. The [classname]#Table# allows selecting one or more items by clicking them with
  83. the mouse. When the user selects an item, the IID of the item will be set as the
  84. property of the table and a [classname]#ValueChangeEvent# is triggered. To
  85. enable selection, you need to set the table __selectable__. You will also need
  86. to set it as __immediate__ in most cases, as we do below, because without it,
  87. the change in the property will not be communicated immediately to the server.
  88. The following example shows how to enable the selection of items in a
  89. [classname]#Table# and how to handle [classname]#ValueChangeEvent# events that
  90. are caused by changes in selection. You need to handle the event with the
  91. [methodname]#valueChange()# method of the
  92. [classname]#Property.ValueChangeListener# interface.
  93. [source, java]
  94. ----
  95. // Allow selecting items from the table.
  96. table.setSelectable(true);
  97. // Send changes in selection immediately to server.
  98. table.setImmediate(true);
  99. // Shows feedback from selection.
  100. final Label current = new Label("Selected: -");
  101. // Handle selection change.
  102. table.addValueChangeListener(new Property.ValueChangeListener() {
  103. public void valueChange(ValueChangeEvent event) {
  104. current.setValue("Selected: " + table.getValue());
  105. }
  106. });
  107. ----
  108. .Table selection example
  109. image::img/table-example2.png[width=35%, scaledwidth=80%]
  110. If the user clicks on an already selected item, the selection will deselected
  111. and the table property will have [parameter]#null# value. You can disable this
  112. behaviour by setting [methodname]#setNullSelectionAllowed(false)# for the table.
  113. The selection is the value of the table's property, so you can get it with
  114. [methodname]#getValue()#. You can get it also from a reference to the table
  115. itself. In single selection mode, the value is the item identifier of the
  116. selected item or [parameter]#null# if no item is selected. In multiple selection
  117. mode (see below), the value is a [classname]#Set# of item identifiers. Notice
  118. that the set is unmodifiable, so you can not simply change it to change the
  119. selection.
  120. === Multiple Selection Mode
  121. A table can also be in __multiselect__ mode, where a user can select multiple
  122. items by clicking them with left mouse button while holding the kbd:[Ctrl] key (or kbd:[Meta] key) pressed. If kbd:[Ctrl] is not held, clicking an item will select it and
  123. other selected items are deselected. The user can select a range by selecting an
  124. item, holding the kbd:[Shift] key pressed, and clicking another item, in which case
  125. all the items between the two are also selected. Multiple ranges can be selected
  126. by first selecting a range, then selecting an item while holding kbd:[Ctrl], and then
  127. selecting another item with both kbd:[Ctrl] and kbd:[Shift] pressed.
  128. The multiselect mode is enabled with the [methodname]#setMultiSelect()# method
  129. of the [classname]#AbstractSelect# superclass of [classname]#Table#. Setting
  130. table in multiselect mode does not implicitly set it as __selectable__, so it
  131. must be set separately.
  132. The [methodname]#setMultiSelectMode()# property affects the control of multiple
  133. selection: [parameter]#MultiSelectMode.DEFAULT# is the default behaviour, which
  134. requires holding the kbd:[Ctrl] (or kbd:[Meta]) key pressed while selecting items, while in
  135. [parameter]#MultiSelectMode.SIMPLE# holding the kbd:[Ctrl] key is not needed. In the
  136. simple mode, items can only be deselected by clicking them.
  137. [[components.table.features]]
  138. == Table Features
  139. === Page Length and Scrollbar
  140. The default style for [classname]#Table# provides a table with a scrollbar. The
  141. scrollbar is located at the right side of the table and becomes visible when the
  142. number of items in the table exceeds the page length, that is, the number of
  143. visible items. You can set the page length with [methodname]#setPageLength()#.
  144. Setting the page length to zero makes all the rows in a table visible, no matter
  145. how many rows there are. Notice that this also effectively disables buffering,
  146. as all the entire table is loaded to the browser at once. Using such tables to
  147. generate reports does not scale up very well, as there is some inevitable
  148. overhead in rendering a table with Ajax. For very large reports, generating HTML
  149. directly is a more scalable solution.
  150. [[components.table.features.resizing]]
  151. === Resizing Columns
  152. You can set the width of a column programmatically from the server-side with
  153. [methodname]#setColumnWidth()#. The column is identified by the property ID and
  154. the width is given in pixels.
  155. The user can resize table columns by dragging the resize handle between two
  156. columns. Resizing a table column causes a [classname]#ColumnResizeEvent#, which
  157. you can handle with a [classname]#Table.ColumnResizeListener#. The table must be
  158. set in immediate mode if you want to receive the resize events immediately,
  159. which is typical.
  160. [source, java]
  161. ----
  162. table.addColumnResizeListener(new Table.ColumnResizeListener(){
  163. public void columnResize(ColumnResizeEvent event) {
  164. // Get the new width of the resized column
  165. int width = event.getCurrentWidth();
  166. // Get the property ID of the resized column
  167. String column = (String) event.getPropertyId();
  168. // Do something with the information
  169. table.setColumnFooter(column, String.valueOf(width) + "px");
  170. }
  171. });
  172. // Must be immediate to send the resize events immediately
  173. table.setImmediate(true);
  174. ----
  175. See <<figure.component.table.columnresize>> for a result after the columns of a
  176. table has been resized.
  177. [[figure.component.table.columnresize]]
  178. .Resizing Columns
  179. image::img/table-column-resize.png[width=50%, scaledwidth=80%]
  180. [[components.table.features.reordering]]
  181. === Reordering Columns
  182. If [methodname]#setColumnReorderingAllowed(true)# is set, the user can reorder
  183. table columns by dragging them with the mouse from the column header,
  184. [[components.table.features.collapsing]]
  185. === Collapsing Columns
  186. When [methodname]#setColumnCollapsingAllowed(true)# is set, the right side of
  187. the table header shows a drop-down list that allows selecting which columns are
  188. shown. Collapsing columns is different than hiding columns with
  189. [methodname]#setVisibleColumns()#, which hides the columns completely so that
  190. they can not be made visible (uncollapsed) from the user interface.
  191. You can collapse columns programmatically with
  192. [methodname]#setColumnCollapsed()#. Collapsing must be enabled before collapsing
  193. columns with the method or it will throw an [classname]#IllegalAccessException#.
  194. [source, java]
  195. ----
  196. // Allow the user to collapse and uncollapse columns
  197. table.setColumnCollapsingAllowed(true);
  198. // Collapse this column programmatically
  199. try {
  200. table.setColumnCollapsed("born", true);
  201. } catch (IllegalAccessException e) {
  202. // Can't occur - collapsing was allowed above
  203. System.err.println("Something horrible occurred");
  204. }
  205. // Give enough width for the table to accommodate the
  206. // initially collapsed column later
  207. table.setWidth("250px");
  208. ----
  209. See <<figure.component.table.columncollapsing>>.
  210. [[figure.component.table.columncollapsing]]
  211. .Collapsing Columns
  212. image::img/table-column-collapsing.png[width=40%, scaledwidth=80%]
  213. If the table has undefined width, it minimizes its width to fit the width of the
  214. visible columns. If some columns are initially collapsed, the width of the table
  215. may not be enough to accomodate them later, which will result in an ugly
  216. horizontal scrollbar. You should consider giving the table enough width to
  217. accomodate columns uncollapsed by the user.
  218. [[components.table.features.components]]
  219. === Components Inside a Table
  220. The cells of a [classname]#Table# can contain any user interface components, not
  221. just strings. If the rows are higher than the row height defined in the default
  222. theme, you have to define the proper row height in a custom theme.
  223. When handling events for components inside a [classname]#Table#, such as for the
  224. [classname]#Button# in the example below, you usually need to know the item the
  225. component belongs to. Components do not themselves know about the table or the
  226. specific item in which a component is contained. Therefore, the handling method
  227. must use some other means for finding out the Item ID of the item. There are a
  228. few possibilities. Usually the easiest way is to use the [methodname]#setData()#
  229. method to attach an arbitrary object to a component. You can subclass the
  230. component and include the identity information there. You can also simply search
  231. the entire table for the item with the component, although that solution may not
  232. be so scalable.
  233. The example below includes table rows with a [classname]#Label# in HTML content
  234. mode, a multiline [classname]#TextField#, a [classname]#CheckBox#, and a
  235. [classname]#Button# that shows as a link.
  236. [source, java]
  237. ----
  238. // Create a table and add a style to
  239. // allow setting the row height in theme.
  240. Table table = new Table();
  241. table.addStyleName("components-inside");
  242. /* Define the names and data types of columns.
  243. * The "default value" parameter is meaningless here. */
  244. table.addContainerProperty("Sum", Label.class, null);
  245. table.addContainerProperty("Is Transferred", CheckBox.class, null);
  246. table.addContainerProperty("Comments", TextField.class, null);
  247. table.addContainerProperty("Details", Button.class, null);
  248. /* Add a few items in the table. */
  249. for (int i=0; i<100; i++) {
  250. // Create the fields for the current table row
  251. Label sumField = new Label(String.format(
  252. "Sum is <b>$%04.2f</b><br/><i>(VAT incl.)</i>",
  253. new Object[] {new Double(Math.random()*1000)}),
  254. ContentMode.HTML);
  255. CheckBox transferredField = new CheckBox("is transferred");
  256. // Multiline text field. This required modifying the
  257. // height of the table row.
  258. TextField commentsField = new TextField();
  259. commentsField.setRows(3);
  260. // The Table item identifier for the row.
  261. Integer itemId = new Integer(i);
  262. // Create a button and handle its click. A Button does not
  263. // know the item it is contained in, so we have to store the
  264. // item ID as user-defined data.
  265. Button detailsField = new Button("show details");
  266. detailsField.setData(itemId);
  267. detailsField.addClickListener(new Button.ClickListener() {
  268. public void buttonClick(ClickEvent event) {
  269. // Get the item identifier from the user-defined data.
  270. Integer iid = (Integer)event.getButton().getData();
  271. Notification.show("Link " +
  272. iid.intValue() + " clicked.");
  273. }
  274. });
  275. detailsField.addStyleName("link");
  276. // Create the table row.
  277. table.addItem(new Object[] {sumField, transferredField,
  278. commentsField, detailsField},
  279. itemId);
  280. }
  281. // Show just three rows because they are so high.
  282. table.setPageLength(3);
  283. ----
  284. See the http://demo.vaadin.com/book-examples-vaadin7/book#component.table.components.components2[on-line example, window="_blank"].
  285. The row height has to be set higher than the default with a style rule such as
  286. the following:
  287. [source, css]
  288. ----
  289. /* Table rows contain three-row TextField components. */
  290. .v-table-components-inside .v-table-cell-content {
  291. height: 54px;
  292. }
  293. ----
  294. See the http://demo.vaadin.com/book-examples-vaadin7/book#component.table.components.components2[on-line example, window="_blank"].
  295. The table will look as shown in <<figure.components.table.components-inside>>.
  296. [[figure.components.table.components-inside]]
  297. .Components in a Table
  298. image::img/table-components.png[width=70%, scaledwidth=100%]
  299. [[components.table.features.iterating]]
  300. === Iterating Over a Table
  301. As the items in a [classname]#Table# are not indexed, iterating over the items
  302. has to be done using an iterator. The [methodname]#getItemIds()# method of the
  303. [classname]#Container# interface of [classname]#Table# returns a
  304. [classname]#Collection# of item identifiers over which you can iterate using an
  305. [classname]#Iterator#. For an example about iterating over a [classname]#Table#,
  306. please see
  307. <<dummy/../../../framework/datamodel/datamodel-container#datamodel.container,"Collecting
  308. Items in Containers">>. Notice that you may not modify the [classname]#Table#
  309. during iteration, that is, add or remove items. Changing the data is allowed.
  310. [[components.table.features.filtering]]
  311. === Filtering Table Contents
  312. A table can be filtered if its container data source implements the
  313. [classname]#Filterable# interface, as the default [classname]#IndexedContainer#
  314. does. See
  315. <<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.filtered,"Filterable
  316. Containers">>. ((("Container",
  317. "Filterable")))
  318. [[components.table.editing]]
  319. == Editing the Values in a Table
  320. Normally, a [classname]#Table# simply displays the items and their fields as
  321. text. If you want to allow the user to edit the values, you can either put them
  322. inside components as we did earlier or simply call
  323. [methodname]#setEditable(true)#, in which case the cells are automatically
  324. turned into editable fields.
  325. Let us begin with a regular table with a some columns with usual Java types,
  326. namely a [classname]#Date#, [classname]#Boolean#, and a [classname]#String#.
  327. [source, java]
  328. ----
  329. // Create a table. It is by default not editable.
  330. Table table = new Table();
  331. // Define the names and data types of columns.
  332. table.addContainerProperty("Date", Date.class, null);
  333. table.addContainerProperty("Work", Boolean.class, null);
  334. table.addContainerProperty("Comments", String.class, null);
  335. ...
  336. ----
  337. You could put the table in editable mode right away. We continue the example by
  338. adding a check box to switch the table between normal and editable modes:
  339. [source, java]
  340. ----
  341. CheckBox editable = new CheckBox("Editable", true);
  342. editable.addValueChangeListener(valueChange -> // Java 8
  343. table.setEditable((Boolean) editable.getValue()));
  344. ----
  345. Now, when you check to checkbox, the components in the table turn into editable
  346. fields, as shown in <<figure.component.table.editable>>.
  347. [[figure.component.table.editable]]
  348. .A Table in Normal and Editable Mode
  349. image::img/table-editable3.png[width=100%, scaledwidth=100%]
  350. [[components.table.editing.fieldfactories]]
  351. === Field Factories
  352. The field components that allow editing the values of particular types in a
  353. table are defined in a field factory that implements the
  354. [classname]#TableFieldFactory# interface. The default implementation is
  355. [classname]#DefaultFieldFactory#, which offers the following crude mappings:
  356. .Type to Field Mappings in [classname]#DefaultFieldFactory#
  357. [options="header",cols="2,5"]
  358. |===============
  359. |Property Type|Mapped to Field Class
  360. |[classname]#Date#|A [classname]#DateField#.
  361. |[classname]#Boolean#|A [classname]#CheckBox#.
  362. |[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#.
  363. |__other__|A [classname]#TextField#. The text field manages conversions from the basic types, if possible.
  364. |===============
  365. Field factories are covered with more detail in
  366. <<dummy/../../../framework/datamodel/datamodel-itembinding#datamodel.itembinding,"Creating
  367. Forms by Binding Fields to Items">>. You could just implement the
  368. [classname]#TableFieldFactory# interface, but we recommend that you extend the
  369. [classname]#DefaultFieldFactory# according to your needs. In the default
  370. implementation, the mappings are defined in the
  371. [methodname]#createFieldByPropertyType()# method (you might want to look at the
  372. source code) both for tables and forms.
  373. ifdef::web[]
  374. [[components.table.editing.navigation]]
  375. === Navigation in Editable Mode
  376. In the editable mode, the editor fields can have focus. Pressing Tab moves the
  377. focus to next column or, at the last column, to the first column of the next
  378. item. Respectively, pressing kbd:[Shift+Tab] moves the focus backward. If the focus is
  379. in the last column of the last visible item, the pressing kbd:[Tab] moves the focus
  380. outside the table. Moving backward from the first column of the first item moves
  381. the focus to the table itself. Some updates to the table, such as changing the
  382. headers or footers or regenerating a column, can move the focus from an editor
  383. component to the table itself.
  384. The default behaviour may be undesirable in many cases. For example, the focus
  385. also goes through any read-only editor fields and can move out of the table
  386. inappropriately. You can provide better navigation is to use event handler for
  387. shortcut keys such as kbd:[Tab], kbd:[Arrow Up], kbd:[Arrow Down], and kbd:[Enter].
  388. [source, java]
  389. ----
  390. // Keyboard navigation
  391. class KbdHandler implements Handler {
  392. Action tab_next = new ShortcutAction("Tab",
  393. ShortcutAction.KeyCode.TAB, null);
  394. Action tab_prev = new ShortcutAction("Shift+Tab",
  395. ShortcutAction.KeyCode.TAB,
  396. new int[] {ShortcutAction.ModifierKey.SHIFT});
  397. Action cur_down = new ShortcutAction("Down",
  398. ShortcutAction.KeyCode.ARROW_DOWN, null);
  399. Action cur_up = new ShortcutAction("Up",
  400. ShortcutAction.KeyCode.ARROW_UP, null);
  401. Action enter = new ShortcutAction("Enter",
  402. ShortcutAction.KeyCode.ENTER, null);
  403. public Action[] getActions(Object target, Object sender) {
  404. return new Action[] {tab_next, tab_prev, cur_down,
  405. cur_up, enter};
  406. }
  407. public void handleAction(Action action, Object sender,
  408. Object target) {
  409. if (target instanceof TextField) {
  410. // Move according to keypress
  411. int itemid = (Integer) ((TextField) target).getData();
  412. if (action == tab_next || action == cur_down)
  413. itemid++;
  414. else if (action == tab_prev || action == cur_up)
  415. itemid--;
  416. // On enter, just stay where you were. If we did
  417. // not catch the enter action, the focus would be
  418. // moved to wrong place.
  419. if (itemid >= 0 && itemid < table.size()) {
  420. TextField newTF = valueFields.get(itemid);
  421. if (newTF != null)
  422. newTF.focus();
  423. }
  424. }
  425. }
  426. }
  427. // Panel that handles keyboard navigation
  428. Panel navigator = new Panel();
  429. navigator.addStyleName(Reindeer.PANEL_LIGHT);
  430. navigator.addComponent(table);
  431. navigator.addActionHandler(new KbdHandler());
  432. ----
  433. The main issue in implementing keyboard navigation in an editable table is that
  434. the editor fields do not know the table they are in. To find the parent table,
  435. you can either look up in the component container hierarchy or simply store a
  436. reference to the table with [methodname]#setData()# in the field component. The
  437. other issue is that you can not acquire a reference to an editor field from the
  438. [classname]#Table# component. One solution is to use some external collection,
  439. such as a [classname]#HashMap#, to map item IDs to the editor fields.
  440. [source, java]
  441. ----
  442. // Can't access the editable components from the table so
  443. // must store the information
  444. final HashMap<Integer,TextField> valueFields =
  445. new HashMap<Integer,TextField>();
  446. ----
  447. The map has to be filled in a [classname]#TableFieldFactory#, such as in the
  448. following. You also need to set the reference to the table there and you can
  449. also set the initial focus there.
  450. [source, java]
  451. ----
  452. table.setTableFieldFactory(new TableFieldFactory () {
  453. public Field createField(Container container, Object itemId,
  454. Object propertyId, Component uiContext) {
  455. TextField field = new TextField((String) propertyId);
  456. // User can only edit the numeric column
  457. if ("Source of Fear".equals(propertyId))
  458. field.setReadOnly(true);
  459. else { // The numeric column
  460. // The field needs to know the item it is in
  461. field.setData(itemId);
  462. // Remember the field
  463. valueFields.put((Integer) itemId, field);
  464. // Focus the first editable value
  465. if (((Integer)itemId) == 0)
  466. field.focus();
  467. }
  468. return field;
  469. }
  470. });
  471. ----
  472. The issues are complicated by the fact that the editor fields are not generated
  473. for the entire table, but only for a cache window that includes the visible
  474. items and some items above and below it. For example, if the beginning of a big
  475. scrollable table is visible, the editor component for the last item does not
  476. exist. This issue is relevant mostly if you want to have wrap-around navigation
  477. that jumps from the last to first item and vice versa.
  478. endif::web[]
  479. [[components.table.headersfooters]]
  480. == Column Headers and Footers
  481. [classname]#Table# supports both column headers and footers; the headers are
  482. enabled by default.
  483. [[components.table.headersfooters.headers]]
  484. === Headers
  485. The table header displays the column headers at the top of the table. You can
  486. use the column headers to reorder or resize the columns, as described earlier.
  487. By default, the header of a column is the property ID of the column, unless
  488. given explicitly with [methodname]#setColumnHeader()#.
  489. [source, java]
  490. ----
  491. // Define the properties
  492. table.addContainerProperty("lastname", String.class, null);
  493. table.addContainerProperty("born", Integer.class, null);
  494. table.addContainerProperty("died", Integer.class, null);
  495. // Set nicer header names
  496. table.setColumnHeader("lastname", "Name");
  497. table.setColumnHeader("born", "Born");
  498. table.setColumnHeader("died", "Died");
  499. ----
  500. The text of the column headers and the visibility of the header depends on the
  501. __column header mode__. The header is visible by default, but you can disable it
  502. with [methodname]#setColumnHeaderMode(Table.COLUMN_HEADER_MODE_HIDDEN)#.
  503. [[components.table.headersfooters.footers]]
  504. === Footers
  505. The table footer can be useful for displaying sums or averages of values in a
  506. column, and so on. The footer is not visible by default; you can enable it with
  507. [methodname]#setFooterVisible(true)#. Unlike in the header, the column headers
  508. are empty by default. You can set their value with
  509. [methodname]#setColumnFooter()#. The columns are identified by their property
  510. ID.
  511. The following example shows how to calculate average of the values in a column:
  512. [source, java]
  513. ----
  514. // Have a table with a numeric column
  515. Table table = new Table("Custom Table Footer");
  516. table.addContainerProperty("Name", String.class, null);
  517. table.addContainerProperty("Died At Age", Integer.class, null);
  518. // Insert some data
  519. Object people[][] = { {"Galileo", 77},
  520. {"Monnier", 83},
  521. {"Vaisala", 79},
  522. {"Oterma", 86}};
  523. for (int i=0; i<people.length; i++)
  524. table.addItem(people[i], new Integer(i));
  525. // Calculate the average of the numeric column
  526. double avgAge = 0;
  527. for (int i=0; i<people.length; i++)
  528. avgAge += (Integer) people[i][1];
  529. avgAge /= people.length;
  530. // Set the footers
  531. table.setFooterVisible(true);
  532. table.setColumnFooter("Name", "Average");
  533. table.setColumnFooter("Died At Age", String.valueOf(avgAge));
  534. // Adjust the table height a bit
  535. table.setPageLength(table.size());
  536. ----
  537. The resulting table is shown in
  538. <<figure.components.table.headersfooters.footer>>.
  539. [[figure.components.table.headersfooters.footer]]
  540. .A Table with a Footer
  541. image::img/table-footer.png[width=25%, scaledwidth=40%]
  542. [[components.table.headersfooters.clicks]]
  543. === Handling Mouse Clicks on Headers and Footers
  544. Normally, when the user clicks a column header, the table will be sorted by the
  545. column, assuming that the data source is [classname]#Sortable# and sorting is
  546. not disabled. In some cases, you might want some other functionality when the
  547. user clicks the column header, such as selecting the column in some way.
  548. Clicks in the header cause a [classname]#HeaderClickEvent#, which you can handle
  549. with a [classname]#Table.HeaderClickListener#. Click events on the table header
  550. (and footer) are, like button clicks, sent immediately to server, so there is no
  551. need to set [methodname]#setImmediate()#.
  552. [source, java]
  553. ----
  554. // Handle the header clicks
  555. table.addHeaderClickListener(new Table.HeaderClickListener() {
  556. public void headerClick(HeaderClickEvent event) {
  557. String column = (String) event.getPropertyId();
  558. Notification.show("Clicked " + column +
  559. "with " + event.getButtonName());
  560. }
  561. });
  562. // Disable the default sorting behavior
  563. table.setSortDisabled(true);
  564. ----
  565. Setting a click handler does not automatically disable the sorting behavior of
  566. the header; you need to disable it explicitly with
  567. [methodname]#setSortDisabled(true)#. Header click events are not sent when the
  568. user clicks the column resize handlers to drag them.
  569. The [classname]#HeaderClickEvent# object provides the identity of the clicked
  570. column with [methodname]#getPropertyId()#. The [methodname]#getButton()# reports
  571. the mouse button with which the click was made: [parameter]#BUTTON_LEFT#,
  572. [parameter]#BUTTON_RIGHT#, or [parameter]#BUTTON_MIDDLE#. The
  573. [methodname]#getButtonName()# a human-readable button name in English: "
  574. [parameter]#left#", " [parameter]#right#", or " [parameter]#middle#". The
  575. [methodname]#isShiftKey()#, [methodname]#isCtrlKey()#, etc., methods indicate if
  576. the kbd:[Shift], kbd:[Ctrl], kbd:[Alt] or other modifier keys were pressed during the click.
  577. Clicks in the footer cause a [classname]#FooterClickEvent#, which you can handle
  578. with a [classname]#Table.FooterClickListener#. Footers do not have any default
  579. click behavior, like the sorting in the header. Otherwise, handling clicks in
  580. the footer is equivalent to handling clicks in the header.
  581. [[components.table.columngenerator]]
  582. == Generated Table Columns
  583. A table can have generated columns which values can be calculated based on the
  584. values in other columns. The columns are generated with a class implementing the
  585. [interfacename]#Table.ColumnGenerator# interface.
  586. The [classname]#GeneratedPropertyContainer# described in
  587. <<dummy/../../../framework/datamodel/datamodel-container#datamodel.container.gpc,"GeneratedPropertyContainer">>
  588. is another way to accomplish the same task at container level. In addition to
  589. generating values, you can also use the feature for formatting or styling
  590. columns.
  591. ifdef::web[]
  592. [[components.table.columngenerator.generator]]
  593. === Defining a Column Generator
  594. Column generators are objects that implement the
  595. [classname]#Table.ColumnGenerator# interface and its
  596. [methodname]#generateCell()# method. The method gets the identity of the item
  597. and column as its parameters, in addition to the table object, and has to return
  598. a component. The interface is functional, so you can also define it by a lambda
  599. expression or a method reference in Java 8.
  600. The following example defines a generator for formatting [classname]#Double#
  601. valued fields according to a format string (as in
  602. [classname]#java.util.Formatter#).
  603. [source, java]
  604. ----
  605. /** Formats the value in a column containing Double objects. */
  606. class ValueColumnGenerator implements Table.ColumnGenerator {
  607. String format; /* Format string for the Double values. */
  608. /**
  609. * Creates double value column formatter with the given
  610. * format string.
  611. */
  612. public ValueColumnGenerator(String format) {
  613. this.format = format;
  614. }
  615. /**
  616. * Generates the cell containing the Double value.
  617. * The column is irrelevant in this use case.
  618. */
  619. public Component generateCell(Table source, Object itemId,
  620. Object columnId) {
  621. // Get the object stored in the cell as a property
  622. Property prop =
  623. source.getItem(itemId).getItemProperty(columnId);
  624. if (prop.getType().equals(Double.class)) {
  625. Label label = new Label(String.format(format,
  626. new Object[] { (Double) prop.getValue() }));
  627. // Set styles for the column: one indicating that it's
  628. // a value and a more specific one with the column
  629. // name in it. This assumes that the column name
  630. // is proper for CSS.
  631. label.addStyleName("column-type-value");
  632. label.addStyleName("column-" + (String) columnId);
  633. return label;
  634. }
  635. return null;
  636. }
  637. }
  638. ----
  639. The column generator is called for all the visible (or more accurately cached)
  640. items in a table. If the user scrolls the table to another position in the
  641. table, the columns of the new visible rows are generated dynamically.
  642. Generated column cells are automatically updated when a property value in the
  643. table row changes. Note that a generated cell, even if it is a field, does not
  644. normally have a property value bound to the table's container, so changes in
  645. generated columns do not trigger updates in other generated columns. It should
  646. also be noted that if a generated column cell depends on values in other rows,
  647. changes in the other rows do not trigger automatic update. You can get notified
  648. of such value changes by listening for them with a
  649. [interfacename]#ValueChangeListener# in the generated components. If you do so,
  650. you must remove such listeners when the generated components are detached from
  651. the UI or otherwise the listeners will accumulate in the container when the
  652. table is scrolled back and forth, causing possibly severe memory leak.
  653. endif::web[]
  654. ifdef::web[]
  655. [[components.table.columngenerator.adding]]
  656. === Adding Generated Columns
  657. You add new generated columns to a [classname]#Table# with
  658. [methodname]#addGeneratedColumn()#. It takes a property ID of the generated
  659. column as the first parameter and the generator as the second.
  660. [source, java]
  661. ----
  662. // Define the generated columns and their generators
  663. table.addGeneratedColumn("date", // Java 8:
  664. this::generateNonEditableCell);
  665. table.addGeneratedColumn("price",
  666. new PriceColumnGenerator());
  667. table.addGeneratedColumn("consumption",
  668. new ConsumptionColumnGenerator());
  669. table.addGeneratedColumn("dailycost",
  670. new DailyCostColumnGenerator());
  671. ----
  672. Notice that the [methodname]#addGeneratedColumn()# always places the generated
  673. columns as the last column, even if you defined some other order previously. You
  674. will have to set the proper order with [methodname]#setVisibleColumns()#.
  675. [source, java]
  676. ----
  677. table.setVisibleColumns("date", "quantity", "price", "total");
  678. ----
  679. endif::web[]
  680. ifdef::web[]
  681. [[components.table.columngenerator.editable]]
  682. === Generators in Editable Table
  683. When you set a table as [parameter]#editable#, table cells change to editable
  684. fields. When the user changes the values in the fields, the generated cells in
  685. the same row are updated automatically. However, putting a table with generated
  686. columns in editable mode has a few quirks. One is that the editable mode does
  687. not affect generated columns. You have two alternatives: either you generate the
  688. editing fields in the generator or, in case of formatter generators, remove the
  689. generators in the editable mode to allow editing the values. The following
  690. example uses the latter approach.
  691. [source, java]
  692. ----
  693. // Have a check box that allows the user
  694. // to make the quantity and total columns editable.
  695. final CheckBox editable = new CheckBox(
  696. "Edit the input values - calculated columns are regenerated");
  697. editable.setImmediate(true);
  698. editable.addClickListener(new ClickListener() {
  699. public void buttonClick(ClickEvent event) {
  700. table.setEditable(editable.booleanValue());
  701. // The columns may not be generated when we want to
  702. // have them editable.
  703. if (editable.booleanValue()) {
  704. table.removeGeneratedColumn("quantity");
  705. table.removeGeneratedColumn("total");
  706. } else { // Not editable
  707. // Show the formatted values.
  708. table.addGeneratedColumn("quantity",
  709. new ValueColumnGenerator("%.2f l"));
  710. table.addGeneratedColumn("total",
  711. new ValueColumnGenerator("%.2f e"));
  712. }
  713. // The visible columns are affected by removal
  714. // and addition of generated columns so we have
  715. // to redefine them.
  716. table.setVisibleColumns("date", "quantity",
  717. "price", "total", "consumption", "dailycost");
  718. }
  719. });
  720. ----
  721. You will also have to set the editing fields in [parameter]#immediate# mode to
  722. have the update occur immediately when an edit field loses the focus. You can
  723. set the fields in [parameter]#immediate# mode with the a custom
  724. [classname]#TableFieldFactory#, such as the one given below, that just extends
  725. the default implementation to set the mode:
  726. [source, java]
  727. ----
  728. public class ImmediateFieldFactory extends DefaultFieldFactory {
  729. public Field createField(Container container,
  730. Object itemId,
  731. Object propertyId,
  732. Component uiContext) {
  733. // Let the DefaultFieldFactory create the fields...
  734. Field field = super.createField(container, itemId,
  735. propertyId, uiContext);
  736. // ...and just set them as immediate.
  737. ((AbstractField)field).setImmediate(true);
  738. return field;
  739. }
  740. }
  741. ...
  742. table.setTableFieldFactory(new ImmediateFieldFactory());
  743. ----
  744. If you generate the editing fields with the column generator, you avoid having
  745. to use such a field factory, but of course have to generate the fields for both
  746. normal and editable modes.
  747. <<figure.ui.table.generated>> shows a table with columns calculated (blue) and
  748. simply formatted (black) with column generators.
  749. [[figure.ui.table.generated]]
  750. .Table with generated columns
  751. image::img/table-generatedcolumns1.png[width=90%, scaledwidth=100%]
  752. endif::web[]
  753. [[components.table.columnformatting]]
  754. == Formatting Table Columns
  755. The displayed values of properties shown in a table are normally formatted using
  756. the [methodname]#toString()# method of each property. Customizing the format in
  757. a table column can be done in several ways:
  758. * Using [classname]#ColumnGenerator# to generate a second column that is formatted. The original column needs to be set invisible. See <<components.table.columngenerator>>.
  759. * Using a [classname]#Converter# to convert between the property data model and its representation in the table.
  760. * Using a [classname]#GeneratedPropertyContainer# as a wrapper around the actual container to provide formatting.
  761. * Overriding the default [methodname]#formatPropertyValue()# in [classname]#Table#.
  762. As using a [classname]#PropertyFormatter# is generally much more awkward than
  763. overriding the [methodname]#formatPropertyValue()#, its use is not described
  764. here.
  765. You can override [methodname]#formatPropertyValue()# as is done in the following
  766. example:
  767. [source, java]
  768. ----
  769. // Create a table that overrides the default
  770. // property (column) format
  771. final Table table = new Table("Formatted Table") {
  772. @Override
  773. protected String formatPropertyValue(Object rowId,
  774. Object colId, Property property) {
  775. // Format by property type
  776. if (property.getType() == Date.class) {
  777. SimpleDateFormat df =
  778. new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
  779. return df.format((Date)property.getValue());
  780. }
  781. return super.formatPropertyValue(rowId, colId, property);
  782. }
  783. };
  784. // The table has some columns
  785. table.addContainerProperty("Time", Date.class, null);
  786. ... Fill the table with data ...
  787. ----
  788. You can also distinguish between columns by the [parameter]#colId# parameter,
  789. which is the property ID of the column. [classname]#DecimalFormat# is useful for
  790. formatting decimal values.
  791. [source, java]
  792. ----
  793. ... in formatPropertyValue() ...
  794. } else if ("Value".equals(pid)) {
  795. // Format a decimal value for a specific locale
  796. DecimalFormat df = new DecimalFormat("#.00",
  797. new DecimalFormatSymbols(locale));
  798. return df.format((Double) property.getValue());
  799. }
  800. ...
  801. table.addContainerProperty("Value", Double.class, null);
  802. ----
  803. A table with the formatted date and decimal value columns is shown in
  804. <<figure.components.table.columnformatting>>.
  805. [[figure.components.table.columnformatting]]
  806. .Formatted Table columns
  807. image::img/table-columnformatting.png[width=40%, scaledwidth=50%]
  808. You can use CSS for further styling of table rows, columns, and individual cells by using a [classname]#CellStyleGenerator#.
  809. ifdef::web[It is described in <<components.table.css>>.]
  810. [[components.table.css]]
  811. == CSS Style Rules
  812. [source, css]
  813. ----
  814. .v-table {}
  815. .v-table-header-wrap {}
  816. .v-table-header {}
  817. .v-table-header-cell {}
  818. .v-table-resizer {} /* Column resizer handle. */
  819. .v-table-caption-container {}
  820. .v-table-body {}
  821. .v-table-row-spacer {}
  822. .v-table-table {}
  823. .v-table-row {}
  824. .v-table-cell-content {}
  825. ----
  826. Notice that some of the widths and heights in a table are calculated dynamically
  827. and can not be set in CSS.
  828. ifdef::web[]
  829. [[components.table.css.cellstylegenerator]]
  830. === Generating Cell Styles With [interfacename]#CellStyleGenerator#
  831. The [classname]#Table.CellStyleGenerator# interface allows you to set the CSS
  832. style for each individual cell in a table. You need to implement the
  833. [methodname]#getStyle()#, which gets the row (item) and column (property)
  834. identifiers as parameters and can return a style name for the cell. The returned
  835. style name will be concatenated to prefix "
  836. [literal]#++v-table-cell-content-++#".
  837. The [methodname]#getStyle()# is called also for each row, so that the
  838. [parameter]#propertyId# parameter is [literal]#++null++#. This allows setting a
  839. row style.
  840. Alternatively, you can use a [classname]#Table.ColumnGenerator# (see
  841. <<components.table.columngenerator>>) to generate the actual UI components of
  842. the cells and add style names to them.
  843. [source, java]
  844. ----
  845. Table table = new Table("Table with Cell Styles");
  846. table.addStyleName("checkerboard");
  847. // Add some columns in the table. In this example, the property
  848. // IDs of the container are integers so we can determine the
  849. // column number easily.
  850. table.addContainerProperty("0", String.class, null, "", null, null);
  851. for (int i=0; i<8; i++)
  852. table.addContainerProperty(""+(i+1), String.class, null,
  853. String.valueOf((char) (65+i)), null, null);
  854. // Add some items in the table.
  855. table.addItem(new Object[]{
  856. "1", "R", "N", "B", "Q", "K", "B", "N", "R"}, new Integer(0));
  857. table.addItem(new Object[]{
  858. "2", "P", "P", "P", "P", "P", "P", "P", "P"}, new Integer(1));
  859. for (int i=2; i<6; i++)
  860. table.addItem(new Object[]{String.valueOf(i+1),
  861. "", "", "", "", "", "", "", ""}, new Integer(i));
  862. table.addItem(new Object[]{
  863. "7", "P", "P", "P", "P", "P", "P", "P", "P"}, new Integer(6));
  864. table.addItem(new Object[]{
  865. "8", "R", "N", "B", "Q", "K", "B", "N", "R"}, new Integer(7));
  866. table.setPageLength(8);
  867. // Set cell style generator
  868. table.setCellStyleGenerator(new Table.CellStyleGenerator() {
  869. public String getStyle(Object itemId, Object propertyId) {
  870. // Row style setting, not relevant in this example.
  871. if (propertyId == null)
  872. return "green"; // Will not actually be visible
  873. int row = ((Integer)itemId).intValue();
  874. int col = Integer.parseInt((String)propertyId);
  875. // The first column.
  876. if (col == 0)
  877. return "rowheader";
  878. // Other cells.
  879. if ((row+col)%2 == 0)
  880. return "black";
  881. else
  882. return "white";
  883. }
  884. });
  885. ----
  886. You can then style the cells, for example, as follows:
  887. [source, css]
  888. ----
  889. /* Center the text in header. */
  890. .v-table-header-cell {
  891. text-align: center;
  892. }
  893. /* Basic style for all cells. */
  894. .v-table-checkerboard .v-table-cell-content {
  895. text-align: center;
  896. vertical-align: middle;
  897. padding-top: 12px;
  898. width: 20px;
  899. height: 28px;
  900. }
  901. /* Style specifically for the row header cells. */
  902. .v-table-cell-content-rowheader {
  903. background: #E7EDF3
  904. url(../default/table/img/header-bg.png) repeat-x scroll 0 0;
  905. }
  906. /* Style specifically for the "white" cells. */
  907. .v-table-cell-content-white {
  908. background: white;
  909. color: black;
  910. }
  911. /* Style specifically for the "black" cells. */
  912. .v-table-cell-content-black {
  913. background: black;
  914. color: white;
  915. }
  916. ----
  917. The table will look as shown in <<figure.components.table.cell-style>>.
  918. [[figure.components.table.cell-style]]
  919. .Cell style generator for a Table
  920. image::img/table-cellstylegenerator1.png[width=50%, scaledwidth=80%]
  921. endif::web[]
  922. endif::vaadin7[]
  923. (((range="endofrange", startref="term.components.table")))