Sign in
Screen Component: Data Table (for 5.3)
Created on: May 31, 2013 | Last modified: June 9, 2016 | Version: 5.3
Important: This document refers to the Data Table for Appway 5.3.

If you are working with Appway 6.2+ please consult the following updated version: https://developer.appway.com/screen/ViewDocument/bookId/3eccfbc7-cfcb-4a01-8c6b-352306c341a3
1Introduction
1.1Description
The Data Table is a Screen component to display data in a table. It allows data sorting and filtering with minimal resource requirements. It is available starting with Appway 5.3.
The following document uses a list of movie titles as an example of how to set up a Data Table. The table provides four columns, one of which is an action column. The action column allows users to execute certain functions on a movie. The table is sortable and filterable. By clicking on a row, the table displays an interline revealing more detailed information about each movie.

To make it easier for you to follow this document, you can download and import the business objects that are created and used in the examples.
1.2Components
A Data Table is composed of three different screen components, giving you maximum flexibility for configuring your table. Each component is responsible for a specific part of the table.
1.2.1Data Table
The Data Table component itself is the root component of a Data Table. It defines a data source and the overall rendering behavior. You can for example configure if the status bar is visible or not.

The Data Table also defines which screen to render as the interlining content of the table – and whether to display an interline at all.
1.2.2Data Column
The Data Column component is a direct child of a Data Table. For each column you want the table to render, you have to add a Data Column to the Data Table. On each column you can configure its width, a header label and some styling. Most importantly, you can set up Filters and Sorters on this component.
1.2.3Data Content
A Data Column does not yet define any cell content in itself: this is the role of a third component called Data Content. The Data Content element defines which portion of data provided by the table's data source is displayed in a cell. Each Data Content can have its own text and/or icon, styling and actions. This way, you are able to create content for a cell that has multiple parts, like two icons and a text.
Of course, actions can be applied to whole rows, cells or content only. Please refer to Chapter 3 "Actions" below.
2Data Source
In order to display data, we have to set up a data source for the Data Table. The data source is configured in the Data Table component and is usually the first thing you do when creating a new table. There are three kinds of data sources you can choose from: Catalog, Expression or Custom Model.
2.1Catalog
Using a catalog is the simplest way of setting up a data source for a data table. In our movie table example, we use a static catalog that defines a column for each movie property.

In the Screen Editor, drag a Data Table to the display area and choose "Catalog" as the "Source". Then, use the "Source Catalog" dropdown to look for the respective Catalog you want to use. An important step is to set the "Row Object Variable". It defines the name of the variable which will hold a reference to the catalog record processed when rendering a row in the Data Table. You will for example use this variable when setting up cell content or when defining actions for a specific row.

The next step is to add a Data Column for each column you want to display in the table. The amount of columns or their semantic meaning do not have to match the columns of the catalog. You can create any columns you want by using the catalog's data and creating a column out of it.
For example, if you have a table of users, it is easy to create a column that shows each user's full name (first name and last name) even though the underlying user object may not contain a property for that. You simply concatenate the user's first and last name within a single column.

For the moment it is enough to merely set the label for each column. A standard filter and a standard setter are automatically attached to each column. The columns' width is also set automatically. You can tweak this later.
Next, you need to tell the table what it should display in the columns by adding a Data Content element to each column. It has a property called "Text", in which we can now use the "Row Object Variable." You previously defined this variable's name in the Data Table component.
Each time a row is rendered, all the attached Data Content components will be rendered. This means that what you define as a Data Content's text property will be written to the cells of its column. Going back to our example, if we want to fill the first column with movie titles, we access the title that is stored in each catalog record. We now use the Row Object Variable "$movie" which, in the case of a catalog, represents the record currently processed. To access the title column of this record, we use the function $movie.getValue('title").

Repeating this for each column, the result is a table that shows all of the movies defined in the catalog as follows:

2.2Expression
Instead of a Catalog, you can use an Expression as a data source. It returns a collection of custom defined objects rather than Records. For our movie example, we define a Data Class "Movie" which looks as follows:

Let us assume that there is an indexed collection of the "Movies" data class within our process. It is the same movies that we had in the catalog above. And we want to access this collection in order to display the movies in a table.
What we now have to do is set the Data Table's source to "Expression." This will show a property called "Source Collection." We set it so that it points to the indexed collection of movies just mentioned. As in the catalog setup, we define a Row Object Variable. But in the expression case, we also have to set this variable's type and collection type since Appway does not automatically know which is the row object's type. We thus set the Row Object Type property to "Movie" and the Row Object Collection to "Single Element": each object in our collection is of type "Movie" and is a single element.

