Skip to content

Das Kanban-Modul verbindet strukturierte Darstellung mit interaktiver Steuerung. So organisierst du Projekte, Aufgaben und Arbeitsflüsse klar und flexibel. Wie bei all unseren Modulen stehen hohe Performance und maximale Flexibilität im Mittelpunkt. Zu den Highlights gehören:

  • Einfache Gruppierung von Cards in Spalten und Gruppen
  • Verschieben von Cards per Drag & Drop, auch auf mobilen Geräten
  • Umfrangreiche Optionen für Actions, Updates on Drop und Interaktionsbeschränkungen
  • Karten innerhalb von Spalten und Gruppen per Drag & Drop sortieren
  • Add-Button zum Erstellen neuer Cards

Funktionen und Aussehen

Im Kanban-Modul dreht sich alles um das Anordnen und Gruppieren deiner Cards. Du ordnest sie den Kanban-Spalten zu und kannst sie innerhalb der Spalten zusätzlich in beliebig vielen Ebenen gruppieren. Spalten und Gruppen bieten praktische Funktionen: Titel anpassen, Styling über CSS-Classes und Inline-Styles steuern sowie ein- und ausklappen.

Der Inhalt der Cards ist frei gestaltbar und kann Text, andere GIP- oder arcRider-Module sowie HTML-Elemente enthalten. Deiner Kreativität sind keine Grenzen gesetzt. Auch die Cards kannst du individuell stylen, indem du entweder den Style-Key für Inline-Styles nutzt oder den Class-Key, um deine globalen Classes zu referenzieren.

Nutzer-Interaktion

Karten sind die zentralen Elemente deines Kanban-Boards. Du verschiebst sie ganz einfach per Drag & Drop zwischen Spalten und kannst sie auch innerhalb einer Spalte neu sortieren. In beiden Fällen stehen die spezielle Keys zur Verfügung, um diese Updates in Ninox-Felder zu übertragen. Zusätzlich kannst du Regeln festlegen, welche Cards wohin gezogen werden dürfen.

Wenn du das Verschieben von Karten für das komplette Board deaktivieren möchtest, kannst du unseren integrierten Lock-Button. Das ist besonders auf mobilen Geräten hilfreich, auf denen Touch-Events schnell zu versehentlichem Verschieben führen. Den Sperrstatus kannst du einfach umschalten und ebenfalls in einem Ninox-Feld speichern.

Für jedes Element deines Boards – Karten, Spalten, Gruppen oder das gesamte Board – kannst du individuelle Aktionen definieren. Jede Aktion wird durch einen Trigger (z.B. Klick oder Mausbewegung) ausgelöst und führt eine Abfolge von Skripten aus. Eine Übersicht möglicher Events findest du in dieser Liste.

Modulcode

