Skip to content

Mit dem Table-Modul erstellst du übersichtliche und interaktive Tabellen, die sich perfekt für die Darstellung und Verwaltung deiner Daten eignen. Wie immer steht bei uns die Performance im Fokus, gefolgt von maximaler Flexibilität. Und auch über die vielen Features wirst du dich freuen, unter anderem:

  • Gruppierungen in unendlich vielen Ebenen
  • Nativ eingebaute Checkboxen - kein Zusatzmodul nötig
  • Definiere Actions pro Zeile, pro Zelle oder beides!
  • Class- und Style-Keys soweit das Auge reicht

Funktion und Aussehen

Unser Table-Modul besteht aus drei Hauptabschnitten: Dem Header, dem Body und dem Footer. Im Header werden die Spaltenbreiten definiert, die sich dank Subgrid-Dynamik auf Body und Footer übertragen. Ansonsten kannst du alle drei Abschnitte unabhängig voneinander nach deinen Vorstellungen stylen, sowohl mit dem Class-Key, als auch mit dem Style-Key. Mit dem Class-Key kannst du CSS-Klassen zuweisen, die von dir oder uns im Globalen Code definiert wurden. Damit kannst du kundenspezifische Stylings in Windeseile auf alle verbauten Tabellen anwenden. Zusätzlich kannst du den Style-Key für Inline-CSS nutzen. Beachte dabei, dass Inline-Styles immer Vorrang haben und die Class-Styles überschreiben, sofern sie dieselbe Property (z.B. background-color) betreffen.

Übersichtliche Gruppierung

Du kannst deine Daten in unendlich vielen Ebenen übersichtlich gruppieren und nach Bedarf ein- oder ausklappen. So können verschachtelte Ninox-Tabellen ganz einfach greifbar gemacht werden. Möchtest du z.B. eine Projekt-Verwaltung darstellen mit den Projekten auf 1. Ebene, Projektphasen in der 2. Ebene und den Aufgaben in diesen Projektphasen in der 3. Ebene? Kein Problem!

Die Gruppierungsfunktion macht deine Daten übersichtlich und leicht zugänglich. Du entscheidest selbst, welche Gruppen ein- oder ausgeklappt werden sollen. Mit dem "clickToCollapse"-Key kannst du festlegen, wo Benutzer klicken müssen, um Gruppen zu öffnen oder zu schließen. Zusätzlich gibt dir der Collapsable-Key die Möglichkeit, das Einklappen gleich für die komplette Gruppe zu deaktivieren. Außerdem hast du die Möglichkeit mit dem Collapsed-Key festzulegen, ob deine Nutzer die Gruppe beim ersten Laden ein- oder ausgeklappt sehen sollen.

Einfache Checklisten

Dank der nativ eingebauten Checkbox-Option kannst du schnell und unkompliziert Checklisten bauen. Du kannst sowohl einzelne Einträge oder gleich ganze Gruppen auswählen. Die verschiedenen Zustände der Gruppen-Checkboxen (ausgewählt, nicht ausgewählt, teilweise ausgewählt) geben dir einen schnellen Überblick über deine Auswahl. Du hast sogar die Möglichkeit, die jeweiligen Symbole selbst festzulegen, z.B. indem du unsere Material Symbols nutzt.

Optimale Performance

Mit unserer Table reizen wir die Grenzen der Ninox-Performance aus. Du kannst problemlos große Datenmengen darstellen und bearbeiten.

Nutzer-Interaktion

Wie die meisten unserer Module ist auch die Table mit dem Actions-Key ausgestattet. Lege vollkommen frei fest, welche und wie viele Actions du pro Zeile oder pro Zelle haben möchtest. Jede Action besteht aus einem auslösenden Trigger und einem Array von Scripts, die bei Auslösen des Triggers ausgeführt werden. Als Trigger können Klick, Mausbewegung und alle weiteren HTML Mouse Events eingesetzt werden. Je nach Anwendung können auch andere HTML Events funktionieren. Eine Liste solcher Events findest du beispielsweise hier. Weitere Infos zu Actions findest du in diesem Teil unserer Doku.

Modulcode