Since we have instances of "Movie" instead of accessing catalog records, we cannot use $movie.getValue() to get the values for the Data Content. Instead, the data class "Movie" provides properties for each value. We set the text property of each Data Content to $movie.title, $movie.year and $movie.genre respectively, as depicted in the picture above.
What we end up with is the same table as the one we build in the previous section, using a catalog as the data source. Instead, this table accesses data using an expression collection.

2.3Custom Model
The Custom Model was deprecated in Appway 6.2 and has been removed in Appway 6.3, so it should not be used anymore. See the following link to a Forum Thread that explains the situation: https://developer.appway.com/screen/ForumDisplaySingleThread/selectedThreadId/1454013685946
If you have a very large amount of data, you will achieve the best performance by creating your own data model. This means that you are in charge of sorting, filtering and fetching the data for your table by providing a data class that follows a certain API. A custom model does not configure any columns, it only serves row objects, which can be used in the same way you would when configuring a catalog or an expression as the data source.
A Data Table is never fully rendered. Instead, only the relevant rows that the user currently sees are rendered. As the user scrolls, more rows will be fetched from the server and displayed in the view. This makes it possible to display a table with an unlimited amount of rows, as long as the server is capable of handling this amount.
In order to guarantee a smooth experience to the user, it is important to have a very fast server for sorting, filtering and fetching data. Appway gives you the possibility to optimize these actions by letting you create your own data models. A custom data model is a data class which provides the following functions: fetch(), filter(), and sort().
2.3.1The fetch() Function
Function fetch(DataTableResultSet $rs, Integer $start, Integer $limit) : Nothing
Every time the data table loads rows from the server, it calls the fetch() function on the model. Fetch does not return anything. Instead, it fills the given DataTableResultSet with row objects. Remember that a data table model does not fill columns, it only serves row objects that can be used to create columns in the screen editor.
The more or less tricky part about the fetch() function is that it has to fill the correct row objects into the result set. The Data Table tells you which of the rows have to be fetched using the two other parameters ($start and $limit). $start is the index of the first row that has to be filled into the result set. Be aware that row indices start with 0 (zero). $limit tells us how many rows have to be filled in at most. It is perfectly possible that the data table requests more rows than there actually are. It is the responsibility of the model to handle this.
A very important role the function fetch() also has is to set the current amount of filtered (and thus visible) rows as well as the current total amount of rows. The Data Table needs this to know how to render the vertical scrollbar. It is also necessary to display the numbers in the Data Table's status bar. The result set offers two functions to set these values.
$rs.setVisibleCount(Integer $visibleCount);
$rs.setTotalCount(Integer $totalCount);
2.3.2The filter() Function
Function filter(Indexed DataColumnModel $columns) : Nothing
Whenever a column's filter value changes, the data table asks the server to filter the data, and then refetches the data. The Data Table provides the function with a $columns parameter. This indexed collection of DataColumnModels holds all columns that currently have an active filter. It is therefore possible to call the function with an empty collection, which means that all filters have been reset.
Here it is completely up to the developer to decide whether it is better to filter data directly in this function or if to store the collection of filtered columns and filter the data when it is fetched. For example, this is useful when the data is gathered from a SQL database. In this case, it is possible to create a query and use it in fetch().
2.3.3The sort() Function
Function sort(Indexed DataColumnModel $columns) : Nothing
The sort() function behaves exactly like filter(). But in this case, a sorted collection of columns is given, either ascending or descending. Similar to the filter function, the given collection can be empty, which would mean that no column is currently sorted.
Beware: the order of the given columns matters. Since the user is able to sort a data table's multiple columns of holding the shift key, it is also important that the data be sorted in that order of relevance.
2.3.4Example
The following example uses the same data as the Catalog and Expression data sources, but this time, we create custom code for the model using the expression language. We have to provide a data class that acts as our Custom Data Model. We call it "MovieDataModel."
We create three properties for the data class. One is a collection of all the movies there are. Another is a collection of the filtered movies. A third is a boolean that will tell whether the table is currently filtered or not. Additionnally we define the three necessary functions fetch(), filter() and sort().