json
GIP_kanban({
   uniqueId: "myUniqueName" + Nr
   embedded: true
   kanbanColumns: [{
   	titleContent: {
   		value: *any*
   	},
   	cards: [{
   		recordId: raw(Nr)
   		value: *any*
   	}],
   }],
})
json
GIP_kanban({
   uniqueId: "myUniqueName" + Nr
   embedded: true
   style: ""
   kanbanColumns: [{
   	style: ""
   	titleContent: {
   		style: ""
   		value: *any*
   	},
   	cards: [{
   		recordId: raw(Nr)
   		style: ""
   		value: *any*
   		draggable: true
   	}],
   	cardGroups: [{
   		style: ""
   		titleContent: {
   			style: ""
   			value: *any*
   		},
   		cards: [{
   			recordId: raw(Nr)
   			style: ""
   			value: *any*
   			draggable: true
   		}],
   		droppable: true
   	}],
   	droppable: true
   }],
})
json
GIP_kanban({
   uniqueId: "myUniqueName" + Nr
   embedded: true
   class: ""
   style: ""
   kanbanColumns: [{
   	class: ""
   	style: ""
   	titleContent: {
   		class: ""
   		style: ""
   		value: *any*
   		addButton: {
   			tableId: ""
   			changeFieldValues: [{
   				fieldId: ""
   				value: *any*
   			}],
   			displayAfterCreate: "popup"
   			setNewRecordId: [{
   				recordId: raw(Nr)
   				fieldId: ""
   			}],
   		},
   		actions: [{
   			trigger: "click"
   			scripts: [{
   				type: ""
   			}],
   		}],
   	},
   	cards: [{
   		recordId: raw(Nr)
   		class: ""
   		style: ""
   		sorting: {
   			fieldId: ""
   			sortNr: 1
   		},
   		value: *any*
   		actions: [{
   			trigger: "click"
   			scripts: [{
   				type: ""
   			}],
   		}],
   		draggable: true
   	}],
   	cardGroups: [{
   		class: ""
   		style: ""
   		titleContent: {
   			class: ""
   			style: ""
   			value: *any*
   			addButton: {
   				tableId: ""
   				changeFieldValues: [{
   					fieldId: ""
   					value: *any*
   				}],
   				displayAfterCreate: "popup"
   				setNewRecordId: [{
   					recordId: raw(Nr)
   					fieldId: ""
   				}],
   			},
   			actions: [{
   				trigger: "click"
   				scripts: [{
   					type: ""
   				}],
   			}],
   		},
   		cards: [{
   			recordId: raw(Nr)
   			class: ""
   			style: ""
   			sorting: {
   				fieldId: ""
   				sortNr: 1
   			},
   			value: *any*
   			actions: [{
   				trigger: "click"
   				scripts: [{
   					type: ""
   				}],
   			}],
   			draggable: true
   		}],
   		cardGroups: [{
   		}],
   		droppable: true
   		dropUpdates: [{
   			fieldId: ""
   			value: *any*
   		}],
   		sortingEnabled: true
   	}],
   	droppable: true
   	dropUpdates: [{
   		fieldId: ""
   		value: *any*
   	}],
   	sortingEnabled: true
   }],
   lockButton: {
   	recordId: raw(Nr)
   	fieldId: ""
   	value: *any*
   },
})
json
GIP_kanban({
   uniqueId: "",
   embedded: {
   	height: "",
   	styleHtml: "",
   	styleStri: "",
   	presets: "",
   },
   class: "",
   style: "",
   kanbanColumns: [{
   	collapseChildren: {
   		recordId: "",
   		fieldId: "",
   		value: *any*,
   	},
   	class: "",
   	collapsible: true,
   	style: "",
   	titleContent: {
   		class: "",
   		style: "",
   		value: *any*,
   		addButton: {
   			tableId: "",
   			tab: "",
   			changeFieldValues: [{
   				fieldId: "",
   				value: *any*,
   			}],
   			displayAfterCreate: "",
   			setNewRecordId: [{
   				recordId: "",
   				fieldId: "",
   			}],
   		},
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	},
   	cards: [{
   		recordId: "",
   		class: "",
   		style: "",
   		sorting: {
   			fieldId: "",
   			sortNr: 0,
   		},
   		value: *any*,
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   		draggable: {
   		},
   	}],
   	cardGroups: [{
   		collapseChildren: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		class: "",
   		collapsible: true,
   		style: "",
   		titleContent: {
   			class: "",
   			style: "",
   			value: *any*,
   			addButton: {
   				tableId: "",
   				tab: "",
   				changeFieldValues: [{
   					fieldId: "",
   					value: *any*,
   				}],
   				displayAfterCreate: "",
   				setNewRecordId: [{
   					recordId: "",
   					fieldId: "",
   				}],
   			},
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		},
   		cards: [{
   			recordId: "",
   			class: "",
   			style: "",
   			sorting: {
   				fieldId: "",
   				sortNr: 0,
   			},
   			value: *any*,
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   			draggable: {
   			},
   		}],
   		cardGroups: [{
   		}],
   		droppable: {
   		},
   		dropUpdates: [{
   			fieldId: "",
   			value: *any*,
   		}],
   		sortingEnabled: true,
   	}],
   	droppable: {
   	},
   	dropUpdates: [{
   		fieldId: "",
   		value: *any*,
   	}],
   	sortingEnabled: true,
   }],
   lockButton: {
   	recordId: "",
   	fieldId: "",
   	value: *any*,
   },
})
json
html( raw(GIP_master({})) + 
raw(GIP_kanban({
   uniqueId: "",
   embedded: {
   	height: "",
   	styleHtml: "",
   	styleStri: "",
   	presets: "",
   },
   class: "",
   style: "",
   kanbanColumns: [{
   	collapseChildren: {
   		recordId: "",
   		fieldId: "",
   		value: *any*,
   	},
   	class: "",
   	collapsible: true,
   	style: "",
   	titleContent: {
   		class: "",
   		style: "",
   		value: *any*,
   		addButton: {
   			tableId: "",
   			tab: "",
   			changeFieldValues: [{
   				fieldId: "",
   				value: *any*,
   			}],
   			displayAfterCreate: "",
   			setNewRecordId: [{
   				recordId: "",
   				fieldId: "",
   			}],
   		},
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   	},
   	cards: [{
   		recordId: "",
   		class: "",
   		style: "",
   		sorting: {
   			fieldId: "",
   			sortNr: 0,
   		},
   		value: *any*,
   		actions: [{
   			trigger: "",
   			scripts: [{
   				type: "",
   			}],
   		}],
   		draggable: {
   		},
   	}],
   	cardGroups: [{
   		collapseChildren: {
   			recordId: "",
   			fieldId: "",
   			value: *any*,
   		},
   		class: "",
   		collapsible: true,
   		style: "",
   		titleContent: {
   			class: "",
   			style: "",
   			value: *any*,
   			addButton: {
   				tableId: "",
   				tab: "",
   				changeFieldValues: [{
   					fieldId: "",
   					value: *any*,
   				}],
   				displayAfterCreate: "",
   				setNewRecordId: [{
   					recordId: "",
   					fieldId: "",
   				}],
   			},
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   		},
   		cards: [{
   			recordId: "",
   			class: "",
   			style: "",
   			sorting: {
   				fieldId: "",
   				sortNr: 0,
   			},
   			value: *any*,
   			actions: [{
   				trigger: "",
   				scripts: [{
   					type: "",
   				}],
   			}],
   			draggable: {
   			},
   		}],
   		cardGroups: [{
   		}],
   		droppable: {
   		},
   		dropUpdates: [{
   			fieldId: "",
   			value: *any*,
   		}],
   		sortingEnabled: true,
   	}],
   	droppable: {
   	},
   	dropUpdates: [{
   		fieldId: "",
   		value: *any*,
   	}],
   	sortingEnabled: true,
   }],
   lockButton: {
   	recordId: "",
   	fieldId: "",
   	value: *any*,
   },
})
))