json
GIP_table({
   uniqueId: "myUniqueName" + Nr
   embedded: true
   header: {
   	headerColumns: [{
   		columnWidth: "1fr"
   		value: *any*
   	}],
   },
   body: {
   	rowHeight: "40px"
   	rows: [{
   		columns: [{
   			value: *any*
   		}],
   	}],
   },
})
json
GIP_table({
   uniqueId: "myUniqueName" + Nr
   embedded: true
   style: ""
   header: {
   	style: ""
   	headerColumns: [{
   		style: ""
   		columnWidth: "1fr"
   		value: *any*
   	}],
   },
   body: {
   	style: ""
   	rowHeight: "40px"
   	rows: [{
   		style: ""
   		columns: [{
   			style: ""
   			hoverElement: {
   				style: ""
   				value: *any*
   			},
   			value: *any*
   		}],
   	}],
   	groups: [{
   		style: ""
   		groupColumns: [{
   			style: ""
   			hoverElement: {
   				style: ""
   				value: *any*
   			},
   			value: *any*
   		}],
   		rows: [{
   			style: ""
   			columns: [{
   				style: ""
   				hoverElement: {
   					style: ""
   					value: *any*
   				},
   				value: *any*
   			}],
   		}],
   	}],
   },
})
json
GIP_table({
   uniqueId: "myUniqueName" + Nr
   embedded: true
   class: ""
   style: ""
   showCheckboxes: true
   header: {
   	class: ""
   	style: ""
   	headerColumns: [{
   		style: ""
   		columnWidth: "1fr"
   		value: *any*
   		actions: [{
   			trigger: "click"
   			scripts: [{
   				type: ""
   			}],
   		}],
   	}],
   	actions: [{
   		trigger: "click"
   		scripts: [{
   			type: ""
   		}],
   	}],
   },
   body: {
   	class: ""
   	style: ""
   	rowHeight: "40px"
   	placeholder: {
   		style: ""
   		value: *any*
   	},
   	rows: [{
   		class: ""
   		style: ""
   		columns: [{
   			style: ""
   			hoverElement: {
   				class: ""
   				style: ""
   				value: *any*
   				actions: [{
   					trigger: "click"
   					scripts: [{
   						type: ""
   					}],
   				}],
   			},
   			value: *any*
   			actions: [{
   				trigger: "click"
   				scripts: [{
   					type: ""
   				}],
   			}],
   		}],
   		actions: [{
   			trigger: "click"
   			scripts: [{
   				type: ""
   			}],
   		}],
   	}],
   	groups: [{
   		class: ""
   		style: ""
   		groupColumns: [{
   			style: ""
   			hoverElement: {
   				class: ""
   				style: ""
   				value: *any*
   				actions: [{
   					trigger: "click"
   					scripts: [{
   						type: ""
   					}],
   				}],
   			},
   			value: *any*
   			actions: [{
   				trigger: "click"
   				scripts: [{
   					type: ""
   				}],
   			}],
   		}],
   		rows: [{
   			class: ""
   			style: ""
   			columns: [{
   				style: ""
   				hoverElement: {
   					class: ""
   					style: ""
   					value: *any*
   					actions: [{
   						trigger: "click"
   						scripts: [{
   							type: [{}]
   						}],
   					}],
   				},
   				value: *any*
   				actions: [{
   					trigger: "click"
   					scripts: [{
   						type: ""
   					}],
   				}],
   			}],
   			actions: [{
   				trigger: "click"
   				scripts: [{
   					type: ""
   				}],
   			}],
   		}],
   		groups: [{
   		}],
   		actions: [{
   			trigger: "click"
   			scripts: [{
   				type: ""
   			}],
   		}],
   	}],
   },
   footer: {
   	class: ""
   	style: ""
   	footerColumns: [{
   		style: ""
   		value: *any*
   		actions: [{
   			trigger: "click"
   			scripts: [{
   				type: ""
   			}],
   		}],
   	}],
   	actions: [{
   		trigger: "click"
   		scripts: [{
   			type: ""
   		}],
   	}],
   },
})
json
GIP_table({
   uniqueId: "",
   embedded: {
   	height: "",
   	styleHtml: "",
   	styleStri: "",
   },
   class: "",
   style: "",
   showCheckboxes: {
   	iconSelected: true / *any*,
   	iconUnselected: true / *any*,
   	iconIndeterminate: true / *any*,
   },
   header: {
   	class: "",
   	style: "",
   	checkbox: {
   		recordId: "",
   		fieldId: "",
   		value: *any*,
   	},
   	headerColumns: [{
   		style: "",
   		columnWidth: "",
   		value: *any*,
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   	actions: [{
   		trigger: "",
   		scripts: [{
   			type: "",
   		}],
   	}],
   },
   body: {
   	class: "",
   	style: "",
   	rowHeight: "",
   	placeholder: {
   		style: "",
   		value: *any*,
   	},
   	rows: [{
   		class: "",
   		style: "",
   		checkbox: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		columns: [{
   			style: "",
   			hoverElement: {
   				class: "",
   				style: "",
   				value: *any*,
   				actions: [{
   					trigger: "",
   					scripts: [{
   						type: "",
   					}],
   				}],
   			},
   			value: *any*,
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		}],
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   	groups: [{
   		collapseChildren: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		collapsible: true,
   		class: "",
   		style: "",
   		checkbox: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		groupColumns: [{
   			clickToCollapse: true,
   			style: "",
   			hoverElement: {
   				class: "",
   				style: "",
   				value: *any*,
   				actions: [{
   					trigger: "",
   					scripts: [{
   						type: "",
   					}],
   				}],
   			},
   			value: *any*,
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		}],
   		rows: [{
   			class: "",
   			style: "",
   			checkbox: {
   				recordId: "",
   				fieldId: "",
   				value: *any*,
   			},
   			columns: [{
   				style: "",
   				hoverElement: {
   					class: "",
   					style: "",
   					value: *any*,
   					actions: [{
   						trigger: "",
   						scripts: [{
   							type: "",
   						}],
   					}],
   				},
   				value: *any*,
   				actions: [{
   					trigger: "",
   					scripts: [{
   						type: "",
   					}],
   				}],
   			}],
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		}],
   		groups: [{
   		}],
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   },
   footer: {
   	class: "",
   	style: "",
   	footerColumns: [{
   		style: "",
   		value: *any*,
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   	actions: [{
   		trigger: "",
   		scripts: [{
   			type: "",
   		}],
   	}],
   },
})
json
html( raw(GIP_master({})) + 
raw(GIP_table({
   uniqueId: "",
   embedded: {
   	height: "",
   	styleHtml: "",
   	styleStri: "",
   },
   class: "",
   style: "",
   showCheckboxes: {
   	iconSelected: true / *any*,
   	iconUnselected: true / *any*,
   	iconIndeterminate: true / *any*,
   },
   header: {
   	class: "",
   	style: "",
   	checkbox: {
   		recordId: "",
   		fieldId: "",
   		value: *any*,
   	},
   	headerColumns: [{
   		style: "",
   		columnWidth: "",
   		value: *any*,
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   	actions: [{
   		trigger: "",
   		scripts: [{
   			type: "",
   		}],
   	}],
   },
   body: {
   	class: "",
   	style: "",
   	rowHeight: "",
   	placeholder: {
   		style: "",
   		value: *any*,
   	},
   	rows: [{
   		class: "",
   		style: "",
   		checkbox: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		columns: [{
   			style: "",
   			hoverElement: {
   				class: "",
   				style: "",
   				value: *any*,
   				actions: [{
   					trigger: "",
   					scripts: [{
   						type: "",
   					}],
   				}],
   			},
   			value: *any*,
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		}],
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   	groups: [{
   		collapseChildren: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		collapsible: true,
   		class: "",
   		style: "",
   		checkbox: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		groupColumns: [{
   			clickToCollapse: true,
   			style: "",
   			hoverElement: {
   				class: "",
   				style: "",
   				value: *any*,
   				actions: [{
   					trigger: "",
   					scripts: [{
   						type: "",
   					}],
   				}],
   			},
   			value: *any*,
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		}],
   		rows: [{
   			class: "",
   			style: "",
   			checkbox: {
   				recordId: "",
   				fieldId: "",
   				value: *any*,
   			},
   			columns: [{
   				style: "",
   				hoverElement: {
   					class: "",
   					style: "",
   					value: *any*,
   					actions: [{
   						trigger: "",
   						scripts: [{
   							type: "",
   						}],
   					}],
   				},
   				value: *any*,
   				actions: [{
   					trigger: "",
   					scripts: [{
   						type: "",
   					}],
   				}],
   			}],
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		}],
   		groups: [{
   		}],
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   },
   footer: {
   	class: "",
   	style: "",
   	footerColumns: [{
   		style: "",
   		value: *any*,
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	}],
   	actions: [{
   		trigger: "",
   		scripts: [{
   			type: "",
   		}],
   	}],
   },
})
))

Key-Table

Beispiele

Table: Basic ohne Gruppen

Beschreibung

Wir möchten eine Table bauen, in der wir Projekt-Aufgaben anzeigen mit Informationen zum Kunden, Projekt, Verantwortlichen, Priorität und Status der Aufgabe. Zusätzlich möchten wir bei Hover über Kunden, Projekte und Aufgaben jeweils einen Button angezeigt bekommen, mit dem wir in den entsprechenden Record springen können.

Um alle Daten in unserer Tabelle anzeigen zu können, benötigen wir 6 Spalten. Wir beginnen damit, im header diese 6 Spalten als headerColumns anzulegen, ihnen jeweils einen Spaltentitel (value) und eine Breite (columnWidth) zu geben. Diese festgelegte Breite wird automatisch auf alle Zellen der Spalte übertragen, inkl. dem Footer.

json
headerColumns: [{
		style: "",
		columnWidth: "3fr", // Die Spaltenbreite kann in Einheiten wie Fraction (fr) oder Pixel (px) angegeben werden. Wenn "auto" angegeben wird, passt sich die Spalte auf den Inhalt des Headers an.
		value: "Kunde"
	}, {
		style: "",
		columnWidth: "2fr",
		value: "Projekt"
	}, {
		style: "",
		columnWidth: "2fr",
		value: "Aufgabe"
	}, {
		style: "",
		columnWidth: "1fr",
		value: "Verantwortlicher"
	}, {
		style: "",
		columnWidth: "1fr",
		value: "Priorität"
	}, {
		style: "",
		columnWidth: "1fr",
		value: "Status"
	}]
}

Nach der Definition des Headers füllen wir den Body mit Daten. Wir vergeben eine feste rowHeight, sodass alle Zeilen der Tabelle eine einheitliche Höhe haben. Es kann ein Platzhalter definiert werden, für den Fall, dass keine Zeilen in der Tabelle angezeigt werden (z.B. weil es noch keine Aufgaben gibt oder weil mit den aktiven Filtern keine Aufgaben gefunden wurden).

Um den nun die Zeilen der Tabelle zu übergeben, nutzen wir den Key rows. Hierin iterieren wir durch unsere Aufgaben (projectTasks), um eine Zeile pro Aufgabe zu erhalten. Pro Zeile legen wir im Key columns die Zellen (Spalten) der Zeile fest. Jede Zelle besitzt einen value und kann ihr eigenes Styling haben. Außerdem legen wir für ausgewählte Spalten ein hoverElement fest. Dies ist ein Button, der beim Überfahren der Zelle mit der Maus erscheint. Als Action legen wir fest, dass der jeweilige Record des Kunden, des Projektes oder der Aufgabe geöffnet werden soll.

Wichtig ist, dass wir in den columns genauso viele Spalten definieren, wie wir headerCoulmns definiert haben. Ansonsten kommt es zu falschen Anzeigen und Umbrüchen der Spalten.

json
rows: for task in projectTasks do
	task.{
		style: "",
		columns: [{
				style: "",
				hoverElement: if Projekte.Kunde then
					{
						class: "",
						style: "",
						value: "Kunde öffnen",
						actions: [{
								trigger: "click",
								scripts: [{
										type: "popup",
										recordId: Projekte.Kunde.Nr
									}]
							}]
					}
				end,
				value: Projekte.Kunde.Kundenname
			}, {
				style: "",
				hoverElement: if Projekte then
					{
						class: "",
						style: "",
						value: "Projekt öffnen",
						actions: [{
								trigger: "click",
								scripts: [{
										type: "popup",
										recordId: Projekte.Nr
									}]
							}]
					}
				end,
				value: Projekte.Projektname
			}, {
				style: "",
				hoverElement: {
					class: "",
					style: "",
					value: "Aufgabe öffnen",
					actions: [{
							trigger: "click",
							scripts: [{
									type: "popup",
									recordId: Nr
								}]
						}]
				},
				value: Bezeichnung
			}, {
				style: "",
				value: Verantwortlicher.Name
			}, {
				style: "",
				value: text('Priorität')
			}, {
				style: "",
				value: Status.Bezeichnung
			}]
	}
end

Schließlich können wir auch für den Footer noch footerColumns festlegen, z.B. um die Anzahl der abgeschlossenen Aufgaben anzugeben. Die Nutzung des Footers ist optional, aber wenn er verwendet wird, muss auch hier auf die richtige Anzahl der Spalten geachtet werden.

json
footerColumns: [{
		style: "",
		value: cnt(unique(projectTasks.Projekte.Kunde)) + " Kunden"
	}, {
		style: "",
		value: cnt(unique(projectTasks.Projekte)) + " Projekte"
	}, {
		style: "",
		value: cnt(projectTasks) + " Aufgaben"
	}, {
		style: "",
		value: cnt(unique(projectTasks.Verantwortlicher)) + " Verantwortliche"
	}, {
		style: "",
		value: ""
	}, {
		style: "",
		value: cnt(projectTasks[number(Status) = 5]) + " Abgeschlossen"
	}]

Hinweis: Mit den richtigen CSS-Stylings können Zellen auch spaltenübergreifend zusammengefasst werden, z.B. um eine durchgehende Footer-Zeile zu erzeugen.

Code des Beispiels
json
let projectTasks := (select Aufgaben);
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "OhneGruppierung" + Nr,
		embedded: false,
		class: "",
		style: "height: 500px;",
		header: {
			class: "",
			style: "",
			headerColumns: [{
					style: "",
					columnWidth: "3fr",
					value: "Kunde"
				}, {
					style: "",
					columnWidth: "2fr",
					value: "Projekt"
				}, {
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			rows: for task in projectTasks do
				task.{
					style: "",
					columns: [{
							style: "",
							hoverElement: if Projekte.Kunde then
								{
									class: "",
									style: "",
									value: "Kunde öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Projekte.Kunde.Nr
												}]
										}]
								}
							end,
							value: Projekte.Kunde.Kundenname
						}, {
							style: "",
							hoverElement: if Projekte then
								{
									class: "",
									style: "",
									value: "Projekt öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Projekte.Nr
												}]
										}]
								}
							end,
							value: Projekte.Projektname
						}, {
							style: "",
							hoverElement: {
								class: "",
								style: "",
								value: "Aufgabe öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: Bezeichnung
						}, {
							style: "",
							value: Verantwortlicher.Name
						}, {
							style: "",
							value: text('Priorität')
						}, {
							style: "",
							value: Status.Bezeichnung
						}]
				}
			end
		},
		footer: {
			class: "",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(unique(projectTasks.Projekte.Kunde)) + " Kunden"
				}, {
					style: "",
					value: cnt(unique(projectTasks.Projekte)) + " Projekte"
				}, {
					style: "",
					value: cnt(projectTasks) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(projectTasks.Verantwortlicher)) + " Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(projectTasks[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
let projectTasks := (select Aufgaben);
GIP_table({
	uniqueId: "OhneGruppierung" + Nr,
	embedded: true,
	class: "",
	style: "height: 500px;",
	header: {
		class: "",
		style: "",
		headerColumns: [{
				style: "",
				columnWidth: "3fr",
				value: "Kunde"
			}, {
				style: "",
				columnWidth: "2fr",
				value: "Projekt"
			}, {
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		rows: for task in projectTasks do
			task.{
				style: "",
				columns: [{
						style: "",
						hoverElement: if Projekte.Kunde then
							{
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Projekte.Kunde.Nr
											}]
									}]
							}
						end,
						value: Projekte.Kunde.Kundenname
					}, {
						style: "",
						hoverElement: if Projekte then
							{
								class: "",
								style: "",
								value: "Projekt öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Projekte.Nr
											}]
									}]
							}
						end,
						value: Projekte.Projektname
					}, {
						style: "",
						hoverElement: {
							class: "",
							style: "",
							value: "Aufgabe öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: Bezeichnung
					}, {
						style: "",
						value: Verantwortlicher.Name
					}, {
						style: "",
						value: text('Priorität')
					}, {
						style: "",
						value: Status.Bezeichnung
					}]
			}
		end
	},
	footer: {
		class: "",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(unique(projectTasks.Projekte.Kunde)) + " Kunden"
			}, {
				style: "",
				value: cnt(unique(projectTasks.Projekte)) + " Projekte"
			}, {
				style: "",
				value: cnt(projectTasks) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(projectTasks.Verantwortlicher)) + " Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(projectTasks[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})

Table: Eine Gruppenebene

Beschreibung

Wir möchten eine Table bauen, in der wir Projekt-Aufgaben anzeigen mit Informationen zum Kunden, Projekt, Verantwortlichen, Priorität und Status der Aufgabe. Für eine bessere Übersichtlichkeit möchten wir nach dem Kunden gruppieren.

Wie üblich beginnen wir damit, unseren header und die headerColumns zu definieren. Allerdings brauchen wir hier nur 5 Spalten, da wir unseren gruppierten Wert nicht in einer eigenen Spalte darstellen müssen.

json
headerColumns: [{
		style: "",
		columnWidth: "2fr",
		value: "Projekt"
	}, {
		style: "",
		columnWidth: "2fr",
		value: "Aufgabe"
	}, {
		style: "",
		columnWidth: "1fr",
		value: "Verantwortlicher"
	}, {
		style: "",
		columnWidth: "1fr",
		value: "Priorität"
	}, {
		style: "",
		columnWidth: "1fr",
		value: "Status"
	}]
}

Um unsere Gruppierung zu erhalten, verwenden wir im body den Key groups. Als Array of Objects übergeben wir an dieser Stelle alle Gruppen, also alle Kunden. Für jede Gruppe können wir Einstellungen (z.B. style, class,...), Gruppenspalten (groupColumns) und untergeordnete Zeilen (rows) übergeben.

Die Definition der groupColumns ist ähnlich zu den normalen Columns: Wir übergeben eine Array ob Objects, wobei jedes Object eine Spalte ist. Für jede Spalte der Gruppenzeilen können wir Stylings, den anzuzeigenden Wert und Actions festlegen. Im Gegensatz zu den normalen Columns besitzt groupColumns noch den Key clickToCollapse. Wenn beispielsweise ein Button in einer Spalte enthalten ist, können wir mit clickToCollapse: false verhindern, dass die Gruppe bei jeden Klick auf den Button ein-/aufklappt.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {
		style: "",
		value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
	},
	groups: for costumer in projectCostumers do
		costumer.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [{
					style: "",
					clickToCollapse: true,
					hoverElement: {
						class: "",
						style: "",
						value: "Kunde öffnen",
						actions: [{
								trigger: "click",
								scripts: [{
										type: "popup",
										recordId: Nr
									}]
							}]
					},
					value: Kundenname
				}, {
					clickToCollapse: true,
					style: "",
					value: ""
				}, {
					clickToCollapse: true,
					style: "",
					value: ""
				}, {
					clickToCollapse: true,
					style: "",
					value: ""
				}, {
					clickToCollapse: true,
					style: "",
					value: ""
				}],
			rows: [{}]
		}
	}

Für die komplette Gruppe legen wir mit dem Key collapsible fest, ob sie überhaupt ein-/ausklappbar sein soll. Zusätzlich können wir mit collapseChildren einstellen, ob die Gruppe beim ersten Laden der Table ausgeklappt sein soll (mit einem Object statt einem Boolean-Wert könnte hier auch auf ein Ninox 'Ja / Nein'-Feld verwiesen werden).

In rows übergeben wir nun diejenigen Aufgaben, die den Projekten des aktuellen Kunden zugeordnet sind. Pro Aufgabe bauen wir nun wie üblich unsere Row-Objects.

Wichtig ist, dass wir in den groupColumns und columns genauso viele Spalten definieren, wie wir headerCoulmns definiert haben. Ansonsten kommt es zu falschen Anzeigen und Umbrüchen der Spalten.

Hinweis: Mit den richtigen CSS-Stylings können Zellen auch spaltenübergreifend zusammengefasst werden, z.B. um eine durchgehende Footer-Zeile zu erzeugen.

Code des Beispiels
json
let projectCostumers := (select Kunden);
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "MitEinerGruppe" + Nr,
		embedded: false,
		class: "",
		style: "height: 500px;",
		header: {
			class: "",
			style: "",
			headerColumns: [{
					style: "",
					columnWidth: "2fr",
					value: "Projekt"
				}, {
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			groups: for costumer in projectCostumers do
				costumer.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{
							style: "",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: Kundenname
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}],
					rows: for task in Projekte.Aufgaben do
						task.{
							style: "",
							columns: [{
									style: "",
									hoverElement: if Projekte then
										{
											class: "",
											style: "",
											value: "Projekt öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Projekte.Nr
														}]
												}]
										}
									end,
									value: Projekte.Projektname
								}, {
									style: "",
									hoverElement: {
										class: "",
										style: "",
										value: "Aufgabe öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: Bezeichnung
								}, {
									style: "",
									value: Verantwortlicher.Name
								}, {
									style: "",
									value: text('Priorität')
								}, {
									style: "",
									value: Status.Bezeichnung
								}]
						}
					end
				}
			end
		},
		footer: {
			class: "",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(unique(projectCostumers.Projekte)) + " Projekte"
				}, {
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
					" Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
let projectCostumers := (select Kunden);
GIP_table({
	uniqueId: "MitEinerGruppe" + Nr,
	embedded: true,
	class: "",
	style: "height: 500px;",
	header: {
		class: "",
		style: "",
		headerColumns: [{
				style: "",
				columnWidth: "2fr",
				value: "Projekt"
			}, {
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		groups: for costumer in projectCostumers do
			costumer.{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				groupColumns: [{
						style: "",
						clickToCollapse: true,
						hoverElement: {
							class: "",
							style: "",
							value: "Kunde öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: Kundenname
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}],
				rows: for task in Projekte.Aufgaben do
					task.{
						style: "",
						columns: [{
								style: "",
								hoverElement: if Projekte then
									{
										class: "",
										style: "",
										value: "Projekt öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Projekte.Nr
													}]
											}]
									}
								end,
								value: Projekte.Projektname
							}, {
								style: "",
								hoverElement: {
									class: "",
									style: "",
									value: "Aufgabe öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: Bezeichnung
							}, {
								style: "",
								value: Verantwortlicher.Name
							}, {
								style: "",
								value: text('Priorität')
							}, {
								style: "",
								value: Status.Bezeichnung
							}]
					}
				end
			}
		end
	},
	footer: {
		class: "",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(unique(projectCostumers.Projekte)) + " Projekte"
			}, {
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
				" Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})

Table: Mehrere Gruppenebenen

Beschreibung

Wir möchten eine Table bauen, in der wir Projekt-Aufgaben anzeigen mit Informationen zum Kunden, Projekt, Verantwortlichen, Priorität und Status der Aufgabe. Für eine bessere Übersichtlichkeit möchten wir nach dem Kunden und dem Projekt gruppieren.

Wir beginnen wie im Beispiel zur Gruppierung auf einer Ebene und erzeugen für jeden Kunden eine Gruppe im Array of Objects von groups.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {
		style: "",
		value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
	},
	groups: for costumer in projectCostumers do
		costumer.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [{ }]
		},
	}

Nun übergeben wir aber keine rows innerhalb der einzelnen Kundengruppen, sondern eine weitere Ebene an groups. In dieser Untergruppen-Ebene erzeugen wir für jedes Projekt des Kunden ein Object, das wir mit denselben Optionen einstellen können, wie die erste Gruppierungsebene.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {
		style: "",
		value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
	},
	groups: for costumer in projectCostumers do
		costumer.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [{ }],
			groups: for project in costumer.Projekte do
				project.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{ }]
			},
		},
	}

