owc-table is a highly configurable table component for displaying structured data. It supports client-side and server-side data loading, filtering, sorting, grouping, inline editing, exporting, custom row rendering, and many additional features while remaining easy to integrate into existing applications.
The component is configured through a column definition and a data source. Whether your data already exists locally or needs to be loaded from an API, owc-table provides a consistent API for displaying and interacting with tabular data.
The following example demonstrates a simple setup to render a table.
Columns are defined using the .columns property, while row data is provided through .data. Each column requires a unique field that maps to a property of every row object. The label property defines the text shown in the table header.
export const simpleTable = () => {
return html`
<div style="height: 60vh; overflow: auto; ">
<owc-table
virtualizer-mode="always"
selectable
grow-full-width
filter-mode="global-search-with-builder"
save-state-to-url
sticky-header
show-info
.actionTabs=${{
export: { visible: true },
settings: { visible: true },
}}
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
{
label: 'Profession',
field: 'profession',
filterable: true,
},
{
label: 'Age',
field: 'age',
formatter: 'number',
showInCalculateSums: true,
filterable: true,
},
{
label: 'Monthly Pay',
field: 'monthlyPay',
formatter: 'number',
},
{
label: 'Birthdate',
field: 'birthDate',
formatter: 'date',
},
]}
.data=${generateMoreData(200)}
></owc-table>
</div>
`;
};
Every table consists of two building blocks:
A row is represented as a plain JavaScript object. Each column references one property of that object using its field property.
const row = {
id: 1,
firstName: 'Ada',
lastName: 'Lovelace',
};
columns = [
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
];
Once both are provided, the table automatically renders the data.
owc-table supports two approaches for loading data:
.data.handleData function.Choose the approach that best fits your application. If your data already exists locally, using .data is the simplest option. If the data needs to be fetched from an API or database, handleData provides an asynchronous loading mechanism.
If all data is already available when the table is rendered, provide it through the .data property.
This is the recommended approach for applications that keep their data locally, for example after synchronizing it with a backend.
export const dataHandlingPassing = () => {
return html`
<owc-table
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
]}
.data=${[
{ id: 1, firstName: 'Ada', lastName: 'Lovelace' },
{ id: 2, firstName: 'Grace', lastName: 'Hopper' },
]}
></owc-table>
`;
};
When data must be retrieved from an external source such as a REST API or database, provide an asynchronous handleData function instead of using .data.
Depending on the configured mode, the table decides when this function is executed.
| Mode | Description |
|---|---|
initiallyOnce | Loads the data once and performs all filtering and sorting locally. |
anyFilterChange | Requests new data whenever filters change. |
initiallyAndAnyFilterChange | Loads data initially and again whenever filters change. (Currently not implemented.) |
The behavior can be configured using .handleDataOptions.
initiallyOnce is the default mode.
This mode is ideal for datasets that comfortably fit into memory and do not change frequently. The table performs a single request when it is first rendered. Afterwards, filtering and sorting happen entirely in the browser without additional network requests.
Whenever new data is required, call callHandleData() manually or use the built-in refresh button (enabled by default).
export const handleDataOptions_initiallyOnce = () => {
return html`
<owc-table
.handleData=${async () => {
// Simulate an API request.
await new Promise(resolve => setTimeout(resolve, 1000));
return personData;
}}
.columns=${[
{
label: 'Profession',
field: 'profession',
},
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
]}
></owc-table>
`;
};
Use anyFilterChange for large datasets that should remain on the server.
Whenever a filter changes, the table calls handleData() again with the current filters. This allows filtering to happen directly in the backend or database instead of loading all records into the browser.
To reduce unnecessary requests, use the optional condition callback. It determines whether a request should be executed for the current filter state. The example below only performs a request once the user has entered at least three characters.
const handleDataOptionsArray = [...generateMoreData(100), ...personData];
export const handleDataOptions_anyFilterChange = () => {
return html`
<owc-table
.handleData="${async ({ jsonFilters }) => {
const search = jsonFilters[0]?.value;
await new Promise(resolve => setTimeout(resolve, 1000));
return handleDataOptionsArray.filter(
person =>
person.firstName.toLowerCase().includes(search.toLowerCase()) ||
person.lastName.toLowerCase().includes(search.toLowerCase()),
);
}}"
filter-mode="global-search"
.handleDataOptions=${{
mode: 'anyFilterChange',
condition: ({ jsonFilters }) => {
const search = jsonFilters[0]?.value;
return search && search.length > 2;
},
debounceTime: 500,
}}
.columns=${[
{
label: 'Profession',
field: 'profession',
filterable: true,
filterType: 'autocomplete',
filterOptions: [
{ value: 'Teacher', label: 'Teacher' },
{ value: 'Lawyer', label: 'Lawyer' },
{ value: 'Developer', label: 'Developer' },
{ value: 'Professor', label: 'Professor' },
],
},
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
]}
></owc-table>
`;
};
Columns define how data is presented in the table.
Every column represents one value of a row object. At minimum, a column requires a label, which is displayed in the table header, and a field, which references the corresponding property on each row.
columns = [
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
];
Besides displaying values, columns also control formatting, filtering, sorting, editing, visibility, alignment, export behavior, and many other features described throughout this section.
Columns can format their displayed values using the formatter property.
Several built-in formatters are available:
| Formatter | Description |
|---|---|
rownum | Displays the row number. |
number | Formats numeric values. |
currency | Formats values using the configured currency formatter. |
percent | Formats percentages. |
date | Formats dates using the configured date formatter. |
datetime | Formats date and time values. |
In addition to the built-in formatters, a custom formatter function can be supplied. The function receives the current row and additional rendering information and may return plain text or HTML.
export const formatTable = () => {
return html`
<owc-table
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Age',
field: 'age',
formatter: 'number',
},
{
label: 'Monthly Pay',
field: 'monthlyPay',
formatter: 'currency',
},
{
label: 'Birthdate',
field: 'birthDate',
formatter: 'date',
},
{
label: 'Hobbies',
field: 'hobbies[]',
fieldFilteredReturn: 'hobbies[]',
filterType: 'number',
formatter: (row, { fieldValueFiltered }) => {
return html`
<ul>
${fieldValueFiltered.map(value => html`<li>${value}</li>`)}
</ul>
`;
},
},
{
label: 'Profession',
field: 'profession',
formatter: row => html`
<span style="background: green; border-radius: 5px; color: white; padding: 3px;">
${row.profession}
</span>
`,
},
]}
.data=${personData}
></owc-table>
`;
};
The built-in formatters use German locale settings by default.
You can override the formatter instances to use different locales, currencies, or formatting options throughout the entire table.
export const overrideBuiltinFormatter = () => {
return html`
<owc-table
.columns=${[
{ label: 'Nr.', formatter: 'rownum' },
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Age',
field: 'age',
formatter: 'number',
},
{
label: 'Monthly Pay',
field: 'monthlyPay',
formatter: 'currency',
},
{
label: 'Birthdate',
field: 'birthDate',
formatter: 'date',
},
]}
.data=${personData}
.numberFormatter=${new Intl.NumberFormat('de', {
minimumFractionDigits: 2,
})}
.currencyFormatter=${new Intl.NumberFormat('en-IN', {
style: 'currency',
currency: 'INR',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})}
.dateFormatter=${new Intl.DateTimeFormat('en-US', {
month: '2-digit',
day: '2-digit',
year: 'numeric',
})}
></owc-table>
`;
};
Use the align property to control how values are aligned inside a column.
| Value | Description |
|---|---|
start | Left-aligned (default). |
center | Center-aligned. |
end | Right-aligned. |
full | Centers the cell content while leaving the header aligned normally. |
export const alignContent = () => {
return html`
<owc-table
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
align: 'start',
},
{
label: 'Profession',
field: 'profession',
align: 'center',
},
{
label: 'First Name',
field: 'firstName',
align: 'end',
},
{
label: 'Last Name',
field: 'lastName',
align: 'full',
},
]}
.data=${personData}
></owc-table>
`;
};
Use the width property to define an initial column width.
Columns are resizable by default. Set resizable to false to disable resizing for an individual column.
export const widthOfColumns = () => {
return html`
<owc-table
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
resizable: false,
width: 60,
},
{
label: 'Profession',
field: 'profession',
resizable: false,
width: 300,
},
{
label: 'First Name',
field: 'firstName',
resizable: true,
width: 1000,
},
{
label: 'Last Name',
field: 'lastName',
resizable: true,
},
]}
.data=${personData}
></owc-table>
`;
};
Columns can be shown, hidden, or displayed only when they are part of the active filter.
To allow users to configure column visibility interactively, enable the built-in Settings action tab.
<owc-table
.actionTabs=${{
settings: {
visible: true,
},
}}
></owc-table>
Each column controls its visibility using the visible property.
| Value | Description |
|---|---|
always | Always display the column (default). |
never | Never display the column. |
ifFiltered | Display the column only when it participates in an active filter. |
export const hideColumnsTable = () => {
return html`
<owc-table
filter-mode="global-search-with-builder"
.actionTabs=${{
settings: {
visible: true,
},
}}
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
visible: 'always',
filterable: true,
},
{
label: 'Profession',
field: 'profession',
visible: 'never',
filterable: true,
},
{
label: 'First Name',
field: 'firstName',
visible: 'always',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
visible: 'ifFiltered',
filterable: true,
},
{
label: 'Age',
field: 'age',
visible: 'ifFiltered',
filterable: true,
},
]}
.data=${personData}
></owc-table>
`;
};
owc-table provides flexible filtering and sorting capabilities for both client-side and server-side data.
Filtering is configured per column, while the overall filtering experience is controlled using the filter-mode attribute.
Depending on your data source, filtering can either happen directly in the browser or on the server through handleData(). For more information about server-side filtering, see Loading Data.
The filter-mode attribute controls how users interact with filters.
| Value | Description |
|---|---|
hidden | Filtering is disabled. |
global-search | Displays a single global search field. |
builder | Displays the filter builder only. |
global-search-with-builder | Displays both the global search and the filter builder. |
<owc-table filter-mode="global-search"></owc-table>
Choose the mode that best fits your use case:
Columns participate in filtering only when filterable is enabled.
columns = [
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
];
If a column is not marked as filterable, it will never appear in the filter builder.
Each filterable column may define a filter type.
| Type | Description |
|---|---|
text | Free text input (default). |
number | Numeric comparison. |
date | Date comparison. |
checkbox | Boolean values. |
autocomplete | Select values from predefined options. |
If no filterType is specified, the table automatically uses a text filter.
export const filterTypes = () => {
return html`
<owc-table
filter-mode="builder"
.columns=${[
{
label: 'First Name',
field: 'firstName',
filterable: true,
filterType: 'text',
},
{
label: 'Age',
field: 'age',
filterable: true,
filterType: 'number',
},
{
label: 'Birthdate',
field: 'birthDate',
filterable: true,
filterType: 'date',
},
{
label: 'Premium',
field: 'premium',
filterable: true,
type: 'editable',
editableOptions: { type: 'checkbox' },
filterType: 'checkbox',
},
{
label: 'Profession',
field: 'profession',
filterable: true,
filterType: 'autocomplete',
filterOptions: [
{
label: 'Developer',
value: 'Developer',
},
{
label: 'Teacher',
value: 'Teacher',
},
{
label: 'Lawyer',
value: 'Lawyer',
},
],
},
]}
.data=${personData}
></owc-table>
`;
};
Autocomplete filters present a predefined list of selectable values.
Provide the available options through filterOptions.
{
label: 'Profession',
field: 'profession',
filterable: true,
filterType: 'autocomplete',
filterOptions: [
{
label: 'Developer',
value: 'Developer',
},
{
label: 'Teacher',
value: 'Teacher',
},
],
}
This filter type is particularly useful when users should only choose from a known set of values.
Columns become sortable by enabling the sortable property.
columns = [
{
label: 'Age',
field: 'age',
sortable: true,
},
];
Users can sort by clicking the corresponding table header.
Sorting is performed automatically using the configured formatter and data type. For more advanced use cases, custom sorters can be provided through the sorters property.
export const sorting = () => {
return html`
<owc-table
.columns=${[
{
label: 'First Name',
field: 'firstName',
sortable: true,
},
{
label: 'Last Name',
field: 'lastName',
sortable: true,
},
{
label: 'Age',
field: 'age',
sortable: true,
formatter: 'number',
},
]}
.data=${personData}
></owc-table>
`;
};
For values that cannot be sorted using the default comparison logic, provide custom sorters through the sorters property. Here, as an example, only month and day are considered.
export const sortingTable = () => {
return html`
<owc-table
.columns=${[
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
{
label: 'Profession',
field: 'profession',
filterable: true,
},
{
label: 'Birthdate',
field: 'birthDate',
formatter: 'date',
sorter: [{ field: 'birthDate', order: 'desc', sortType: 'dateNoYear' }],
},
]}
.data=${generateMoreData(10)}
></owc-table>
`;
};
Custom sorters are especially useful when displaying formatted values while sorting by a different underlying value.
Enable the save-state-to-url attribute to persist the current table state in the browser URL.
<owc-table save-state-to-url></owc-table>
The following state is preserved:
Sharing the URL restores the same table configuration when it is opened again.
Currently, there is no support for multiple tables using this attribute on the same page. trying to use it with multiple tables on the same page will result in fighting of the tables.
Filter descriptions provide additional information about available filters and their purpose.
They help users understand which values are expected and can be especially useful when many filterable columns are available.
{
label: 'Birthdate',
field: 'birthDate',
filterable: true,
filterDescription:
'Enter a date to find everyone born on or after the selected day.',
}
export const descriptionTable = () => {
return html`
<owc-table
filter-mode="global-search-with-builder"
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
filterable: true,
description: 'This filters the first name with text search',
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
description: 'This filters the last name with text search',
},
{
label: 'Profession',
field: 'profession',
filterable: true,
filterType: 'autocomplete',
filterOptions: [
{ value: 'Teacher', label: 'Teacher' },
{ value: 'Lawyer', label: 'Lawyer' },
{ value: 'Developer', label: 'Developer' },
{ value: 'Professor', label: 'Professor' },
],
description: 'This filters the professions with multiple options',
subDescription: 'All options are: Teacher, Lawyer, Developer, Professor',
},
{
label: 'Age',
field: 'age',
filterable: true,
filterType: 'number',
description: 'This filters the age with a number filter with conditional operators',
},
]}
.data=${personData}
></owc-table>
`;
};
Descriptions are shown inside the filter builder and should briefly explain what the filter does without repeating the column label.
Filtering behaves differently depending on how data is loaded.
| Data Source | Behavior |
|---|---|
.data | All filtering is performed locally in the browser. |
handleData() with initiallyOnce | Data is loaded once, then filtered locally. |
handleData() with anyFilterChange | Filters are sent to the server and filtering happens remotely. |
Choose the approach that best matches the size and update frequency of your dataset.
By default, rows are rendered as plain table rows.
owc-table provides several rendering options that allow rows to behave as links, display expandable details, show annotations, or be grouped.
The rendering mode is controlled using the render-mode attribute.
| Type | Description |
|---|---|
simple | Simple Row (default). |
link | Row with a link |
detail | Row with an expandable Detail (preloaded) |
detailDeferred | Row with an expandable Detail (loaded after call) |
linkWithDetail | combination of link and detail |
Set render-mode="link" to make every row behave as a link.
The destination for each row is provided by the getRowLinkSettings callback. The callback receives the complete row object, allowing links to be generated dynamically. In this example, it searches via the google search for the profession. it can also reference pages of the same Website.
export const rowsAsLinks = () => {
return html`
<owc-table
render-mode="link"
.getRowLinkSettings=${row => ({ href: `https://www.google.com/search?q=${row.profession}` })}
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${personData}
></owc-table>
`;
};
Besides the destination URL, additional link settings such as target or other supported anchor attributes may also be returned.
Rows can display additional content below the main row.
Enable this behavior using render-mode="detail" and provide a renderDetail callback. this is called when the row is clicked upon.
The callback receives the selected row and returns the content that should be displayed when the row is expanded.
export const showDetails = () => {
return html`
<owc-table
render-mode="detail"
.renderDetail=${row => html`
<div style="padding:16px;">
<h4>${row.firstName} ${row.lastName}</h4>
<p>Profession: ${row.profession}</p>
<p>Age: ${row.age}</p>
</div>
`}
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${personData}
></owc-table>
`;
};
Expanded rows remain open until the user collapses them again or the table state changes.
Expanded rows can also be controlled programmatically.
Assign row identifiers to the openDetails property to expand specific rows.
table.openDetails = [2, 5, 9];
The identifiers must match the values returned by getRowId().
const showDetailsProgrammaticallyArray = [...generateMoreData(20), ...personData]
.map(value => ({ value, sort: Math.random() }))
.sort((a, b) => a.sort - b.sort)
.map(({ value }) => value);
export const showDetailsProgrammatically = () => {
return html`
<owc-table
render-mode="detail"
.openDetails=${['0013X00002eOb5BQAS', '0013X00002eP8qsQAC', '0013X00002eP8sDQAS']}
.renderDetail=${row => html`
<div style="padding:16px;">
<h4>${row.firstName} ${row.lastName}</h4>
<p>Profession: ${row.profession}</p>
<p>Age: ${row.age}</p>
</div>
`}
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${showDetailsProgrammaticallyArray}
></owc-table>
`;
};
usually, details get rendered in the background if the row itself is displayed. this might causes cluttering of the DOM. to reduce objects in the DOM, you can use render-mode="detailDeferred" instead. then, the detail will only be rendered when opened.
export const showDetailsDeferred = () => {
return html`
<owc-table
render-mode="detailDeferred"
.renderDetail=${row => html`
<div style="padding:16px;">
<h4>${row.firstName} ${row.lastName}</h4>
<p>Profession: ${row.profession}</p>
<p>Age: ${row.age}</p>
</div>
`}
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${personData}
></owc-table>
`;
};
Sometimes additional information is not available immediately and must be loaded from an API.
renderDetail may therefore return a Promise.
The table automatically waits for the promise to resolve before rendering the returned content.
export const asyncShowDetails = () => {
return html`
<owc-table
render-mode="detail"
.renderDetail=${async row => {
await new Promise(resolve => setTimeout(resolve, 1000));
return html`
<div style="padding:16px;">
<h4>${row.firstName} ${row.lastName}</h4>
<p>Additional information loaded asynchronously.</p>
</div>
`;
}}
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${personData}
></owc-table>
`;
};
This approach is recommended when loading expensive or rarely used data.
it's also possible to combine the link and the detail mode. in this mode, an additional button is provided to expand the detail. the rest of the row still functions like a link.
export const rowsAsLinksAndDetails = () => {
return html`
<owc-table
render-mode="linkWithDetail"
.getRowLinkSettings=${row => ({ href: `https://www.google.com/search?q=${row.profession}` })}
.renderDetail=${row => html`
<div style="padding:16px;">
<h4>${row.firstName} ${row.lastName}</h4>
<p>Profession: ${row.profession}</p>
<p>Age: ${row.age}</p>
</div>
`}
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${personData}
></owc-table>
`;
};
Annotations allow additional visual content to be rendered inside a row without modifying the regular column layout.
Provide a renderAnnotation callback that returns the annotation for the current row.
export const annotation = () => {
return html`
<owc-table
.renderAnnotation=${row => {
return !row.profession
? html`<wa-tag variant="warning">
<wa-icon name="clock"></wa-icon>
<span>Warning: Profession field is empty</span>
</wa-tag>`
: nothing;
}}
.columns=${[
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${personData}
></owc-table>
`;
};
Annotations are useful for displaying status indicators, badges, warnings, or other supplementary information.
Rows can be grouped by providing a groupSelector function.
The callback receives each row and returns the group identifier for that row.
table.groupSelector = row => row.profession;
Rows sharing the same group identifier are rendered together. In order to make it work, you need in addition provide the groups
export const groupedTable = () => {
return html`
<div style="height: 60vh; overflow: auto; ">
<owc-table
.groupList=${[
{
key: 'Teacher',
label: 'Teacher',
},
{
key: 'Developer',
label: 'Developer',
},
{
key: 'Lawyer',
label: 'Lawyer',
},
{
key: 'Professor',
label: 'Professor',
},
]}
.groupSelector=${row => row.profession}
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'Gender',
field: 'gender',
},
{
label: 'Birthdate',
field: 'birthDate',
formatter: 'date',
sorter: [{ field: 'birthDate', order: 'desc', sortType: 'dateNoYear' }],
},
{
label: 'Profession',
field: 'profession',
},
{
label: 'First Name',
field: 'firstName',
},
]}
.data=${groupedTableArray}
></owc-table>
</div>
`;
};
The appearance and ordering of groups can be customized through groupList.
Each group may define a label, priority, colors, and whether it should be expanded by default.
table.groupList = [
{
key: 'developer',
label: 'Developers',
priority: 1,
active: true,
backgroundColor: '#1976d2',
textColor: '#fff',
},
{
key: 'teacher',
label: 'Teachers',
priority: 2,
},
];
const customGroupedTableArray = [...generateMoreData(100), ...personData];
export const customGroupedTable = () => {
return html`
<div style="height: 60vh; overflow: auto; ">
<owc-table
.groupList=${[
{ key: 'j', label: 'Starts with R', priority: 10, active: true },
{
key: 'm',
label: 'Starts with M',
priority: 10,
active: false,
backgroundColor: 'red',
textColor: 'white',
},
]}
.groupSelector=${row => row.firstName.toLowerCase().substring(0, 1)}
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'Gender',
field: 'gender',
},
{
label: 'Birthdate',
field: 'birthDate',
formatter: 'date',
sorter: [{ field: 'birthDate', order: 'desc', sortType: 'dateNoYear' }],
},
{
label: 'First Name',
field: 'firstName',
},
]}
.data=${groupedTableArray}
></owc-table>
</div>
`;
};
Groups are rendered according to their priority. Groups that are not listed may optionally be collected into an Others group.
Enable othersGroupActive to collect all groups that are not explicitly defined in groupList.
table.othersGroupActive = true;
This is useful when only a subset of groups should receive a custom appearance while all remaining groups are still displayed.
This chapter mainly talks about additional settings, that do not directly have to do with the data itself but more of additional features and options over the entire row.
The show-info shows a help text that says how many rows are currently being displayed. This works with the filter as well.
export const showInfoTable = () => {
return html`
<owc-table
show-info
filter-mode="global-search"
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
]}
.data=${personData}
></owc-table>
`;
};
If you want to add static content between the Filter and the content you can provide a renderHeaderContent function
export const headerContent = () => {
return html`
<owc-table
filter-mode="global-search"
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
visible: 'always', // is the default
},
{
label: 'First Name',
field: 'firstName',
visible: 'always',
},
]}
.renderHeaderContent=${() => html`<p>some extra header content</p>`}
.data=${personData}
></owc-table>
`;
};
Use the sticky-header attribute to make the header fixed at the top of the table when scrolling.
export const stickyHeaderTable = () => {
return html`
<div style="height: 60vh; overflow: auto; ">
<owc-table
sticky-header
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'Profession',
field: 'profession',
},
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
]}
.data=${generateMoreData()}
></owc-table>
</div>
`;
};
Tables can be copied to an Excel Table or downloaded as a *.csv. It comes with a built in "Export" Tab action which you can enable by setting it's visibility to true.
.actionTabs=${{
export: { visible: true },
}}
To exclude a column, set includeInExport to false.
export const exportTable = () => {
return html`
<owc-table
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
includeInExport: false,
},
{
label: 'Profession',
field: 'profession',
},
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
]}
.data=${personData}
.actionTabs=${{
export: { visible: true },
}}
></owc-table>
`;
};
The selectable attribute makes rows selectable and adds a checkbox to select all rows. To work with the selected data add a custom tab via the .actionTabs property - its content callback receives selectedData, an array with the information of the selected rows. In this example it is logged in the console. IDs are required when using selectable rows.
export const selectableTable = () => {
return html`
<owc-table
selectable
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
},
{
label: 'Last Name',
field: 'lastName',
},
]}
.actionTabs=${{
showSelected: {
label: 'Auswahl',
visible: true,
content: ({ selectedData }) => html`
<wa-button
size="s"
@click=${async () => {
console.log(
'Selected Data: ' +
selectedData
.map(data => data.firstName + ' ' + data.lastName + ' (' + data.id + ')')
.join(', '),
);
}}
>
Show Selected
</wa-button>
`,
},
}}
.data=${personData}
></owc-table>
`;
};
You can add your own action by adding an additional key to the actionTabs.
selected data contains the data that has been selected via the checkbox and processed data is the data that is currently shown. i.e.: if a filter is applied, the statistics will be calculated based of that.
export const actionTabTable = () => {
return html`
<div style="height: 60vh; overflow: auto; ">
<owc-table
show-info
filter-mode="global-search-with-builder"
selectable
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
includeInExport: false,
filterable: true,
},
{
label: 'Profession',
field: 'profession',
filterable: true,
},
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
]}
.data=${generateMoreData(100)}
.actionTabs=${{
statistics: {
label: 'Gender Statistics',
content: ({ selectedData, processedData }) => {
const data = selectedData.length > 0 ? selectedData : processedData;
const maleCount = data.filter(person => person.gender === 'male').length;
const femaleCount = data.filter(person => person.gender === 'female').length;
return html`
<p>Males: ${maleCount}</p>
<p>Females: ${femaleCount}</p>
`;
},
},
}}
></owc-table>
</div
`;
};
You can pre open a tag by setting .actionTabActive or action-tab-active to the key of the tab.
Example <owc-table action-tab-active="statistics"></owc-table>
export const actionTabOpenTable = () => {
return html`
<owc-table
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
includeInExport: false,
filterable: true,
},
{
label: 'Profession',
field: 'profession',
filterable: true,
},
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
]}
.data=${generateMoreData(10)}
.actionTabs=${{
export: {
visible: true,
},
settings: {
visible: true,
},
statistics: {
label: 'Gender Statistics',
content: ({ selectedData, processedData }) => {
const data = selectedData.length > 0 ? selectedData : processedData;
const maleCount = data.filter(person => person.gender === 'male').length;
const femaleCount = data.filter(person => person.gender === 'female').length;
return html`
<p>Males: ${maleCount}</p>
<p>Females: ${femaleCount}</p>
`;
},
},
}}
action-tab-active="statistics"
></owc-table>
`;
};
owc-table supports inline editing for individual cells, adding new rows, and editing multiple rows at once.
Editing behavior is configured on a per-column basis, allowing editable and read-only columns to coexist within the same table.
Enable editing by setting the editable property on a column.
When a user edits a value, the table updates the row and optionally forwards the change through handleUpdate.
columns = [
{
label: 'First Name',
field: 'firstName',
editable: true,
},
];
The appropriate editor is selected automatically based on the column configuration.
By default, editable columns use a text input.
export const editableTable = () => {
return html`
<owc-table
.columns=${[
{
label: 'First Name',
field: 'firstName',
type: 'editable',
},
{
label: 'Last Name',
field: 'lastName',
type: 'editable',
},
{
label: 'Profession',
field: 'profession',
},
]}
.data=${personData}
></owc-table>
`;
};
Users can click a cell to edit its value directly.
Boolean values can be edited using a checkbox.
Configure the column with an appropriate editor.
export const editCheckbox = () => {
return html`
<owc-table
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
type: 'editable',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Premium',
field: 'premium',
type: 'editable',
editableOptions: { type: 'checkbox' },
},
]}
.data=${personData}
></owc-table>
`;
};
Checkboxes are recommended whenever the edited value represents a boolean state.
Autocomplete editors present a predefined list of selectable values.
Provide the available options through editOptions.
export const editAutocomplete = () => {
return html`
<owc-table
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
type: 'editable',
},
{
label: 'Last Name',
field: 'lastName',
},
{
label: 'Profession',
field: 'profession',
type: 'editable',
editableOptions: {
type: 'autocomplete',
data: [
{ value: 'Teacher', label: 'Teacher' },
{ value: 'Lawyer', label: 'Lawyer' },
{ value: 'Developer', label: 'Developer' },
{ value: 'Professor', label: 'Professor' },
],
},
},
]}
.data=${personData}
></owc-table>
`;
};
Autocomplete editors help ensure users only enter valid values.
Provide a handleInsert callback to allow users to create new rows.
The callback should return a newly initialized row object.
table.handleInsert = () => ({
id: crypto.randomUUID(),
firstName: '',
lastName: '',
profession: '',
});
export const addNewRow = () => {
return html`
<owc-table
.handleInsert=${() => ({ id: crypto.randomUUID(), firstName: '', lastName: '' })}
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
type: 'editable',
},
{
label: 'Last Name',
field: 'lastName',
type: 'editable',
},
]}
.data=${personData}
></owc-table>
`;
};
The returned object is inserted into the table and can immediately be edited.
Whenever a value changes, handleUpdate is called.
Use this callback to synchronize changes with an external data source such as a REST API or database.
here's an example of a call. use autoSetData() to save it locally.
export const handleDataUpdatesExample = () => {
return html`
<owc-table
selectable
.handleUpdate=${async ({ data, field, config, value, autoSetData }) => {
await new Promise(resolve => setTimeout(resolve, 2000));
console.log('data has been saved');
autoSetData();
}}
.actionTabs=${{
massEdit: { visible: true },
}}
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
type: 'editable',
},
{
label: 'Last Name',
field: 'lastName',
formatterCompare: (row, { override }) => `${override.lastName}`,
editableOptions: {
massEdit: true,
},
},
{
label: 'Profession',
field: 'profession',
type: 'editable',
editableOptions: {
massEdit: true,
type: 'autocomplete',
data: [
{ value: 'Teacher', label: 'Teacher' },
{ value: 'Lawyer', label: 'Lawyer' },
{ value: 'Developer', label: 'Developer' },
],
},
},
]}
.data=${personData}
></owc-table>
`;
};
Multiple selected rows can be updated simultaneously.
Mass editing is useful when the same value should be applied to several rows at once. Mass edit can only change columns if it's specified in the editableOptions via massEdit: true. in addition, you can specify a formatterCompare to display the change.
export const massEdit = () => {
return html`
<owc-table
selectable
.handleUpdate=${({ data, field, config, value, autoSetData }) => {
autoSetData();
}}
.actionTabs=${{
massEdit: { visible: true },
}}
.columns=${[
{
label: 'Nr.',
formatter: 'rownum',
},
{
label: 'First Name',
field: 'firstName',
type: 'editable',
},
{
label: 'Last Name',
field: 'lastName',
formatterCompare: (row, { override }) => `${override.lastName}`,
editableOptions: {
massEdit: true,
},
},
{
label: 'Profession',
field: 'profession',
type: 'editable',
editableOptions: {
massEdit: true,
type: 'autocomplete',
data: [
{ value: 'Teacher', label: 'Teacher' },
{ value: 'Lawyer', label: 'Lawyer' },
{ value: 'Developer', label: 'Developer' },
],
},
},
]}
.data=${personData}
></owc-table>
`;
};
Mass editing respects the same validation and update handling as editing individual cells.
owc-table comes with the option to localize on top of the formatter overwrites. it does so by using webawesome localization. Currently, it supports german and english. to change it, add a lang tag.
german:
export const localizationGerman = () => {
return html`
<owc-table
lang="de"
selectable
filter-mode="global-search-with-builder"
show-info
.columns=${[
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
]}
.data=${personData}
></owc-table>
`;
};
english:
export const localizationEnglish = () => {
return html`
<owc-table
lang="en"
selectable
filter-mode="global-search-with-builder"
show-info
.columns=${[
{
label: 'First Name',
field: 'firstName',
filterable: true,
},
{
label: 'Last Name',
field: 'lastName',
filterable: true,
},
]}
.data=${personData}
></owc-table>
`;
};
The following reference lists all public attributes, properties, callbacks, and events exposed by owc-table.
Unless stated otherwise, properties can be configured declaratively as HTML attributes (where supported) or programmatically using JavaScript.
| Property | Type | Description |
|---|---|---|
data | Array<T> | is the access point to initialize the data (see Passing Data) |
visibleData | Array<T> | contains all the visible data |
processedData | Array<T> | the data after it has been processed by a filter |
allData | Array<T> | contains every entry |
insertData | Array<T> | used to insert data |
handleData | (options?: { jsonFilters?: NestedJsonFilters }) => Promise<T[]> | Async data provider function (see Loading Data with handleData) |
handleDataOptions | HandleDataOptions | the options to handleData (see Handle Data Mode) |
save-state-to-url | boolean | saves active options into the url (see Save State to Url) |
store-name-prefix | string | changes the name prefix of the table when using save-state-to-url (see Save State to Url) |
loading | boolean | reflects if the table is loading at the moment |
| Property | Type | Description |
|---|---|---|
columns | Column<T>[] | the definition of the columns (see Columns) |
overrides | Overrides | overrides certain column options like visibility |
selectable | boolean | defines if rows can be selected |
getSelectorSettings | (row: T) => SelectorSettings | gets the selector settings for that row |
| Property | Type | Description |
|---|---|---|
filter-mode | string | Sets the filtering behavior (See Filtering) |
filter | Filter<unknown> | null | custom filter function |
jsonFilters | NestedJsonFilters | Declarative JSON filters |
highlightFilter | Filter<unknown> | null | custom highlight function |
highlightJsonFilters | NestedJsonFilters | Declarative JSON highlight filters |
sorters | Sorter[] | custom sorting functions |
jsonSorters | JsonSorter[] | Declarative json sorters |
| Property | Type | Description |
|---|---|---|
render-mode | string | Sets how the rows are rendered (see Row Rendering) |
sticky-header | boolean | makes the header sticky (see Sticky Header) |
renderDetail | renderDetail<T> | renderDetailPromise<T> | The function to render a detail (see Row Details) |
renderAnnotation | RenderAnnotation<T> | function to render annotations above the rows (see Row Annotation) |
renderHeaderContent | RenderHeaderContent | renders static content at the header (see Add Header Content) |
openDetails | (string | number)[] | Expands listed rows programmatically (see Opening Details Programmatically) |
getRowLinkSettings | (row: T) => RowLinkSettings | sets the link for the rows (see Rows as Links) |
getRowId | (row: T) => string | number | returns the id of the specified row |
| Property | Type | Description |
|---|---|---|
groupSelector | (row: T) => string | determines the selector to be used to assign the group (see Grouping Rows) |
groupList | Group[] | the available groups (see Grouping Rows) |
othersGroupActive | boolean | determines if other entities should be displayed in an other group |
| Property | Type | Description |
|---|---|---|
handleInsert | () => T | is called to handle inserts of data (see Adding Rows) |
handleUpdate | handleUpdate<T> | is called for every update (Handling Data Updates) |
compareOverrides | Record<string, Partial<T>> | makes the override comparison for mass edit |
| Property | Type | Description |
|---|---|---|
actionTabs | Tabs<OwcTableActionTabsRenderOptions<T>> | a list of action tabs (see Add your own Action Tab) |
action-tab-active | string | the tab that should be per default open (see Open a specific Action Tab) |
show-info | boolean | displays additional info (see Extra Info) |
to overwrite the formats, see Override Built-in Formatters
| Property | Type | Description | Default |
|---|---|---|---|
currencyFormatter | Intl.NumberFormat | manages how currencyis displaced | de, EUR; xxx.xxx,xx € |
numberFormatter | Intl.NumberFormat | manages how number is displaced | xxx.xxx,oo (o means optionally, max two decimals) |
percentFormatter | Intl.NumberFormat | manages how percent is displaced | de; xx,xx% / xx% |
dateFormatter | Intl.DateTimeFormat | manages how date is displaced | de; dd.MM.yyyy |
dateTimeFormatter | Intl.DateTimeFormat | manages how datetime is displaced | de; dd.MM.yyyy, H:mm / dd.MM.yyyy, HH:mm (depends on the browser) |
| Property | Type | Description |
|---|---|---|
customStyles | CSSResult | TemplateResult | styles for the shadow doms. (i.e.: if you need custom styles for the action tab) |
virtualizerMode | "auto" | "always" | "never" | sets the mode of how the rows are rendered. if set to auto(on large tables) or always, row that are outside out of field of view will be unrendered and deleted from the DOM |
| Event | Description |
|---|---|
| @rowClick | Fired when a row is clicked; the row data is on event.row (RowClickEvent). |
| @owc-table-data-ready | Fired (bubbling, composed) once new data has been rendered. |