Key-Table

Beispiele

Kanban: Basic ohne Gruppen

Beschreibung

Wir möchten ein Kanban zur Verwaltung von Aufgaben bauen. Dabei sollen die Spalten des Kanbans jeweils einen Aufgaben-Status darstellen.

Im Key kanbanColumns erstellen wir zunächst pro Projektstatus ein Object, indem wir durch die Stati iterieren. Für jede dieser Status-Spalten suchen wir uns dann die dazugehörigen Aufgaben, welche wir als cards übergeben.

json
{
	uniqueId: "ProjektAufgabenVerwaltungBasic" + Nr,
	embedded: false,
	class: "",
	style: "height: 600px;",
	kanbanColumns: for column in allStati do
		let columnTasks := tasks[number(Status) = number(column)];
		column.{
			class: "",
			style: "",
			cards: for card in columnTasks do
				card.{
					class: "",
					style: "border-left: 6px solid " + Verantwortlicher.Farbe + ";",
					recordId: raw(Nr),
					draggable: true,
					value: Bezeichnung,
					actions: [{
							trigger: "click",
							scripts: [{
									type: "popup",
									recordId: raw(Nr)
								}]
						}]
				}
			end
		}
	end
}

Nachdem die Aufgaben in Spalten eingeordnet sind, legen wir noch fest, welchen Titel unsere Spalten haben sollen und ob die Spalten eingeklappt werden können. Wir können mit collapseChildren außerdem einen initialen Einklappstatus festlegen.

