Referenz

Alle Aufrufe des Grid-Builders und der Spalten auf einen Blick. Wie sie zusammenspielen, zeigen die Beispiele in der Navigation.

Alle Aufrufe des Grid-Builders und der Spalten auf einen Blick. Wie sie zusammenspielen, zeigen die Beispiele — hier steht, was es gibt.

Der Builder

Methode Wirkung
.BindTo(daten) Die Zeilen. Eine leere Liste ist zulässig.
.Id(id) Eindeutige Id des Grids. Bei mehreren Tabellen auf einer Seite Pflicht.
.Columns(spalten => …) Die Spaltendefinition, siehe unten.
.EnableSorting(spalte, richtung) Anfangssortierung. SortDirection.Ascending / .Descending.
.EnableSearchBox() Suchfeld über der Tabelle. Nur mit AjaxDataUrl — siehe «Grundfall».
.PageSizeMenu(10, 25, 50) Auswahl der Seitengrösse. Nur mit AjaxDataUrl.
.DefaultPageSize(25) Vorgewählte Seitengrösse. Nur mit AjaxDataUrl.
.HidePaginationIfSinglePage() Blätterleiste ausblenden, wenn alles auf eine Seite passt. Nur mit AjaxDataUrl.
.NoRecordsText(text) Text für die leere Tabelle. .NoResultsText(text) setzt denselben Wert — siehe «Ohne Daten».
.EmptyColumnChar("–") Zeichen für leere Zellen; alternativ ein IconType — siehe «Zellen auszeichnen».
.DisableResponsiveMode(minWidth) Schaltet das Zusammenklappen schmaler Spalten ab.
.EnableRowClick() Zeile anklickbar machen — siehe «Zeilenklick».
.DisableRow(bedingung, einstellungen) Zeilen fachlich sperren. Die Einstellungen sind Pflicht (FmhGrid.RowSettings()).
.DisableRowClientExpression(ausdruck, tooltip) Dasselbe, aber im Browser entschieden.
.EnableRowDragAndDrop() Zeilen umsortierbar machen — siehe «Zeilen umsortieren (gridrowmoved)».
.ClientDataKey(name) Feldname der Auswahlkästchen, wenn die Zeilen aus einer Ajax-Datenquelle stammen. Bei BindTo liefert BulkEditCheckbox den Namen.
.DeleteConfirmMessage(text) Rückfrage vor dem Entfernen. Der Text darf Markup enthalten.
.NestedForm() Für Grids, die in einem umschliessenden Formular stehen — siehe «Mehrfachauswahl (bulkeditchange)».
.IsInAsyncAction() Für Aktionen, die per Ajax laufen.

.EnableRowClickNavigation() gibt es zwar, hat aber keine Wirkung: Der Wert wird gesetzt und nirgends gelesen. Für den Zeilenklick genügt .EnableRowClick().

Die Spalten

Methode Wirkung
spalten.For(x => x.Feld) Wertspalte aus einer Eigenschaft.
spalten.Custom(x => …, "Titel") Freie Spalte; der Ausdruck liefert den Inhalt, auch HTML. Auch für berechnete Werte.
spalten.Button(x => FmhGrid.…) Aktionsspalte, siehe «Aktionen je Zeile».
spalten.BulkEditCheckbox(x => x.Id) Auswahlkästchen für Mehrfachaktionen.
spalten.ChildFor(icon, x => x.Feld, x => …) Aufklappbare Unterzeile, Inhalt steht beim Rendern fest.
spalten.ChildForAsync(icon, x => x.Anzahl, url) Unterzeile, deren Inhalt erst beim Aufklappen geholt wird — siehe «Unterzeilen nachladen».

Am Spaltenobjekt weiter:

