Wizard
Viele Schritte
Derselbe Aufbau mit zwölf Schritten – an der Konfiguration ändert sich nichts. Zu sehen ist, wie sich die Schritt-Navigation bei vielen Einträgen verhält.
@model WizardBeispielViewModel
@* Derselbe Aufbau mit zwölf Schritten. Interessant ist hier allein die Schritt-Navigation: Sie
bricht um und bleibt bedienbar, ohne dass an der Konfiguration etwas anderes gesetzt wäre. *@
<form method="post" asp-area="Wizard" asp-controller="WizardBeispiel" asp-action="Schritt">
@Html.Fmh(Fmh.Wizard()
.Id("wz-viele")
.Add(ViewBag.Wizard as WizardSetup))
<div class="my-4" style="max-width: 40rem;">
@Html.HiddenFor(m => m.Nr)
@Html.HiddenFor(m => m.Anzahl)
@Html.EditorFor(m => m.Bemerkung)
</div>
@Html.Fmh(Fmh.WizardNavigation()
.Add(ViewBag.Wizard as WizardSetup))
</form>
using System.Collections.Generic;
using System.Linq;
using FMH.Komponente.Infrastruktur.Ui.Controls.Button;
using FMH.Komponente.WebUI.Areas.Wizard.Models;
using FMH.Komponente.Wizard.Ui.Controls.Wizard.Daten;
using FMH.Komponente.Wizard.Ui.Controls.Wizard.Erweiterungen;
using Microsoft.AspNetCore.Mvc;
namespace FMH.Komponente.WebUI.Areas.Wizard.Controllers.Beispiele
{
// Ein Endpunkt für alle Schritte: Welcher Schritt gerade läuft, sagt die Route (nr). In einem
// Projekt sind es üblicherweise benannte Actions je Schritt (Schritt01, Schritt02, …) — die
// Mechanik ist dieselbe, hier bleibt der Ablauf so aber in einer Datei nachlesbar.
[Area("Wizard")]
public class WizardBeispielController : Controller
{
[HttpGet]
public IActionResult Schritt(int nr = 1, int anzahl = 5)
{
this.ViewBag.Wizard = BuildWizard(nr, anzahl);
return this.View(new WizardBeispielViewModel { Nr = nr, Anzahl = anzahl });
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Schritt(WizardBeispielViewModel modell, string submitButton)
{
// Die Wizardkonfiguration wird für den POST neu aufgebaut — sie wird nicht mitgeführt.
var wz = BuildWizard(modell.Nr, modell.Anzahl);
this.ViewBag.Wizard = wz;
// Vorgelagerte Prüfung: IsWizardValid berücksichtigt die über DisableInputValidationFor
// abgeschalteten Bereiche, bevor es auf ModelState.IsValid zurückfällt. Ohne diesen
// Aufruf würde die Pflichtangabe auch «Speichern» und die Schritt-Navigation blockieren.
if (this.ModelState.IsWizardValid(wz, submitButton))
{
// PRG: Das Ziel ergibt sich aus der Wizardkonfiguration und der gedrückten
// Schaltfläche — der Controller muss die Schrittlogik nicht selbst kennen.
return this.RedirectWithWizardSetupAndNavigationValue(wz, submitButton);
}
return this.View(modell);
}
// Die Konfiguration liegt in einer eigenen Methode, damit GET und POST dieselbe erzeugen.
private static WizardSetup BuildWizard(int nr, int anzahl)
{
var ws = new WizardSetup
{
Title = "Antrag einreichen",
// Präfix der Button-Ids. Darüber sprechen die jQuery-Funktionen
// wizardNavbarDisabled/-Enabled die Schaltflächen an.
IdPrefix = "wz",
};
ws.Steps = Enumerable.Range(1, anzahl)
.Select(
i => new WizardStep
{
Title = SchrittTitel(i, anzahl),
Action = new MvcAction("Schritt", "WizardBeispiel", new { nr = i, anzahl }),
// Der letzte Schritt lässt sich nicht direkt anspringen — ein häufiger
// fachlicher Fall: Übermitteln erst nach dem Durchlaufen.
IsDisabled = i == anzahl,
State = ZuStatus(i, nr),
})
.ToList();
ws.SetCancelAction("Wizard", "Demo", new { area = "Wizard" });
ws.SetHelpAction("/Wizard/Demo/Wizard");
ws.SetSaveButtonText("Entwurf speichern");
// Speichern und die Schritt-Navigation sollen auch mit unvollständigen Angaben
// funktionieren; nur «Weiter» erzwingt die Pflichtangabe.
ws.DisableInputValidationFor(ValidationArea.NavigationWizard | ValidationArea.Save);
return ws;
}
private static string SchrittTitel(int nummer, int anzahl)
{
if (nummer == 1)
{
return "Bemerkungen";
}
if (nummer == anzahl)
{
return "Antrag übermitteln";
}
return $"Angaben Teil {nummer - 1}";
}
// Schritte vor dem aktuellen gelten als erledigt, der aktuelle ist Current, der Rest offen.
private static WizardStepState ZuStatus(int nummer, int aktuell)
{
if (nummer < aktuell)
{
return WizardStepState.Filled;
}
return nummer == aktuell ? WizardStepState.Current : WizardStepState.ToDo;
}
}
}
Darstellung
Zwölf statt fünf Schritte — an der Konfiguration ändert sich nichts, die Schritte kommen aus derselben Liste. Sichtbar wird nur, wie sich die Schritt-Navigation bei vielen Einträgen verhält.
Zwei Dinge lohnen bei langen Wizards die Überlegung:
StepNavigationDisabled, wenn die Reihenfolge fachlich zwingend ist. Bei vielen Schritten ist der Direktsprung sonst der schnellste Weg in einen inkonsistenten Zustand.IsDisabledje Schritt als feinere Variante: Die Navigation bleibt, einzelne Ziele sind aber gesperrt — hier der letzte Schritt «Antrag übermitteln».
Die Beschriftungen der Schaltflächen bleiben unverändert; bei vielen Schritten hilft ein
sprechenderer Text über SetNextButtonText(...) eher als eine zusätzliche Erklärung auf der Seite.