json
{
	uniqueId: "ProjektAufgabenVerwaltungBasic" + Nr,
	embedded: false,
	class: "",
	style: "height: 600px;",
	kanbanColumns: for column in allStati do
		let columnTasks := tasks[number(Status) = number(column)];
		column.{
			class: "",
			style: "",
			collapsible: true,
			collapseChildren: {
				recordId: raw(Nr),
				fieldId: fieldId(this, "Collapse in Kanban"),
				value: 'Collapse in Kanban'
			},
			titleContent: {
				class: "",
				style: "background-color: " + Farbe,
				value: Bezeichnung
			},
			cards: for card in columnTasks do
				card.{
					class: "",
					style: "border-left: 6px solid " + Verantwortlicher.Farbe + ";",
					recordId: raw(Nr),
					draggable: true,
					value: Bezeichnung,
					actions: [{
							trigger: "click",
							scripts: [{
									type: "popup",
									recordId: raw(Nr)
								}]
						}]
				}
			end
		}
	end
}

Zuletzt müssen wir noch festlegen, welche Ninox-Felder beim Verschieben einer Aufgabe in eine andere Spalte passieren soll. In unserem Fall möchten wir das Statusfeld in der Aufgabe aktualisieren. Dafür übergeben in dropUpdates in einem Object die Field-ID des Status-Feldes in den Aufgaben und der neue Wert, der gesetzt werden soll, in diesem Fall die Number des Status der Spalte.

json
{
	uniqueId: "ProjektAufgabenVerwaltungBasic" + Nr,
	embedded: false,
	class: "",
	style: "height: 600px;",
	kanbanColumns: for column in allStati do
		let columnTasks := tasks[number(Status) = number(column)];
		column.{
			class: "",
			style: "",
			dropUpdates: [{
					fieldId: fieldId(first(tasks), "Status"),
					value: number(this)
				}],
			collapsible: true,
			collapseChildren: {
				recordId: raw(Nr),
				fieldId: fieldId(this, "Collapse in Kanban"),
				value: 'Collapse in Kanban'
			},
			titleContent: {
				class: "",
				style: "background-color: " + Farbe,
				value: Bezeichnung
			},
			cards: for card in columnTasks do
				card.{
					class: "",
					style: "border-left: 6px solid " + Verantwortlicher.Farbe + ";",
					recordId: raw(Nr),
					draggable: true,
					value: Bezeichnung,
					actions: [{
							trigger: "click",
							scripts: [{
									type: "popup",
									recordId: raw(Nr)
								}]
						}]
				}
			end
		}
	end
}

Es könnten auch noch weitere dropUpdates definiert werden, z.B. welcher Nutzer die Aufgabe zuletzt verschoben hat.

json
dropUpdates: [{
		fieldId: fieldId(first(tasks), "Status"),
		value: number(this)
	},{
		fieldId: fieldId(first(tasks), "Nutzer"),
		value: user()
	}],
Code des Beispiels
json
let allStati := ((select Status) order by Reihenfolge);
let tasks := (select Aufgaben);
html(raw(GIP_master({})) +
raw(GIP_kanban({
		uniqueId: "ProjektAufgabenVerwaltungBasic" + Nr,
		embedded: false,
		class: "",
		style: "height: 600px;",
		kanbanColumns: for column in allStati do
			let columnTasks := tasks[number(Status) = number(column)];
			column.{
				class: "",
				style: "",
				dropUpdates: [{
						fieldId: fieldId(first(tasks), "Status"),
						value: number(this)
					}],
				collapsible: true,
				collapseChildren: {
					recordId: raw(Nr),
					fieldId: fieldId(this, "Collapse in Kanban"),
					value: 'Collapse in Kanban'
				},
				titleContent: {
					class: "",
					style: "background-color: " + Farbe,
					value: Bezeichnung
				},
				cards: for card in columnTasks do
					card.{
						class: "",
						style: "border-left: 6px solid " + Verantwortlicher.Farbe + ";",
						recordId: raw(Nr),
						draggable: true,
						value: Bezeichnung,
						actions: [{
								trigger: "click",
								scripts: [{
										type: "popup",
										recordId: raw(Nr)
									}]
							}]
					}
				end
			}
		end
	})))