Methode Wirkung
.Named(titel) Spaltenüberschrift. Ohne Angabe der Eigenschaftsname.
.Format("{0:dd.MM.yyyy}") Formatzeichenkette für den Wert.
.Format(GridValueFormat.YesNo) Wandelt einen bool in «Ja»/«Nein».
.Format(GridValueFormat.DataSize) Wandelt Bytes in eine lesbare Grösse. Der Wert muss ein int sein.
.IsSortable() Spalte sortierbar machen.
.SortByKey(x => x.Feld) Nach einem anderen Wert sortieren als angezeigt wird.
.Visible(false) Spalte vorhanden, aber ausgeblendet.
.WidthInPixel(120) Feste Breite.
.ResponsivePriority(n) Reihenfolge beim Zusammenklappen; kleiner bleibt länger sichtbar.
.Tooltip(x => x.Feld) Kurzinfo an der Zelle bzw. an der Aufklapp-Schaltfläche.
.ColumnIcon(IconType.…) Symbol der Aufklapp-Schaltfläche; identisch mit dem ersten Parameter von ChildFor.
.ChildRowColumnText(x => x.Feld, stil) Wert neben dem Symbol der Aufklapp-Spalte — siehe «Unterzeilen-Spalte darstellen».
.CellAttributes(new { … }) / .CellAttributes(x => …) Attribute an der Zelle — fest oder je Zeile.
.ToggleStatus(x => x.Feld) Zustand für eine Umschalt-Schaltfläche.
.OpenFirstChildRowWithContent() Erste Unterzeile mit Inhalt beim Laden öffnen.
.ExpandOpenChildRows(false) Geöffnete Unterzeilen offen lassen.
.IsHtmlEncoded(false) Rohes HTML zulassen. Nur für Werte aus vertrauenswürdiger Quelle.

Die Ereignisse

Das Grid meldet im Browser, was es tut. Alle Ereignisse steigen bis zum document auf — ein Listener dort genügt also auch für Zeilen, die erst beim Blättern entstehen.