In the fetch function, we first decide what collection of movies to use. It is either the filtered collection or the collection of all movies. We then use direct access to the row with the start index and loop through the movies until we either reach the end of the collection or the limit provided by the Data Table. Each movie within the loop is added to the result set using the function $rs.row(). Finally, we provide the current visible count of movies and the overall count of movies to the result set.
1Function fetch(DataTableResultSet $rs, Integer $start, Integer $limit) : Nothing Begin 
2   Indexed Movie $movies := IF($this.isFiltered, $this.filteredMovies, $this.allMovies);
3   Integer $i;
4   Integer $max := MIN($start + $limit, $movies.size());
5   For $i := $start Condition $i < $max Step $i := $i + 1 Do 
6      $rs.row($movies.getElement($i + 1));
7   End
8   $rs.setVisibleCount($movies.size());
9   $rs.setTotalCount($this.allMovies.size());
10End
The filter function will set the property isFiltered to true or false, depending on whether there are filtered columns or not. After that, we instantiate a new collection of filtered movies that is filled with movies in the loop that follows. We loop through each movie and check if a filter value of the given columns results in hiding the movie. Each cell provides an index for the row object that is used to decide whether or not the row will be visible. Please refer to section "Filters" for more information about the filter index.
1Function filter(Indexed DataColumnModel $columns) : Nothing Begin 
2   $this.isFiltered := $columns.size() > 0;
3   $this.filteredMovies := NewIndexed(Movie);
4   If $this.isFiltered Then 
5      ForEach $movie In $this.allMovies Do 
6         Boolean $visible := true;
7         ForEach $column In $columns Do 
8            Any $index := $column.getCellFilterIndex($movie);
9            If ! $column.getFilter().isVisible($index) Then 
10               $visible := false;
11               Break;
12            End
13         End
14         If $visible Then 
15            $this.filteredMovies.addElement($movie);
16         End
17      End
18   End
19End
Appway provides a convenience function for deciding whether a row is visible according to the filter values. The function is called IsDataRowVisible($rowObject, $columns). Using this function will shorten the code and execute faster.
1Function filter(Indexed DataColumnModel $columns) : Nothing Begin 
2   Indexed DataColumnModel $cols := $columns;
3   $this.isFiltered := $col.size() > 0;
4   $this.filteredMovies := NewIndexed(Movie);
5   If $this.isFiltered Then 
6      ForEach $movie In $this.allMovies Do 
7         If IsDataRowVisible($movie, $cols) Then 
8            $this.filteredMovies.addElement($movie);
9         End
10      End
11   End
12End
For the sort function(), we have the same possibilities as for filter(). There is also a convenience function provided by Appway called SortDataRows($collection, $columns). It will sort the given collection according to the sort states given by the columns.
1Function sort(Indexed DataColumnModel $columns) : Nothing Begin 
2   Indexed DataColumnModel $cols := $columns;
3   SortDataRows($this.filteredMovies, $cols);
4   SortDataRows($this.allMovies, $cols);
5End
Back in the screen editor, we now use this model for building the data table. We set the Source property to "Custom Model" and provide a reference to an instance of our MovieDataModel in the "Source Custom Model" property. The rest of the properties as well as the properties for the Data Columns and the Data Contents stay the same since we still work with the same row objects, which are of type Movie.

The model itself is instantiated in the process and given to the screen using an assigned variable.
3Actions
In a Data Table, it is possible to add actions to rows, cells or content within a cell.
3.1On Rows
Actions on rows are defined on the Data Table component itself. Use the Data Table's Actions tab. Within such an action you can use the row object variable.
3.2On Cells
Actions on cells are defined on Data Column components. Use the Data Column's Actions tab. Within such an action you can use the row object variable. If you define an action in this way, an action available on a row will not be triggered if the user executes the action on the cell.
3.3On Content
A cell's individual content can have its own actions. If you place multiple Data Content components into a cell, you can set different actions for each of the content elements. If you define an action in this way, an action available on a row or cell will not be triggered if the user executes the action on the content element.
3.4Example
In our movie example, we want to create an extra column that shows two icons. These icons must be action triggers for deleting and editing. We thus add another Data Column to the data table and place two Data Content elements in this column. For each of the Data Content elements, we set an icon according to the action we want to provide. We use the Icon tab for this.

Now that we have the two icons, we can set actions on them. We use the Actions tab on each of the Data Content elements and add actions that fit the desired functionality. As already mentioned you can also access the row object variable when using the expression language within actions.