json
let allStati := ((select Status) order by Reihenfolge);
let tasks := (select Aufgaben);
GIP_kanban({
	uniqueId: "ProjektAufgabenVerwaltungBasic" + Nr,
	embedded: true,
	class: "",
	style: "height: 600px;",
	kanbanColumns: for column in allStati do
		let columnTasks := tasks[number(Status) = number(column)];
		column.{
			class: "",
			style: "",
			dropUpdates: [{
					fieldId: fieldId(first(tasks), "Status"),
					value: number(this)
				}],
			collapsible: true,
			collapseChildren: {
				recordId: raw(Nr),
				fieldId: fieldId(this, "Collapse in Kanban"),
				value: 'Collapse in Kanban'
			},
			titleContent: {
				class: "",
				style: "background-color: " + Farbe,
				value: Bezeichnung
			},
			cards: for card in columnTasks do
				card.{
					class: "",
					style: "border-left: 6px solid " + Verantwortlicher.Farbe + ";",
					recordId: raw(Nr),
					draggable: true,
					value: Bezeichnung,
					actions: [{
							trigger: "click",
							scripts: [{
									type: "popup",
									recordId: raw(Nr)
								}]
						}]
				}
			end
		}
	end
})

Kanban: Mit Gruppen

Beschreibung

Wir möchten ein Kanban anlegen, in dem unsere Spalten verschiedene Aufgaben-Stati sind. Zusätzlich möchten wir innerhalb dieser Spalten die Aufgaben nach Verantwortlichen gruppieren.

Zunächst legen wir wie gewohnt unsere kanbanColumns an. In diesen definieren wir das dropUpdate für die Spalte: Es soll das Feld "Status" mit dem Status der Spalte aktualisiert werden.

json
{
	uniqueId: "ProjektAufgabenVerwaltungGroups" + Nr,
	embedded: true,
	class: "",
	style: "height: 600px;",
	kanbanColumns: for column in allStati do
		column.{
			class: "",
			style: "",
			dropUpdates: [{
					fieldId: fieldId(first(tasks), "Status"),
					value: number(this)
				}],
			collapsible: true,
			collapseChildren: {
				recordId: raw(Nr),
				fieldId: fieldId(this, "Collapse in Kanban"),
				value: 'Collapse in Kanban'
			},
			titleContent: {
				class: "",
				style: "background-color: " + Farbe,
				value: Bezeichnung
			}
		}
	end
}

Nun legen wir noch Gruppen für die Verantwortlichen in jeder Spalte an. Dafür definieren wir cardGroups innerhalb der kanbanColumns. Die Struktur der Gruppen und der Spalten ist identisch, wir ersetzen nur die Werte der Keys. Bei dropUpdates der cardGroups beziehen wir uns in auf der Feld "Verantwortlicher" in unseren Aufgaben und übergeben die ID des Verantwortlichen. Die cards, also die Aufgaben, übergeben wir in den cardGroups.

json
{
	uniqueId: "ProjektAufgabenVerwaltungGroups" + Nr,
	embedded: true,
	class: "",
	style: "height: 600px;",
	kanbanColumns: for column in allStati do
		column.{
			class: "",
			style: "",
			dropUpdates: [{
					fieldId: fieldId(first(tasks), "Status"),
					value: number(this)
				}],
			collapsible: true,
			collapseChildren: {
				recordId: raw(Nr),
				fieldId: fieldId(this, "Collapse in Kanban"),
				value: 'Collapse in Kanban'
			},
			titleContent: {
				class: "",
				style: "background-color: " + Farbe,
				value: Bezeichnung
			},
			cardGroups: for teamMember in teamMembers do
				let groupTasks := tasks[number(Status) = number(column) and number(Verantwortlicher) = number(teamMember)];
				teamMember.{
					class: "",
					style: "",
					dropUpdates: [{
							fieldId: fieldId(first(tasks), "Verantwortlicher"),
							value: number(this)
						}],
					collapsible: true,
					collapseChildren: false,
					titleContent: {
						class: "",
						style: "color: " + Farbe + ";",
						value: Name
					},
					cards: for card in groupTasks do
						card.{...}
					end
				}
			end
		}
	end
}