Ereignis Wann Wichtige Felder in detail Beispiel
griddataloaded Die Antwort des Endpunkts ist da. id, data, totalRecords, totalDisplayRecords «Auf geladene Daten reagieren (griddataloaded)»
gridRowCreated Eine Zeile ist gebaut, aber noch nicht sichtbar. rowIndex, row, element (das <tr>), tableId «Zeilen auszeichnen (gridRowCreated)»
gridCellCreated Eine Zelle ist gebaut. rowIndex, colIndex, row, cell (der gebundene Wert), element (das <td>), tableId «Zellen auszeichnen (gridCellCreated)»
gridButtonClick Eine Aktion wurde geklickt, vor der Ausführung. rowIndex, row, element (die Schaltfläche), tableId «Aktion vor dem Ausführen abfangen (gridButtonClick)»
gridAsyncButtonClicked Ein Postback im Hintergrund ist durch und die Tabelle neu geladen. rowIndex, row, tableId «Nach einer Aktion nachziehen (gridAsyncButtonClicked)»
bulkeditchange Eine Auswahl hat sich geändert. name, id, checked, totalChecked «Mehrfachauswahl (bulkeditchange)»
gridrowmoved Eine Zeile wurde verschoben. rowIndex, rowIndexBefore, row, element, order «Zeilen umsortieren (gridrowmoved)»
childrowclick Eine Unterzeile wurde auf- oder zugeklappt. id (mit #), childTableId, rowIndex, row «Unterzeilen»

Dazu zwei globale Funktionen, die die Komponente im Browser mitbringt — beide erwarten die Id mit vorangestelltem # und nur bei einer Ajax-Datenquelle:

Funktion Wirkung
gridReload('#id') Lädt die Zeilen neu und springt auf die erste Seite — nach einer Änderung am eigenen Filterformular (siehe «Suchformular über der Tabelle»).
gridReloadFromUrl('#id', adresse) Tauscht die Adresse des Endpunkts aus und lädt neu — für Filterwerte, die in der Adresse stehen.

detail.cell ist nicht der angezeigte Text. Es ist der Wert, an den die Spalte gebunden ist — und das ist bei einem SortByKey(x => x.Feld) das Sortierfeld, nicht die angezeigte Eigenschaft. Wer auf den Inhalt einer Zelle reagieren will, liest ihn über detail.row.

detail.buttonId sagt nicht, welche Aktion es war. Bei gridButtonClick und gridAsyncButtonClicked steht dort die technische Id der Schaltfläche (btn + UUID), die bei jedem Aufbau der Tabelle wechselt. Auseinanderhalten lassen sich die Aktionen bei gridButtonClick über detail.elementdata-actionbar-btn nennt die Art der Schaltfläche (View, Delete, Custom), href das Ziel. Bei gridAsyncButtonClicked zeigt detail.element nicht auf die Schaltfläche; dort bleibt nur detail.row.

Die Id der Tabelle steht nicht überall gleich im detail. Beim Filtern auf die eigene Tabelle darauf achten:

Ereignis Feld Schreibweise
griddataloaded detail.id mit #
gridButtonClick detail.tableId mit #
gridRowCreated, gridCellCreated, gridAsyncButtonClicked detail.tableId ohne #

Die fünf Ereignisse der Ajax-Tabelle sind der Ersatz für alles, was serverseitig je Zeile lief:

Serverseitig Auf dem Ajax-Weg
Zeile abhängig von ihren Daten auszeichnen gridRowCreateddetail.element.classList.add(…)
.CellAttributes(x => …) gridCellCreated
.EmptyColumnChar(zeichen) gridCellCreated — Platzhalter in die leere Zelle schreiben
Route-Werte je Zeile ausser id gridButtonClick — Adresse vor der Ausführung ergänzen
Nach einer Aktion die Seite neu aufbauen gridAsyncButtonClicked

Server oder Ajax: was wo wirkt

Die Datenquelle entscheidet, wo eine Zeile entsteht — und damit, welche Angabe überhaupt eine Wirkung hat. Mit BindTo rendert der Server jede Zelle fertig. Mit AjaxDataUrl baut der Browser sie aus dem JSON des Endpunkts; die Razor-Ausdrücke laufen dann trotzdem, aber nur einmal beim Aufbau der Seite und mit einer leeren Zeileninstanz. Sie sehen nie einen echten Wert und wirken deshalb nicht je Zeile.

Daraus folgt die Faustregel: Was wie x => … aussieht und mehr tut, als die Spalte zu beschreiben, gilt nur mit BindTo. Der Ersatz ist immer einer von zweien — den fertigen Wert aus dem Endpunkt liefern oder einen JavaScript-Ausdruck über row. angeben.

Builder

Aufruf BindTo AjaxDataUrl Ersatz bzw. Hinweis
.BindTo(daten) Beides zusammen bringt nichts: Die gerenderten Zeilen ersetzt das Grid beim ersten Laden.
.AjaxDataUrl(url, methode) Ohne Angabe der Methode POST. Zusatzwerte für den Endpunkt stehen entweder in der Adresse oder in einem Formular der Seite — siehe Rubrik «Filtern».
.ClientDataKey(name) nur Feldname der Kästchen Auf dem Ajax-Weg füllt er zusätzlich die Adressen der Zeilen-Aktionen.
.EnableSearchBox() Gesucht wird im Endpunkt; er bekommt den Begriff in Search.
.PageSizeMenu(…) / .DefaultPageSize(n) / .HidePaginationIfSinglePage() Mit BindTo steht paging fest auf false.
.EnableSorting(spalte, richtung) Auf dem Ajax-Weg sortiert der Endpunkt (SortColumnName, SortDirection).
.DisableRow(bedingung, einstellungen) .DisableRowClientExpression(ausdruck, tooltip)
.DisableRowClientExpression(ausdruck, tooltip) Sperrt alle Aktionen der Zeile, auch Ansehen.
.EmptyColumnChar(zeichen) Kein Ersatz im Builder: Platzhalter in die Datenquelle legen oder über gridCellCreated setzen.
.NoRecordsText(text) Auf dem Ajax-Weg gilt derselbe Text auch für die erfolglose Suche.
.Id() / .NestedForm() / .DeleteConfirmMessage() / .EnableRowClick() / .EnableRowDragAndDrop() / .DisableResponsiveMode() / .IsInAsyncAction()

Spalten

Aufruf BindTo AjaxDataUrl Ersatz bzw. Hinweis
spalten.For(x => x.Feld) Auf dem Ajax-Weg bindet die Spalte an den Eigenschaftsnamen im JSON.
spalten.Custom(x => …, "Titel") Wert im Endpunkt aufbereiten und mit For binden. Sonst zeigt die Spalte den Wert aus ClientDataKey.
spalten.BulkEditCheckbox(x => x.Id) Auf dem Ajax-Weg ist der Feldname ClientDataKey, nicht der Spaltenname.
spalten.ChildFor(icon, x => x.Liste, x => …) ChildForAsync
spalten.ChildForAsync(icon, x => x.Anzahl, url) Auf dem Ajax-Weg hängt das Grid die Zeilen-Id an die Adresse. Beim serverseitigen Rendern tut es das nicht — dort die Überladung mit x => … verwenden, die je Zeile eine Adresse liefert.
.Format(…) Fertigen Text aus dem Endpunkt liefern. Ausnahme: Datumsspalten stellt der Browser selbst dar (dd.MM.yyyy).
.IsHtmlEncoded(false) Auf dem Ajax-Weg landet der Wert ohnehin als Markup in der Zelle; mit SortByKey läuft er vorher durch den Sanitizer.
.CellAttributes(new { … }) / .CellAttributes(x => …) gridCellCreated
.Tooltip(x => …) Siehe «Zwei Fallen» unten.
.ChildRowColumnText(x => …, stil) .ChildRowColumnTextClientProperty(name, stil)
.OpenFirstChildRowWithContent() / .ExpandOpenChildRows(false) Gehört zu ChildFor und damit zum serverseitigen Weg.
.Named() / .IsSortable() / .SortByKey() / .Visible() / .WidthInPixel() / .ResponsivePriority() / .ToggleStatus()

Zeilen-Aktionen

Aufruf BindTo AjaxDataUrl Ersatz bzw. Hinweis
.Disabled(bedingung, tooltip) .DisabledClientExpression(ausdruck, tooltip)
.Visible(bedingung) .VisibleClientExpression(ausdruck, stil)
Route-Werte je Zeile (new { id = x.Id, filter = x.Feld }) nur id Alle übrigen Werte werden einmal mit der leeren Zeile berechnet. Zeilenwerte über gridButtonClick anhängen.
.ClientDataKey("row.Feld") Setzt einen anderen Wert als die Id in die Adresse.
.DialogMessage(text) Auf dem Ajax-Weg entweder ein fester Text oder ein JavaScript-Ausdruck, der row. enthält.
.RenderAsFormAction() Auf dem Ajax-Weg läuft die Aktion ohnehin als Postback.
.Attributes(new { data_asyncactiondisabled = "true" }) Schaltet den Postback für diese Aktion ab; der Endpunkt muss dann selbst zurückleiten.
.Property(name) / .Tooltip(text) / .Attributes(…)

Die Ausdrücke haben nicht dieselbe Polarität

Aufruf Ausdruck trifft zu →
.VisibleClientExpression(ausdruck) Aktion ist sichtbar
.DisabledClientExpression(ausdruck) Aktion ist bedienbar
.DisableRowClientExpression(ausdruck, tooltip) Zeile ist gesperrt

Die mittlere Zeile überrascht am ehesten: DisabledClientExpression beschreibt trotz seines Namens den bedienbaren Zustand — gesperrt wird, wenn der Ausdruck nicht zutrifft.

Zwei Fallen

  • Tooltip(x => …), CellAttributes(x => …) und ChildRowColumnText(x => …) setzen intern denselben Sortierschlüssel wie SortByKey. Mit AjaxDataUrl bindet die Spalte damit an die Eigenschaft aus diesem Ausdruck: Angezeigt wird zwar weiterhin der richtige Wert, sortiert und gesucht aber über die falsche Spalte. Auf dem Ajax-Weg deshalb nicht verwenden.
  • Die Antwort des Endpunkts muss in Pascal Case kommen. Das Grid liest Datasource, TotalRecords, TotalDisplayRecords und Draw; der Json-Serializer schreibt ohne Zutun Camel Case, und die Tabelle bliebe leer — siehe «Grundfall» der Gruppe «Datasource (Ajax)».