Tabele danych
Źródło:
DataTableView.js,tableSource.js
Tabela w OVE ma jeden zestaw funkcji (kolumny, wyszukiwanie, sortowanie, paginacja, selekcja, akcje, stopka, edycja wiersza) i dwa źródła danych. Nie ma osobnego datagrid dla danych lokalnych — zmienia się wyłącznie to, skąd biorą się wiersze.
Źródło danych
Lokalne — tablica ze stanu
type: datatable
id: payments
data:
varPath: $page.payments # $page.* | $global.* | $props.* | <viewId>[.pole]
searchFields: [title, note] # pola przeszukiwane (domyślnie wszystkie proste)
paginate: false # wyłącza stronicowanie
search: true # pokazuje pole wyszukiwania (bez parametru API)
columns:
- header: Tytuł
field: title
Filtrowanie, sortowanie i strony liczone są w przeglądarce. Tabela sama odświeża się po zmianie wskazanej zmiennej — bez akcji reload. Sortowanie nie przestawia tablicy w stanie: wiersze są kopiowane przed posortowaniem.
API — zapytanie do modułu
dataSource:
module: core/orders
method: list
params:
status: active
responsePath: data
totalPath: pagination.total
queryKey: name # nazwa parametru wyszukiwania
Serwer dostaje stronę, rozmiar strony, frazę i sortowanie, a zwraca gotowy zestaw wierszy.
Tabela w cudzej karcie
Gdy tabela ma stanąć w content cudzej karty (typowo fragment modułu wstawiany przez schemaGuard do wspólnej karty strony zamówienia), użyj embedded: true zamiast pozwalać jej zbudować własną kartę:
type: datatable
id: shpList
footer: false # embedded ma sens bez stopki i bez title — inaczej znów
embedded: true # widać ramy karty w karcie
dataSource:
module: core/shipping
method: shipments.getByOrderId
params:
orderId: '@orderId'
embedded: true zdejmuje z hosta klasę card (zostaje ove-datatable-embedded) i z <table> klasę card-table — bez tego druga karta rysowałaby się w środku pierwszej, a card-table dokładałby w cudzym card-body drugi padding. Łączy się z footer: false i brakiem title: nagłówek i stopka nadal renderują się na zwykłych zasadach, więc zostawione by dawały ten sam efekt karty w karcie.
Domyślne minHeight: 300px na obszarze tabeli włącza się tylko gdy tabela ma stopkę — paginacja zmienia liczbę wierszy i bez rezerwy układ skakałby przy każdej stronie. Tabela embedded zwykle jest bez stopki, więc domyślnie nie dostaje minimalnej wysokości — zajmuje tyle, ile mają wiersze. Żeby to nadpisać w dowolną stronę (zarezerwować miejsce mimo braku stopki albo wymusić minHeight: 0 mimo stopki), podaj minHeight wprost w schemacie.
Ukrywanie pustej tabeli
Gdy źródło nie zwróci żadnych wierszy, tabelę chowamy klasą d-none przez classIf, nie przez renderIf:
# Pusta tabela znika, ale zostaje jako źródło danych i cel reload/refresh —
# renderIf by ją usunął z drzewa widoków razem z tą rolą.
classIf:
d-none:
shpList:
$size: 0
renderIf przy zmianie warunku usuwa i odbudowuje widok od nowa — tabela przestałaby istnieć, więc nie mogłaby dalej być źródłem danych dla onVarChange innych elementów ani celem akcji reload/refresh po nadaniu. classIf tylko dokłada i zdejmuje klasę na istniejącym hoście, więc tabela żyje przez cały czas, a chowa się wyłącznie wizualnie.
Kolumny
| Pole | Znaczenie |
|---|---|
header | Nagłówek kolumny |
field | Ścieżka do wartości w wierszu (customer.name) — komórka pokazuje samą wartość |
content | Tablica widoków w komórce (ma pierwszeństwo przed field) |
actions | Akcje wiersza jako przyciski (ma pierwszeństwo przed content) |
align | start, center, end |
width | Szerokość kolumny (150px, 1%) |
sortable / sortKey | Sortowanie; sortKey domyślnie równy field |
decimals, prefix, suffix, emptyText | Formatowanie wartości field |
summary / summaryValue | Podsumowanie w stopce tabeli |
className / cellClassName | Dodatkowe klasy nagłówka / komórek |
columns:
- header: Nazwa
field: title
sortable: true
- header: Kwota
field: amount
align: end
decimals: 2
suffix: " PLN"
summary: sum
- header: ""
width: 1%
actions:
- icon: trash
title: Usuń
variant: danger
action: callMethod
module: core/orders
method: payment.remove
params:
id: { valueFrom: payments.$.id }
refresh: [payments]
Wpis akcji to pełna konfiguracja akcji — działa każde jej pole (params, onSuccess, refresh, confirm…), a nie tylko wybrany zestaw. Pola prezentacyjne (label, icon, variant, outline, size, title, className) opisują przycisk.
Podsumowania
- header: Kwota
field: amount
summary: sum # sum | avg | min | max | count
- header: Kwota
field: amount
summary: { agg: sum, decimals: 2, suffix: " PLN" }
- header: Razem
summaryValue: "{{ ordersTable.total }}" # gotowa wartość zamiast agregacji
Źródło lokalne agreguje wszystkie przefiltrowane wiersze, a źródło API — wiersze bieżącej strony, bo tylko one są znane przeglądarce. Dla sum liczonych na serwerze użyj summaryValue.
Edycja wiersza
rowKey: id
editable:
fields: # opis pól taki sam jak w `inlineedit`
- key: qty
type: number
min: 0
required: true
- key: note
save:
module: core/orders
method: product.update
params:
orderId: { valueFrom: $page.orderId }
rowKeyParam: productId # nazwa parametru z identyfikatorem (domyślnie `rowKey`)
onlyChanged: true
refresh: [orderDetail]
columns:
- header: Ilość
field: qty # w trybie edycji zamienia się w kontrolkę pola `qty`
Tabela dokłada kolumnę z przyciskami ✎ / ✓ / ✕. Edycja pracuje na roboczej kopii danych (patrz Edycja danych), więc anulowanie nie wymaga przeładowania. Edytowany wiersz rozpoznawany jest po rowKey, dzięki czemu przeżywa sortowanie i odświeżenie danych; gdy wiersz zniknie ze zbioru, edycja się zamyka. Błąd zapisu pojawia się pod wierszem i nie zamyka trybu edycji.
Zdarzenia
| Zdarzenie | Kiedy |
|---|---|
datatable:loaded:<id> | po załadowaniu danych |
datatable:pageChange:<id> | po zmianie strony |
datatable:search:<id> | po wyszukiwaniu |
datatable:sort:<id> | po sortowaniu |
datatable:select:<id> | po zaznaczeniu wierszy |
datatable:rowClick:<id> | po kliknięciu w wiersz |
datatable:editRow:<id> | po otwarciu edycji wiersza |
datatable:cancelRow:<id> | po anulowaniu edycji |
datatable:savedRow:<id> | po udanym zapisie wiersza |