Erst in der Untergruppe übergeben wir jeweils die Projektaufgaben in rows.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {
		style: "",
		value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
	},
	groups: for costumer in projectCostumers do
		costumer.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [{ }],
			groups: for project in costumer.Projekte do
				project.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{ }],
					rows: [{ }]
			},
		},
	}

Diese Verschachtelung von Gruppen und Untergruppen kann unendlich häufig fortgesetzt werden.

Code des Beispiels
json
let projectCostumers := (select Kunden);
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "MehrereGruppenebenen" + Nr,
		embedded: false,
		class: "",
		style: "height: 500px;",
		header: {
			class: "",
			style: "",
			headerColumns: [{
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			groups: for costumer in projectCostumers do
				costumer.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{
							style: "",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: Kundenname
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}],
					groups: for project in costumer.Projekte do
						project.{
							collapseChildren: true,
							collapsible: true,
							class: "",
							style: "",
							groupColumns: [{
									style: "",
									clickToCollapse: true,
									hoverElement: {
										class: "",
										style: "",
										value: "Projekt öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: Projektname
								}, {
									clickToCollapse: true,
									style: "",
									value: ""
								}, {
									clickToCollapse: true,
									style: "",
									value: ""
								}, {
									clickToCollapse: true,
									style: "",
									value: ""
								}],
							rows: for task in project.Aufgaben do
								task.{
									style: "",
									columns: [{
											style: "",
											hoverElement: {
												class: "",
												style: "",
												value: "Aufgabe öffnen",
												actions: [{
														trigger: "click",
														scripts: [{
																type: "popup",
																recordId: Nr
															}]
													}]
											},
											value: Bezeichnung
										}, {
											style: "",
											value: Verantwortlicher.Name
										}, {
											style: "",
											value: text('Priorität')
										}, {
											style: "",
											value: Status.Bezeichnung
										}]
								}
							end
						}
					end
				}
			end
		},
		footer: {
			class: "",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
					" Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
let projectCostumers := (select Kunden);
GIP_table({
	uniqueId: "MehrereGruppenebenen" + Nr,
	embedded: true,
	class: "",
	style: "height: 500px;",
	header: {
		class: "",
		style: "",
		headerColumns: [{
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		groups: for costumer in projectCostumers do
			costumer.{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				groupColumns: [{
						style: "",
						clickToCollapse: true,
						hoverElement: {
							class: "",
							style: "",
							value: "Kunde öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: Kundenname
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}],
				groups: for project in costumer.Projekte do
					project.{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						groupColumns: [{
								style: "",
								clickToCollapse: true,
								hoverElement: {
									class: "",
									style: "",
									value: "Projekt öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: Projektname
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}],
						rows: for task in project.Aufgaben do
							task.{
								style: "",
								columns: [{
										style: "",
										hoverElement: {
											class: "",
											style: "",
											value: "Aufgabe öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: Bezeichnung
									}, {
										style: "",
										value: Verantwortlicher.Name
									}, {
										style: "",
										value: text('Priorität')
									}, {
										style: "",
										value: Status.Bezeichnung
									}]
							}
						end
					}
				end
			}
		end
	},
	footer: {
		class: "",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
				" Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})

Table: Spaltenübergreifende Zellen

Beispiel Table Spaltenübergreifende Zellen

Beschreibung

In einer Tabelle mit Gruppen möchten wir die Spalten der Gruppenzeilen zusammenfassen, um z.B. längere Texte darin darstellen zu können. Das folgende Vorgehen kann auch auf Footer übertragen werden.

Wir definieren wie üblich unsere 5 Spalten in den headerColumns, um für die Tabelle allgemeine Spaltenbreiten festzulegen. In den groupColumns definieren wir im Gegensatz zum normalen Vorgehen nun aber nur eine einzige Spalte (ein Object im Array). Dieser einzelnen Spalte geben wir folgendes Styling:

json
groupColumns: [{
	style: "grid-column: 1 / -1; text-align: center;",
	clickToCollapse: true,
	value: let splitValue := split(Kundenname, "");
			let countSplitValue := cnt(splitValue);
			lpad(" " + slice(Kundenname, 0, countSplitValue / 2), 50, "~") + rpad(slice(Kundenname, countSplitValue / 2, countSplitValue) + " ", 50, "~")
	}],

Der CSS-Style "grid-column" ermöglicht es uns, eine Spannweite der Zelle zu definieren. In diesem Beispiel übergeben wir den Startindex der Zelle ("1", da wir in der ersten Spalte starten möchten) und den Endpunkt der Zelle. Der Endpunkt "-1" bedeutet, dass die Zelle die komplette Breite einnehmen soll, unabhängig von der tatsächlich Spaltenanzahl.

Weitere Infos, wie man mit "grid-column" Spalten zusammenführen kannst, findest du beispielsweise hier.

Code des Beispiels
json
let projectCostumers := (select Kunden);
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "SpaltenübergreifendeZellen" + Nr,
		embedded: false,
		class: "",
		style: "height: 500px;",
		header: {
			class: "",
			style: "",
			headerColumns: [{
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			groups: for costumer in projectCostumers do
				costumer.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{
							style: "grid-column: 1 / -1; text-align: center;",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: let splitValue := split(Kundenname, "");
							let countSplitValue := cnt(splitValue);
							lpad(" " + slice(Kundenname, 0, countSplitValue / 2), 50, "~") +
							rpad(slice(Kundenname, countSplitValue / 2, countSplitValue) + " ", 50, "~")
						}],
					groups: for project in costumer.Projekte do
						project.{
							collapseChildren: true,
							collapsible: true,
							class: "",
							style: "",
							groupColumns: [{
									style: "grid-column: 1 / -1; text-align: center;",
									clickToCollapse: true,
									hoverElement: {
										class: "",
										style: "",
										value: "Projekt öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: let splitValue := split(Projektname, "");
									let countSplitValue := cnt(splitValue);
									lpad(" " + slice(Projektname, 0, countSplitValue / 2), 50, "+") +
									rpad(slice(Projektname, countSplitValue / 2, countSplitValue) + " ", 50, "+")
								}],
							rows: for task in project.Aufgaben do
								task.{
									style: "",
									columns: [{
											style: "",
											hoverElement: {
												class: "",
												style: "",
												value: "Aufgabe öffnen",
												actions: [{
														trigger: "click",
														scripts: [{
																type: "popup",
																recordId: Nr
															}]
													}]
											},
											value: Bezeichnung
										}, {
											style: "",
											value: Verantwortlicher.Name
										}, {
											style: "",
											value: text('Priorität')
										}, {
											style: "",
											value: Status.Bezeichnung
										}]
								}
							end
						}
					end
				}
			end
		},
		footer: {
			class: "",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
					" Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
let projectCostumers := (select Kunden);
GIP_table({
	uniqueId: "SpaltenübergreifendeZellen" + Nr,
	embedded: true,
	class: "",
	style: "height: 500px;",
	header: {
		class: "",
		style: "",
		headerColumns: [{
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		groups: for costumer in projectCostumers do
			costumer.{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				groupColumns: [{
						style: "grid-column: 1 / -1; text-align: center;",
						clickToCollapse: true,
						hoverElement: {
							class: "",
							style: "",
							value: "Kunde öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: let splitValue := split(Kundenname, "");
						let countSplitValue := cnt(splitValue);
						lpad(" " + slice(Kundenname, 0, countSplitValue / 2), 50, "~") +
						rpad(slice(Kundenname, countSplitValue / 2, countSplitValue) + " ", 50, "~")
					}],
				groups: for project in costumer.Projekte do
					project.{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						groupColumns: [{
								style: "grid-column: 1 / -1; text-align: center;",
								clickToCollapse: true,
								hoverElement: {
									class: "",
									style: "",
									value: "Projekt öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: let splitValue := split(Projektname, "");
								let countSplitValue := cnt(splitValue);
								lpad(" " + slice(Projektname, 0, countSplitValue / 2), 50, "+") +
								rpad(slice(Projektname, countSplitValue / 2, countSplitValue) + " ", 50, "+")
							}],
						rows: for task in project.Aufgaben do
							task.{
								style: "",
								columns: [{
										style: "",
										hoverElement: {
											class: "",
											style: "",
											value: "Aufgabe öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: Bezeichnung
									}, {
										style: "",
										value: Verantwortlicher.Name
									}, {
										style: "",
										value: text('Priorität')
									}, {
										style: "",
										value: Status.Bezeichnung
									}]
							}
						end
					}
				end
			}
		end
	},
	footer: {
		class: "",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
				" Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})

Table: Checkboxen

Beschreibung

Wir möchten in einer Tabelle Checkboxen einbauen, um einzelne Aufgaben, ganze Gruppen und die komplette Table abhaken zu können.

Um überhaupt Checkboxen in unserer Table anzeigen zu können müssen wir für den Key showCheckboxes entweder true oder ein Object mit selbst festgelegten Checkbox-Icons übergeben.

json
GIP_table({
	uniqueId: "SpaltenübergreifendeZellen" + Nr,
	embedded: false,
	class: "",
	style: "height: 500px;",
	showCheckboxes: true,
})

Danach können wir entscheiden, auf welchen Ebenen wir Checkboxen anzeigen möchten: Header, Groups, Rows. In allen Ebenen, in denen Checkboxen angezeigt werden sollen, übergeben wir den Key checkbox mit einem Object. Dieses Object enthält jeweils Informationen zu einem Ninox-Feld, das durch die Checkbox aktualisiert werden soll. In den Rows, also unseren Aufgaben, ist dies ein 'Ja / Nein'-Ninox-Feld, da Checkboxen für Rows nur zwei Stati haben können: true oder false.

json
rows: for task in project.Aufgaben do
	task.{
		style: "",
		checkbox: {
			recordId: Nr,
			fieldId: fieldId(this, "Toggle für Checkbox-Status"),
			value: 'Toggle für Checkbox-Status'
		},
	}

Für Gruppen und Header kommt noch ein weiterer Status hinzu: Indeterminate, der als Zahlwert "2" von der Table an Ninox übergeben wird. Dieser wird gesetzt, wenn nur ein Teil der Aufgaben abgehakt ist. Um diese drei Stati abbilden zu können, brauchen Groups, also unsere Kunden und Projekte, jeweils ein Text- oder Zahlfeld. Dasselbe gilt für den Header.

json
groups: for project in costumer.Projekte do
	project.{
		collapseChildren: true,
		collapsible: true,
		class: "",
		style: "",
		checkbox: {
			recordId: Nr,
			fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
			value: 'Zahlfeld für Checkbox-Status'
		},
	}
json
header: {
	class: "",
	style: "",
	checkbox: {
		recordId: Nr,
		fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
		value: 'Zahlfeld für Checkbox-Status'
	},
}

Die Table hat nun Checkboxen und aktualisiert den Status in den jeweils verknüpften Ninox-Feldern. Allerdings möchten wir ja z.B. beim Aktualisieren eines Projektes auch alle dazugehörigen Aufgaben aktualisieren. Dieses Massenupdate muss Ninox-seitig gebaut werden, da hier die Performance mit dem Befehl "do as transaction" beschleunigt werden kann.

Für das Zahlfeld der Header Checkbox sieht der Trigger nach Änderung wie folgt aus:

do as transaction
	let allCostumers := (select Kunden);
	switch 'Zahlfeld für Checkbox-Status' do
	case 0:
		(
			allCostumers.('Zahlfeld für Checkbox-Status' := 0);
			allCostumers.Projekte.('Zahlfeld für Checkbox-Status' := 0);
			allCostumers.Projekte.Aufgaben.('Toggle für Checkbox-Status' := 0)
		)
	case 1:
		(
			allCostumers.('Zahlfeld für Checkbox-Status' := 1);
			allCostumers.Projekte.('Zahlfeld für Checkbox-Status' := 1);
			allCostumers.Projekte.Aufgaben.('Toggle für Checkbox-Status' := 1)
		)
	case 2:
		void
	end
end

Trigger nach Änderung auf Kunden-Ebene:

do as transaction
	switch 'Zahlfeld für Checkbox-Status' do
	case 0:
		(
			Projekte.('Zahlfeld für Checkbox-Status' := 0);
			Projekte.Aufgaben.('Toggle für Checkbox-Status' := 0)
		)
	case 1:
		(
			Projekte.('Zahlfeld für Checkbox-Status' := 1);
			Projekte.Aufgaben.('Toggle für Checkbox-Status' := 1)
		)
	case 2:
		void
	end
end

Trigger nach Änderung auf Projekt-Ebene:

do as transaction
	switch 'Zahlfeld für Checkbox-Status' do
	case 0:
		Aufgaben.('Toggle für Checkbox-Status' := 0)
	case 1:
		Aufgaben.('Toggle für Checkbox-Status' := 1)
	case 2:
		void
	end
end
Code des Beispiels
json
let projectCostumers := (select Kunden);
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "SpaltenübergreifendeZellen" + Nr,
		embedded: false,
		class: "",
		style: "height: 500px;",
		showCheckboxes: true,
		header: {
			class: "",
			style: "",
			checkbox: {
				recordId: Nr,
				fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
				value: 'Zahlfeld für Checkbox-Status'
			},
			headerColumns: [{
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			groups: for costumer in projectCostumers do
				costumer.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					checkbox: {
						recordId: Nr,
						fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
						value: 'Zahlfeld für Checkbox-Status'
					},
					groupColumns: [{
							style: "grid-column: 2 / -1; text-align: center;",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: let splitValue := split(Kundenname, "");
							let countSplitValue := cnt(splitValue);
							lpad(" " + slice(Kundenname, 0, countSplitValue / 2), 50, "~") +
							rpad(slice(Kundenname, countSplitValue / 2, countSplitValue) + " ", 50, "~")
						}],
					groups: for project in costumer.Projekte do
						project.{
							collapseChildren: true,
							collapsible: true,
							class: "",
							style: "",
							checkbox: {
								recordId: Nr,
								fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
								value: 'Zahlfeld für Checkbox-Status'
							},
							groupColumns: [{
									style: "grid-column: 2 / -1; text-align: center;",
									clickToCollapse: true,
									hoverElement: {
										class: "",
										style: "",
										value: "Projekt öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: let splitValue := split(Projektname, "");
									let countSplitValue := cnt(splitValue);
									lpad(" " + slice(Projektname, 0, countSplitValue / 2), 50, "+") +
									rpad(slice(Projektname, countSplitValue / 2, countSplitValue) + " ", 50, "+")
								}],
							rows: for task in project.Aufgaben do
								task.{
									style: "",
									checkbox: {
										recordId: Nr,
										fieldId: fieldId(this, "Toggle für Checkbox-Status"),
										value: 'Toggle für Checkbox-Status'
									},
									columns: [{
											style: "",
											hoverElement: {
												class: "",
												style: "",
												value: "Aufgabe öffnen",
												actions: [{
														trigger: "click",
														scripts: [{
																type: "popup",
																recordId: Nr
															}]
													}]
											},
											value: Bezeichnung
										}, {
											style: "",
											value: Verantwortlicher.Name
										}, {
											style: "",
											value: text('Priorität')
										}, {
											style: "",
											value: Status.Bezeichnung
										}]
								}
							end
						}
					end
				}
			end
		},
		footer: {
			class: "",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
					" Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
let projectCostumers := (select Kunden);
GIP_table({
	uniqueId: "SpaltenübergreifendeZellen" + Nr,
	embedded: true,
	class: "",
	style: "height: 500px;",
	showCheckboxes: true,
	header: {
		class: "",
		style: "",
		checkbox: {
			recordId: Nr,
			fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
			value: 'Zahlfeld für Checkbox-Status'
		},
		headerColumns: [{
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		groups: for costumer in projectCostumers do
			costumer.{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				checkbox: {
					recordId: Nr,
					fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
					value: 'Zahlfeld für Checkbox-Status'
				},
				groupColumns: [{
						style: "grid-column: 2 / -1; text-align: center;",
						clickToCollapse: true,
						hoverElement: {
							class: "",
							style: "",
							value: "Kunde öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: let splitValue := split(Kundenname, "");
						let countSplitValue := cnt(splitValue);
						lpad(" " + slice(Kundenname, 0, countSplitValue / 2), 50, "~") +
						rpad(slice(Kundenname, countSplitValue / 2, countSplitValue) + " ", 50, "~")
					}],
				groups: for project in costumer.Projekte do
					project.{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						checkbox: {
							recordId: Nr,
							fieldId: fieldId(this, "Zahlfeld für Checkbox-Status"),
							value: 'Zahlfeld für Checkbox-Status'
						},
						groupColumns: [{
								style: "grid-column: 2 / -1; text-align: center;",
								clickToCollapse: true,
								hoverElement: {
									class: "",
									style: "",
									value: "Projekt öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: let splitValue := split(Projektname, "");
								let countSplitValue := cnt(splitValue);
								lpad(" " + slice(Projektname, 0, countSplitValue / 2), 50, "+") +
								rpad(slice(Projektname, countSplitValue / 2, countSplitValue) + " ", 50, "+")
							}],
						rows: for task in project.Aufgaben do
							task.{
								style: "",
								checkbox: {
									recordId: Nr,
									fieldId: fieldId(this, "Toggle für Checkbox-Status"),
									value: 'Toggle für Checkbox-Status'
								},
								columns: [{
										style: "",
										hoverElement: {
											class: "",
											style: "",
											value: "Aufgabe öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: Bezeichnung
									}, {
										style: "",
										value: Verantwortlicher.Name
									}, {
										style: "",
										value: text('Priorität')
									}, {
										style: "",
										value: Status.Bezeichnung
									}]
							}
						end
					}
				end
			}
		end
	},
	footer: {
		class: "",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
				" Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})

Table: Custom Styling

Beispiel Table Custom Styling

Beschreibung

Wir möchten statt dem Default-Styling der GIP-Table ein eigenes Styling festlegen, welches im globalen CSS definiert werden soll.

Wir legen zunächst eine Custom CSS-Class für die Table selbst fest, wo wir in diesem Beispiel eine feste Höhe vergeben. Danach folgen je nach Bedarf die Classen für Header, Body, Groups, Rows und Footer.

Interessant ist dabei, dass wir auch Attribute konditionell überschreiben können, z.B. die background-color der Rows. Zunächst legen wir eine allgemeine Hintergrundfarbe für Rows fest:

CSS
.customRow {
	background-color: #FCFCFC;
}

Da wir aber die Zeilen alternierend in einem helleren und einem dunkleren grau färben möchten definieren wir danach:

CSS
.customBody .GIPTableDummyRow[data-row-index]:nth-child(even) .GIPTableRow {
	background-color: #F5F5F5;
}

Hiermit geben wir an, dass wir in unserem customBody jede zweite Dummy-Row (geradzahlig) nehmen wollen. Für jede dieser ausgewählten Rows definieren wir nun die background-color in einem dunkleren grau.

Beachte bei der Definition immer die Reihenfolge von CSS-Klassen. Außerdem gilt auch für deine Custom Classes, dass Inline-Styles über den style-Key diese CSS-Deklarationen überschreiben können. Weitere Infos findest zu im Kapitel "Globaler CSS-Code".

Code des Beispiels
json
---
############### Füge den folgenden CSS-Code in die globale Funktion "GIP_customCSS()" ein und lade Ninox neu: ###############
---;
"
	.customTable {
		height: 500px;
	}

	.customHeader {
		text-transform: uppercase;
		background-color: #5F7EBA;
		color: white;
	}

	.customBody {
		border-color: white;
	}

	.customGroup {
		font-weight: 600;
	}

	.customRow {
		background-color: #FCFCFC;
	}

	.customBody .GIPTableDummyRow[data-row-index]:nth-child(even) .GIPTableRow {
		background-color: #F5F5F5;
	}

	.customBody .GIPTableColumn {
		padding-left: 12px;
		padding-right: 12px;
	}

	.customFooter {
		background-color: #5F7EBA;
		color: white;
	}
";
---
############### Füge den folgenden Code in dein Ninox-Formelfeld ein: ###############
---;
let projectCostumers := (select Kunden);
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "SpaltenübergreifendeZellen" + Nr,
		embedded: false,
		class: "customTable",
		style: "",
		header: {
			class: "customHeader",
			style: "",
			headerColumns: [{
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "customBody",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			groups: for costumer in projectCostumers do
				costumer.{
					collapseChildren: true,
					collapsible: true,
					class: "customGroup",
					style: "",
					groupColumns: [{
							style: "grid-column: 1 / -1;",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: Kundenname
						}],
					groups: for project in costumer.Projekte do
						project.{
							collapseChildren: true,
							collapsible: true,
							class: "customGroup",
							style: "",
							groupColumns: [{
									style: "grid-column: 1 / -1;",
									clickToCollapse: true,
									hoverElement: {
										class: "",
										style: "",
										value: "Projekt öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: Projektname
								}],
							rows: for task in project.Aufgaben do
								task.{
									class: "customRow",
									style: "",
									columns: [{
											style: "",
											hoverElement: {
												class: "",
												style: "",
												value: "Aufgabe öffnen",
												actions: [{
														trigger: "click",
														scripts: [{
																type: "popup",
																recordId: Nr
															}]
													}]
											},
											value: Bezeichnung
										}, {
											style: "",
											value: Verantwortlicher.Name
										}, {
											style: "",
											value: text('Priorität')
										}, {
											style: "",
											value: Status.Bezeichnung
										}]
								}
							end
						}
					end
				}
			end
		},
		footer: {
			class: "customFooter",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
					" Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
---
############### Füge den folgenden CSS-Code in die globale Funktion "GIP_customCSS()" ein und lade Ninox neu: ###############
---;
"
	.customTable {
		height: 500px;
	}

	.customHeader {
		text-transform: uppercase;
		background-color: #5F7EBA;
		color: white;
	}

	.customBody {
		border-color: white;
	}

	.customGroup {
		font-weight: 600;
	}

	.customRow {
		background-color: #FCFCFC;
	}

	.customBody .GIPTableDummyRow[data-row-index]:nth-child(even) .GIPTableRow {
		background-color: #F5F5F5;
	}

	.customBody .GIPTableColumn {
		padding-left: 12px;
		padding-right: 12px;
	}

	.customFooter {
		background-color: #5F7EBA;
		color: white;
	}
";
---
############### Füge den folgenden Code in dein Ninox-Formelfeld ein: ###############
---;
let projectCostumers := (select Kunden);
GIP_table({
	uniqueId: "SpaltenübergreifendeZellen" + Nr,
	embedded: true,
	class: "customTable",
	style: "",
	header: {
		class: "customHeader",
		style: "",
		headerColumns: [{
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "customBody",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		groups: for costumer in projectCostumers do
			costumer.{
				collapseChildren: true,
				collapsible: true,
				class: "customGroup",
				style: "",
				groupColumns: [{
						style: "grid-column: 1 / -1;",
						clickToCollapse: true,
						hoverElement: {
							class: "",
							style: "",
							value: "Kunde öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: Kundenname
					}],
				groups: for project in costumer.Projekte do
					project.{
						collapseChildren: true,
						collapsible: true,
						class: "customGroup",
						style: "",
						groupColumns: [{
								style: "grid-column: 1 / -1;",
								clickToCollapse: true,
								hoverElement: {
									class: "",
									style: "",
									value: "Projekt öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: Projektname
							}],
						rows: for task in project.Aufgaben do
							task.{
								class: "customRow",
								style: "",
								columns: [{
										style: "",
										hoverElement: {
											class: "",
											style: "",
											value: "Aufgabe öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: Bezeichnung
									}, {
										style: "",
										value: Verantwortlicher.Name
									}, {
										style: "",
										value: text('Priorität')
									}, {
										style: "",
										value: Status.Bezeichnung
									}]
							}
						end
					}
				end
			}
		end
	},
	footer: {
		class: "customFooter",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(projectCostumers.Projekte.Aufgaben.Verantwortlicher)) +
				" Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(projectCostumers.Projekte.Aufgaben[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})

Table: Gruppierte und Ungruppierte Rows

Beispiel Table Gruppierte und Ungruppierte Rows

Beschreibung

Wir haben eine Tabelle, in der wir Aufgaben auf erster Ebene nach Verantwortlichem und auf zweiter Ebene nach Projekt gruppieren. Allerdings gibt es auch Aufgaben ohne Verantwortlichen sowie Aufgaben mit Verantwortlichem, aber ohne Projekt.

Alle Aufgaben ohne Verantwortlichen stellen wir als normale Zeilen ohne Gruppierung dar. Dafür übergeben wir im body wie gewohnt den rows Key, wobei wir durch die Aufgaben ohne Verantwortlichen iterieren.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {...},
	rows: for taskNotAssigned in tasksNotAssigned do
		taskNotAssigned.{
			style: "",
			columns: [...]
		}
	end
}

Zusätzlich übergeben wir im body auch den groups Key auf gleicher Ebene, wie die gerade angelegten rows. Auf dieser ersten Gruppenebene übergeben wir die Mitarbeiter / Verantwortlichen.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {...},
	rows: for taskNotAssigned in tasksNotAssigned do
		taskNotAssigned.{
			style: "",
			columns: [...]
		}
	end,
	groups: for teamMember in teamMembers do
		teamMember.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [...]
		}
	end
}

Mit diesem Aufbau zeigen wir nun in der Table zuerst die nicht gruppierten Zeilen (Aufgaben ohne Verantwortlichen) an und danach die Gruppen (Verantwortliche).

Für den Inhalt der ersten Gruppierungsebene wiederholen wir das Vorgehen. Wir haben pro Verantwortlichem Aufgaben, die keinem Projekt zugeordnet sind, und Projekte, die die zweite Gruppierungsebene ergeben.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {
		style: "",
		value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
	},
	rows: for taskNotAssigned in tasksNotAssigned do
		taskNotAssigned.{
			style: "",
			columns: [...]
		}
	end,
	groups: for teamMember in teamMembers do
		teamMember.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [...],
			rows: for taskNoProject in teamMember.Aufgaben[not Projekte] do
				taskNoProject.{
					style: "",
					columns: [...]
				}
			end,
			groups: for project in unique(teamMember.Aufgaben.Projekte) do
				project.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [...],
				}
			end
		}
	end
}

Schließlich übergeben wir für unsere zweite Gruppierungsebene auch noch die Aufgaben mit Projektzuordnung und Verantwortlichem.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {
		style: "",
		value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
	},
	rows: for taskNotAssigned in tasksNotAssigned do
		taskNotAssigned.{
			style: "",
			columns: [...]
		}
	end,
	groups: for teamMember in teamMembers do
		teamMember.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [...],
			rows: for taskNoProject in teamMember.Aufgaben[not Projekte] do
				taskNoProject.{
					style: "",
					columns: [...]
				}
			end,
			groups: for project in unique(teamMember.Aufgaben.Projekte) do
				project.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [...],
					rows: for task in teamMember.Aufgaben[Projekte = project] do
						task.{
							style: "",
							columns: [...]
						}
					end
				}
			end
		}
	end
}
Code des Beispiels
json
let teamMembers := (select Mitarbeiter);
let allTasks := (select Aufgaben);
let tasksNotAssigned := allTasks[not Verantwortlicher];
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "GruppierteUndUngruppierteRows" + Nr,
		embedded: false,
		class: "",
		style: "height: 500px;",
		header: {
			class: "",
			style: "",
			headerColumns: [{
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			rows: for taskNotAssigned in tasksNotAssigned do
				taskNotAssigned.{
					style: "",
					columns: [{
							style: "",
							hoverElement: {
								class: "",
								style: "",
								value: "Aufgabe öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: Bezeichnung
						}, {
							style: "",
							value: Verantwortlicher.Name
						}, {
							style: "",
							value: text('Priorität')
						}, {
							style: "",
							value: Status.Bezeichnung
						}]
				}
			end,
			groups: for teamMember in teamMembers do
				teamMember.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{
							style: "",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: Name
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}],
					rows: for taskNoProject in teamMember.Aufgaben[not Projekte] do
						taskNoProject.{
							style: "",
							columns: [{
									style: "",
									hoverElement: {
										class: "",
										style: "",
										value: "Aufgabe öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: Bezeichnung
								}, {
									style: "",
									value: Verantwortlicher.Name
								}, {
									style: "",
									value: text('Priorität')
								}, {
									style: "",
									value: Status.Bezeichnung
								}]
						}
					end,
					groups: for project in unique(teamMember.Aufgaben.Projekte) do
						project.{
							collapseChildren: true,
							collapsible: true,
							class: "",
							style: "",
							groupColumns: [{
									style: "",
									clickToCollapse: true,
									hoverElement: {
										class: "",
										style: "",
										value: "Projekt öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: Projektname
								}, {
									clickToCollapse: true,
									style: "",
									value: ""
								}, {
									clickToCollapse: true,
									style: "",
									value: ""
								}, {
									clickToCollapse: true,
									style: "",
									value: ""
								}],
							rows: for task in teamMember.Aufgaben[Projekte = project] do
								task.{
									style: "",
									columns: [{
											style: "",
											hoverElement: {
												class: "",
												style: "",
												value: "Aufgabe öffnen",
												actions: [{
														trigger: "click",
														scripts: [{
																type: "popup",
																recordId: Nr
															}]
													}]
											},
											value: Bezeichnung
										}, {
											style: "",
											value: Verantwortlicher.Name
										}, {
											style: "",
											value: text('Priorität')
										}, {
											style: "",
											value: Status.Bezeichnung
										}]
								}
							end
						}
					end
				}
			end
		},
		footer: {
			class: "",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(allTasks) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(teamMembers)) + " Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(allTasks[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
let teamMembers := (select Mitarbeiter);
let allTasks := (select Aufgaben);
let tasksNotAssigned := allTasks[not Verantwortlicher];
GIP_table({
	uniqueId: "GruppierteUndUngruppierteRows" + Nr,
	embedded: true,
	class: "",
	style: "height: 500px;",
	header: {
		class: "",
		style: "",
		headerColumns: [{
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		rows: for taskNotAssigned in tasksNotAssigned do
			taskNotAssigned.{
				style: "",
				columns: [{
						style: "",
						hoverElement: {
							class: "",
							style: "",
							value: "Aufgabe öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: Bezeichnung
					}, {
						style: "",
						value: Verantwortlicher.Name
					}, {
						style: "",
						value: text('Priorität')
					}, {
						style: "",
						value: Status.Bezeichnung
					}]
			}
		end,
		groups: for teamMember in teamMembers do
			teamMember.{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				groupColumns: [{
						style: "",
						clickToCollapse: true,
						hoverElement: {
							class: "",
							style: "",
							value: "Kunde öffnen",
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: Nr
										}]
								}]
						},
						value: Name
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}, {
						clickToCollapse: true,
						style: "",
						value: ""
					}],
				rows: for taskNoProject in teamMember.Aufgaben[not Projekte] do
					taskNoProject.{
						style: "",
						columns: [{
								style: "",
								hoverElement: {
									class: "",
									style: "",
									value: "Aufgabe öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: Bezeichnung
							}, {
								style: "",
								value: Verantwortlicher.Name
							}, {
								style: "",
								value: text('Priorität')
							}, {
								style: "",
								value: Status.Bezeichnung
							}]
					}
				end,
				groups: for project in unique(teamMember.Aufgaben.Projekte) do
					project.{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						groupColumns: [{
								style: "",
								clickToCollapse: true,
								hoverElement: {
									class: "",
									style: "",
									value: "Projekt öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: Projektname
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}],
						rows: for task in teamMember.Aufgaben[Projekte = project] do
							task.{
								style: "",
								columns: [{
										style: "",
										hoverElement: {
											class: "",
											style: "",
											value: "Aufgabe öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: Bezeichnung
									}, {
										style: "",
										value: Verantwortlicher.Name
									}, {
										style: "",
										value: text('Priorität')
									}, {
										style: "",
										value: Status.Bezeichnung
									}]
							}
						end
					}
				end
			}
		end
	},
	footer: {
		class: "",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(allTasks) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(teamMembers)) + " Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(allTasks[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})

Table: Pseudogruppen

Beispiel Table Pseudogruppen

Beschreibung

Wir haben eine Tabelle, in der wir Aufgaben auf erster Ebene nach Verantwortlichem und auf zweiter Ebene nach Projekt gruppieren. Allerdings gibt es auch Aufgaben ohne Verantwortlichen sowie Aufgaben mit Verantwortlichem, aber ohne Projekt.

Alle Aufgaben ohne Verantwortlichen stellen wir in einer manuell angelegten Pseudogruppe dar. Dafür übergeben wir im body wie gewohnt den groups Key, bauen unser Array of Objects aber aus zwei Teilen auf. Zuerst bauen wir unsere Pseudogruppe auf, indem wir ein Array mit nur einem Object auf eine Variable schreiben. In dieser Pseudogruppe durchlaufen wir alle Aufgaben ohne Verantwortlichen und erstellen für diese die rows.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {...},
	groups: let pseudoTeamMemberGroup := [{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				groupColumns: [...],
				rows: for taskNotAssigned in tasksNotAssigned do
					taskNotAssigned.{
						style: "",
						columns: [...]
					}
				end
			}];
}

Danach bauen wir in einer zweiten Variable die tatsächlich existierenden Gruppen auf, indem wir durch die Mitarbeiter iterieren. Nachdem wir nun sowohl die Pseudogruppe als ein Array mit einem Object, als auch die Verantwortlichen-Gruppen als Array mit vielen Objects angelegt haben, fassen wir diese mit dem Ninox-Befehl "array()" zusammen. Dadurch erhalten wir ein einziges Array ob Objects, für unseren groups-Key.

json
body: {
	class: "",
	style: "",
	rowHeight: "40px",
	placeholder: {...},
	groups: let pseudoTeamMemberGroup := [{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				groupColumns: [...],
				rows: for taskNotAssigned in tasksNotAssigned do
					taskNotAssigned.{
						style: "",
						columns: [...]
					}
				end
			}];
	let teamMemberGroups := for teamMember in teamMembers do
			teamMember.{
				collapseChildren: true,
				collapsible: true,
				class: "",
				style: "",
				groupColumns: [...],
				groups: [...]
			}
		end;
	array(pseudoTeamMemberGroup, teamMemberGroups)
}

Dieses Vorgehen, nicht zugeordnete Einträge in einer Pseudogruppe anzulegen, können wir nun gleich noch einmal verwenden. In den Verantwortlichen-Gruppen möchten wir nun nämlich auch noch nach Projekten gruppieren. Doch es kann auch Aufgaben geben, die zwar dem Verantwortlichen zugordnet sind, aber keinem Projekt haben. Daher erstellen wir innerhalb der Verantwortlichen wieder zur Definition der groups zuerst eine Variable für eine Pseudogruppe, bevor wir die tatsächlichen Projekt-Gruppen erstellen.

json
let teamMemberGroups := for teamMember in teamMembers do
		teamMember.{
			collapseChildren: true,
			collapsible: true,
			class: "",
			style: "",
			groupColumns: [...],
			groups: let pseudoProjectGroup := [{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						groupColumns: [...],
						rows: for taskNoProject in teamMember.Aufgaben[not Projekte] do
							taskNoProject.{
								style: "",
								columns: [...]
							}
						end
					}];
			let projectGroups := for project in unique(teamMember.Aufgaben.Projekte) do
					project.{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						groupColumns: [...],
						rows: for task in teamMember.Aufgaben[Projekte = project] do
							task.{
								style: "",
								columns: [...]
							}
						end
					}
				end;
			array(pseudoProjectGroup, projectGroups)
		}
	end;

Dieses Beispiel kann erweitert werden, um beliebig viele Pseudogruppen oder andere Arten von manuellen Gruppierungen zu erzeugen.

Code des Beispiels
json
let teamMembers := (select Mitarbeiter);
let allTasks := (select Aufgaben);
let tasksNotAssigned := allTasks[not Verantwortlicher];
html(raw(GIP_master({})) +
raw(GIP_table({
		uniqueId: "PseudoGruppen" + Nr,
		embedded: false,
		class: "",
		style: "height: 500px;",
		header: {
			class: "",
			style: "",
			headerColumns: [{
					style: "",
					columnWidth: "2fr",
					value: "Aufgabe"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Verantwortlicher"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Priorität"
				}, {
					style: "",
					columnWidth: "1fr",
					value: "Status"
				}]
		},
		body: {
			class: "",
			style: "",
			rowHeight: "40px",
			placeholder: {
				style: "",
				value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
			},
			groups: let pseudoTeamMemberGroup := [{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						groupColumns: [{
								style: "",
								clickToCollapse: true,
								hoverElement: {
									class: "",
									style: "",
									value: "Kunde öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: "Aufgaben ohne Verantwortlichen"
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}],
						rows: for taskNotAssigned in tasksNotAssigned do
							taskNotAssigned.{
								style: "",
								columns: [{
										style: "",
										hoverElement: {
											class: "",
											style: "",
											value: "Aufgabe öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: Bezeichnung
									}, {
										style: "",
										value: Verantwortlicher.Name
									}, {
										style: "",
										value: text('Priorität')
									}, {
										style: "",
										value: Status.Bezeichnung
									}]
							}
						end
					}];
			let teamMemberGroups := for teamMember in teamMembers do
					teamMember.{
						collapseChildren: true,
						collapsible: true,
						class: "",
						style: "",
						groupColumns: [{
								style: "",
								clickToCollapse: true,
								hoverElement: {
									class: "",
									style: "",
									value: "Kunde öffnen",
									actions: [{
											trigger: "click",
											scripts: [{
													type: "popup",
													recordId: Nr
												}]
										}]
								},
								value: Name
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}, {
								clickToCollapse: true,
								style: "",
								value: ""
							}],
						groups: let pseudoProjectGroup := [{
									collapseChildren: true,
									collapsible: true,
									class: "",
									style: "",
									groupColumns: [{
											style: "",
											clickToCollapse: true,
											hoverElement: {
												class: "",
												style: "",
												value: "Projekt öffnen",
												actions: [{
														trigger: "click",
														scripts: [{
																type: "popup",
																recordId: Nr
															}]
													}]
											},
											value: "Aufgaben ohne Projekt-Zuweisung"
										}, {
											clickToCollapse: true,
											style: "",
											value: ""
										}, {
											clickToCollapse: true,
											style: "",
											value: ""
										}, {
											clickToCollapse: true,
											style: "",
											value: ""
										}],
									rows: for taskNoProject in teamMember.Aufgaben[not Projekte] do
										taskNoProject.{
											style: "",
											columns: [{
													style: "",
													hoverElement: {
														class: "",
														style: "",
														value: "Aufgabe öffnen",
														actions: [{
																trigger: "click",
																scripts: [{
																		type: "popup",
																		recordId: Nr
																	}]
															}]
													},
													value: Bezeichnung
												}, {
													style: "",
													value: Verantwortlicher.Name
												}, {
													style: "",
													value: text('Priorität')
												}, {
													style: "",
													value: Status.Bezeichnung
												}]
										}
									end
								}];
						let projectGroups := for project in unique(teamMember.Aufgaben.Projekte) do
								project.{
									collapseChildren: true,
									collapsible: true,
									class: "",
									style: "",
									groupColumns: [{
											style: "",
											clickToCollapse: true,
											hoverElement: {
												class: "",
												style: "",
												value: "Projekt öffnen",
												actions: [{
														trigger: "click",
														scripts: [{
																type: "popup",
																recordId: Nr
															}]
													}]
											},
											value: Projektname
										}, {
											clickToCollapse: true,
											style: "",
											value: ""
										}, {
											clickToCollapse: true,
											style: "",
											value: ""
										}, {
											clickToCollapse: true,
											style: "",
											value: ""
										}],
									rows: for task in teamMember.Aufgaben[Projekte = project] do
										task.{
											style: "",
											columns: [{
													style: "",
													hoverElement: {
														class: "",
														style: "",
														value: "Aufgabe öffnen",
														actions: [{
																trigger: "click",
																scripts: [{
																		type: "popup",
																		recordId: Nr
																	}]
															}]
													},
													value: Bezeichnung
												}, {
													style: "",
													value: Verantwortlicher.Name
												}, {
													style: "",
													value: text('Priorität')
												}, {
													style: "",
													value: Status.Bezeichnung
												}]
										}
									end
								}
							end;
						array(pseudoProjectGroup, projectGroups)
					}
				end;
			array(pseudoTeamMemberGroup, teamMemberGroups)
		},
		footer: {
			class: "",
			style: "",
			footerColumns: [{
					style: "",
					value: cnt(allTasks) + " Aufgaben"
				}, {
					style: "",
					value: cnt(unique(teamMembers)) + " Verantwortliche"
				}, {
					style: "",
					value: ""
				}, {
					style: "",
					value: cnt(allTasks[number(Status) = 5]) + " Abgeschlossen"
				}]
		}
	})))
json
let teamMembers := (select Mitarbeiter);
let allTasks := (select Aufgaben);
let tasksNotAssigned := allTasks[not Verantwortlicher];
GIP_table({
	uniqueId: "PseudoGruppen" + Nr,
	embedded: true,
	class: "",
	style: "height: 500px;",
	header: {
		class: "",
		style: "",
		headerColumns: [{
				style: "",
				columnWidth: "2fr",
				value: "Aufgabe"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Verantwortlicher"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Priorität"
			}, {
				style: "",
				columnWidth: "1fr",
				value: "Status"
			}]
	},
	body: {
		class: "",
		style: "",
		rowHeight: "40px",
		placeholder: {
			style: "",
			value: "Es wurden keine Daten gefunden, die auf die aktuellen Filter zutreffen."
		},
		groups: let pseudoTeamMemberGroup := [{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{
							style: "",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: "Aufgaben ohne Verantwortlichen"
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}],
					rows: for taskNotAssigned in tasksNotAssigned do
						taskNotAssigned.{
							style: "",
							columns: [{
									style: "",
									hoverElement: {
										class: "",
										style: "",
										value: "Aufgabe öffnen",
										actions: [{
												trigger: "click",
												scripts: [{
														type: "popup",
														recordId: Nr
													}]
											}]
									},
									value: Bezeichnung
								}, {
									style: "",
									value: Verantwortlicher.Name
								}, {
									style: "",
									value: text('Priorität')
								}, {
									style: "",
									value: Status.Bezeichnung
								}]
						}
					end
				}];
		let teamMemberGroups := for teamMember in teamMembers do
				teamMember.{
					collapseChildren: true,
					collapsible: true,
					class: "",
					style: "",
					groupColumns: [{
							style: "",
							clickToCollapse: true,
							hoverElement: {
								class: "",
								style: "",
								value: "Kunde öffnen",
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: Nr
											}]
									}]
							},
							value: Name
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}, {
							clickToCollapse: true,
							style: "",
							value: ""
						}],
					groups: let pseudoProjectGroup := [{
								collapseChildren: true,
								collapsible: true,
								class: "",
								style: "",
								groupColumns: [{
										style: "",
										clickToCollapse: true,
										hoverElement: {
											class: "",
											style: "",
											value: "Projekt öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: "Aufgaben ohne Projekt-Zuweisung"
									}, {
										clickToCollapse: true,
										style: "",
										value: ""
									}, {
										clickToCollapse: true,
										style: "",
										value: ""
									}, {
										clickToCollapse: true,
										style: "",
										value: ""
									}],
								rows: for taskNoProject in teamMember.Aufgaben[not Projekte] do
									taskNoProject.{
										style: "",
										columns: [{
												style: "",
												hoverElement: {
													class: "",
													style: "",
													value: "Aufgabe öffnen",
													actions: [{
															trigger: "click",
															scripts: [{
																	type: "popup",
																	recordId: Nr
																}]
														}]
												},
												value: Bezeichnung
											}, {
												style: "",
												value: Verantwortlicher.Name
											}, {
												style: "",
												value: text('Priorität')
											}, {
												style: "",
												value: Status.Bezeichnung
											}]
									}
								end
							}];
					let projectGroups := for project in unique(teamMember.Aufgaben.Projekte) do
							project.{
								collapseChildren: true,
								collapsible: true,
								class: "",
								style: "",
								groupColumns: [{
										style: "",
										clickToCollapse: true,
										hoverElement: {
											class: "",
											style: "",
											value: "Projekt öffnen",
											actions: [{
													trigger: "click",
													scripts: [{
															type: "popup",
															recordId: Nr
														}]
												}]
										},
										value: Projektname
									}, {
										clickToCollapse: true,
										style: "",
										value: ""
									}, {
										clickToCollapse: true,
										style: "",
										value: ""
									}, {
										clickToCollapse: true,
										style: "",
										value: ""
									}],
								rows: for task in teamMember.Aufgaben[Projekte = project] do
									task.{
										style: "",
										columns: [{
												style: "",
												hoverElement: {
													class: "",
													style: "",
													value: "Aufgabe öffnen",
													actions: [{
															trigger: "click",
															scripts: [{
																	type: "popup",
																	recordId: Nr
																}]
														}]
												},
												value: Bezeichnung
											}, {
												style: "",
												value: Verantwortlicher.Name
											}, {
												style: "",
												value: text('Priorität')
											}, {
												style: "",
												value: Status.Bezeichnung
											}]
									}
								end
							}
						end;
					array(pseudoProjectGroup, projectGroups)
				}
			end;
		array(pseudoTeamMemberGroup, teamMemberGroups)
	},
	footer: {
		class: "",
		style: "",
		footerColumns: [{
				style: "",
				value: cnt(allTasks) + " Aufgaben"
			}, {
				style: "",
				value: cnt(unique(teamMembers)) + " Verantwortliche"
			}, {
				style: "",
				value: ""
			}, {
				style: "",
				value: cnt(allTasks[number(Status) = 5]) + " Abgeschlossen"
			}]
	}
})