The table will now render as follows. As you can see, the columns are equally sized and the newly created column has a filter and a sorter, which are not necessary. Please refer to the chapters "Filters", "Sorters" and "Layout" to see how you can tweak the rendering.

4Filters
Each column has its own filter. There are different types of filters, depending on the column data and what the user interface should render. You can choose between a basic text field, a custom drop-down and a boolean filter.

Note: More filter types, such as a data time chooser, are planned for future releases.
If a column should not be filterable, set the type of the filter to none. This will not render any input and it will therefore not be possible for the user to enter anything.

Each type of filter has three properties: Value, Index, and Config.
4.1The Value Property
The "Value" defines a predefined filter value that will bet set at the time the data table will be rendered. This way, you can set what the user will see when he/she first renders the table. Of course the user can change that value later.
4.2The Index Property
The "Index" is the value of the cell. This index is used to match filter values in order to decide whether a row is visible or not. This property has to be defined only if it is not the same as the text displayed in the cell.
If you use a text filter, you will probably not set any index value. However, if for example a Boolean filter is in use, it needs to match rows for which the cells are either true of false. For this kind of filter, you have to convert whatever data is in the cell to a boolean.
Take the example of a table of users. One column represents users' role: administrator or regular user. In this column, the text displayed is either "Administrator" or "User". Both of these string values do not parse to true. It is therefore here necessary to specify an index that will transform our data into a valid boolean, or a String that parses as a boolean. The expression for this could look as follows:
1$user.getType() == 'Administrator'
Performance tip: Whenever you use a non-text filter without specifying an index, the framework will try to parse the cell text to the desired type. If you have a fast way of providing the value as the desired type (for example if you have it as a property), setting it as the index will result in better performance.
4.3The Config Property
A configuration is only necessary if a filter requires one. Use it to set up special behavior for filters that go beyond a simple input field. To configure a filter, a special ConfigSet type is available. A ConfigSet is an ordered collection of Key/Value pairs with which you can define individual settings for each filter. The following sections contain more information about the configuration specifications for each filter.
4.3.1Textfield
A textfield filter does not evaluate any configuration.
4.3.2Boolean
A boolean filter does not evaluate any configuration.
4.3.3Dropdown
A dropdown filter is of no use as long as it does not know what options it should provide. Use the ConfigSet to specify options. Each entry in the set renders as an option in the dropdown, with the key as the option value and and the value as the option label. Since the ConfigSet is an ordered list, it is guaranteed that the dropdown's options will be rendered in the same order as defined in the ConfigSet.
In our movie example, we have a column that holds a comma-separated list of genres for each movie. Since there is a limited amount of genres and a user will always want to filter for complete genres only, we can provide a dropdown on this column that shows all possible genres. The configuration for this will look as follows:

1ConfigSet $config := NEW(ConfigSet);
2$config.set('Action', 'Action');
3$config.set('Adventure', 'Adventure');
4$config.set('Crime', 'Crime');
5$config.set('Drama', 'Drama');
6$config.set('Fantasy', 'Fantasy');
7$config.set('Mystery', 'Mystery');
8$config.set('Romance', 'Romance');
9$config.set('Thriller', 'Thriller');
10$config.set('War', 'War');
11$config;
Here is the table rendered accordingly, with the dropdown as a filter:

5Sorters
Each column has its own sorter. There are different types of sorters to pick from depending on the column data. Choose between a basic text sorter, a numeric sorter and a boolean sorter.

Note: More sorters, such as a data time sorter, are planned for future releases.
If a column should not be sortable, set the sorter type to none. No clickable header will be rendered and the user will not be able to sort the column.