Die definierten dropUpdates der Spalten und Gruppen funktionieren sowohl separat (eine Aufgabe in eine neue Spalte ODER eine neue Gruppe ziehen), als auch zusammen (eine Aufgabe in eine neue Spalte UND eine neue Gruppe ziehen). Das bedeutet, dass man der Aufgabe gleichzeitig einen neuen Status und einen neuen Verantwortlichen zuweisen kann.

Code des Beispiels
json
let allStati := ((select Status) order by Reihenfolge);
let teamMembers := (select Mitarbeiter);
let tasks := (select Aufgaben);
html(raw(GIP_master({})) +
raw(GIP_kanban({
		uniqueId: "ProjektAufgabenVerwaltungGroups" + Nr,
		embedded: false,
		class: "",
		style: "height: 600px;",
		kanbanColumns: for column in allStati do
			column.{
				class: "",
				style: "",
				dropUpdates: [{
						fieldId: fieldId(first(tasks), "Status"),
						value: number(this)
					}],
				collapsible: true,
				collapseChildren: {
					recordId: raw(Nr),
					fieldId: fieldId(this, "Collapse in Kanban"),
					value: 'Collapse in Kanban'
				},
				titleContent: {
					class: "",
					style: "background-color: " + Farbe,
					value: Bezeichnung
				},
				cardGroups: for teamMember in teamMembers do
					let groupTasks := tasks[number(Status) = number(column) and number(Verantwortlicher) = number(teamMember)];
					teamMember.{
						class: "",
						style: "",
						dropUpdates: [{
								fieldId: fieldId(first(tasks), "Verantwortlicher"),
								value: number(this)
							}],
						collapsible: true,
						collapseChildren: false,
						titleContent: {
							class: "",
							style: "color: " + Farbe + ";",
							value: Name
						},
						cards: for card in groupTasks do
							card.{
								class: "",
								style: "",
								recordId: raw(Nr),
								draggable: true,
								value: Bezeichnung,
								actions: [{
										trigger: "click",
										scripts: [{
												type: "popup",
												recordId: raw(Nr)
											}]
									}]
							}
						end
					}
				end
			}
		end
	})))
json
let allStati := ((select Status) order by Reihenfolge);
let teamMembers := (select Mitarbeiter);
let tasks := (select Aufgaben);
GIP_kanban({
	uniqueId: "ProjektAufgabenVerwaltungGroups" + Nr,
	embedded: true,
	class: "",
	style: "height: 600px;",
	kanbanColumns: for column in allStati do
		column.{
			class: "",
			style: "",
			dropUpdates: [{
					fieldId: fieldId(first(tasks), "Status"),
					value: number(this)
				}],
			collapsible: true,
			collapseChildren: {
				recordId: raw(Nr),
				fieldId: fieldId(this, "Collapse in Kanban"),
				value: 'Collapse in Kanban'
			},
			titleContent: {
				class: "",
				style: "background-color: " + Farbe,
				value: Bezeichnung
			},
			cardGroups: for teamMember in teamMembers do
				let groupTasks := tasks[number(Status) = number(column) and number(Verantwortlicher) = number(teamMember)];
				teamMember.{
					class: "",
					style: "",
					dropUpdates: [{
							fieldId: fieldId(first(tasks), "Verantwortlicher"),
							value: number(this)
						}],
					collapsible: true,
					collapseChildren: false,
					titleContent: {
						class: "",
						style: "color: " + Farbe + ";",
						value: Name
					},
					cards: for card in groupTasks do
						card.{
							class: "",
							style: "",
							recordId: raw(Nr),
							draggable: true,
							value: Bezeichnung,
							actions: [{
									trigger: "click",
									scripts: [{
											type: "popup",
											recordId: raw(Nr)
										}]
								}]
						}
					end
				}
			end
		}
	end
})