Each type of sorter has three properties: Direction, Index and Rank.
5.1The Direction Property
The "Direction" property defines a predefined sorting that will bet set at the time the data table will be rendered. This sets the order the user will see when he/she first renders the table. Of course, the user can change the sorting later.
5.2The Index Property
The "Index" property is the value of the cell. This index decides whether a row is to be placed further up or down in the corresponding table. This property has to be defined only if it is not the same as the text displayed in the cell.
If you use a text sorter and you actually display plain text, you will probably not set anything here. However, if for example a Boolean filter is in use, it requires a boolean value to decide the order of the rows. For this kind of sorter, it is necessary to convert whatever data we have for the cell to a boolean.
Take for instance a table of users. One column represents the users' role: administrator or regular user. In the column, the text displayed is either "Administrator" or "User". Since both of these string values do not parse to true, it is here necessary to specify an index that will transform our data to a valid boolean, or a string that parses as a boolean. This is how we could write the corresponding expression:
1$user.getType() == 'Administrator'
Performance tip: Whenever you use a non-text sorter without specifying an index, the framework will try to parse the cell text to the desired type. If you have a fast way of providing the value as the desired type (for example if you have it as a property), setting it as the index will result in a better performance. Thus, for a numeric sorter it is best to provide a numeric type (Integer, Double, etc.)
5.3The Rank Property
The "Rank" property is only relevant if used in combination with the "Direction." Since it is possible to define a direction for multiple columns, you can provide a rank that will specify the order of the sorted columns. The higher the rank, the later it appears in the resulting collection of sorted columns.
This property only has an effect at render time. Once the Data Table is available to the user, he/she will be able to resort the columns and create another column sort order by using the shift key while clicking the column headers.
6Layout
6.1Table Size
The data table is a component that does not have an initial height. It is a container that shows none to an infinite number of rows.
Tables usually have a fixed amount of rows with a pager; their height is fixed, defined by the amount of rows per page. The data table component works the opposite way: the table's height is set by the available space instead of the amount of rows.
There are two ways to determine the size of a data table can be sized.
One way is to set a fixed height in pixels using the data table's style tab. This way, the surrounding screen components have to fit around the table's fixed size.

The second possibility is to make the surrounding components provide as much space for the data table as possible and let it expand. Depending on the surrounding components, you can do this by:
-Setting a dynamic height of 100% for the data table; or
-Declaring its positioning absolute and letting it expand using the following CSS rules:
#yourFancyDataTable {
   position: absolute;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
}
Be aware that for this CSS rule to work properly, the data table's parent component must have a CSS rule position:absolute itself or at least position:relative. Otherwise, the data table will expand to the next best parent that applies to this rule.
6.2Column Width
The data table provides multiple ways of setting its columns' widths. By default, they are all equally sized and dynamically expanded to the full width of the table. Some columns however require a fixed size, while some of them can stay dynamic, but require a minimum size. Others should be sized relative to other columns according to a specified ratio. Each column has two properties for setting its width: "Min Width" and "Width."
6.2.1The "Min Width" Property
You can set a minimum width for a column. This value has to be an integer. If you set a value for this property, the column will never be smaller than this value, no matter whether a dynamic width or a fixed width has been set. Not even the user can make the column smaller than this value.
6.2.2The "Width" Property
The width can be set as an integer value, which means the column shall be rendered with a fixed width. The other possibility is to set it as a string using the postfix "%". This means that the width of the column will be dynamic.
Columns with a fixed width will be rendered with that width, even if there is not enough space for the column. In this case, the data table will provide horizontal scrollbars to make it possible to see the whole content. The user will be able to resize the column up until its minimum width.
All columns with a dynamic width will share the space that remains after the columns with a fixed width have been rendered. The dynamic value is interpreted as a weight (not as a percentage). If there are for example two dynamic columns, one with 50% and one with 150%, then the remaining space will be distributed to the two columns in a ratio of 50:150, meaning 5:15 or 1:3. Regardless of the calculation, the width of a column will never be smaller than its minimum width setting.
The possibility exists that the remaining space for the dynamic columns is very small or even less than zero pixels. In this case, the dynamic columns will be rendered with their minimum width. This is why it makes sense to always define a minimum width for each column.
For our movie table example, we define a dynamic width of 40% for the title column, a fixed width of 50px for the year, a dynamic width of 60% for the genre and a fixed width of 50px for the actions column. This will nicely render the table.

7Interlines
A data table can show interlines. This means that the user can click on a row, which then folds open to show more details about the row object.
Interlines work similar to Ajax Update Areas. This means that an interline is rendered on demand only. All you have to do is to define a screen that will be rendered as the interline of the rows.
For our movies example, we create another screen that shows more details about a movie including its poster. The detail screen defines an assigned variable of type Movie that we will have to provide. This variable represents the movie that is to be displayed in the interline.

In the screen for our data table, all we have to do is to define the detail screen as the interlining screen. For that, we use the property "Interlining" and select the detail screen.

Finally, we have to provide the assigned variable with a value, which is the movie in each row. For that purpose, we use the "Row Object Variable" that we defined for the data table and that is passed to each row.

Each time an interline is shown, the server will render the detail screen with the corresponding movie as the assigned variable and show it in the table.

Could this article be better?Yes, let me send feedback