DokumentListe

Dokumente bearbeiten

Umbenennen je Zeile und Mehrfachlöschen über eine Auswahlspalte. Beides wird nur markiert und erst in der POST-Action ausgeführt.

@model DokumentListeBearbeitenBeispielViewModel

@* Umbenennen und Mehrfachlöschen wirken nur auf die Tabelle: Im Browser wird lediglich markiert,
   gesammelt in DokumenteUmbenennen bzw. DokumentLoeschenIds. Ausgeführt wird beides erst in der
   POST-Action. *@

<form asp-area="FileUpload" asp-controller="DokumentListeBearbeitenBeispiel" asp-action="Index" method="post">
    <div asp-validation-summary="All" class="text-danger"></div>
    @Html.EditorFor(m => m.PosterBelege)
    @Html.Fmh(Fmh.Button()
        .Text("Absenden")
        .Size(ButtonSize.Sm)
        .Attributes(new { @class = "mt-3" }))
</form>
using System.ComponentModel.DataAnnotations;
using FMH.Komponente.Basis.Ui.Validation.DataAnnotation;
using FMH.Komponente.FileUpload.Models;
using FMH.Komponente.Infrastruktur;

namespace FMH.Komponente.WebUI.Areas.FileUpload.Models
{
    public class DokumentListeBearbeitenBeispielViewModel
    {
        // MetaDataRename ergänzt je Zeile eine Schaltfläche zum Umbenennen, MetaDataBulkDelete eine
        // Auswahlspalte samt Sammel-Löschen. Beide Angaben wirken nur auf die Tabelle der bereits
        // vorhandenen Dokumente, nicht auf die Ablagefläche.
        [UIHint("DokumentListe")]
        [Display(Name = "Poster hochladen")]
        [AdditionalMetadata(MvcConst.MetaDataRename, "true")]
        [AdditionalMetadata(MvcConst.MetaDataBulkDelete, "true")]
        [AdditionalMetadata(MvcConst.MetaDataUploadUrl, "/FileUpload/DokumentListeBearbeitenBeispiel/Upload")]
        [AdditionalMetadata(MvcConst.MaxFileSizeInMb, "1")]
        [AdditionalMetadata(MvcConst.MaxFiles, "10")]
        [AdditionalMetadata(MvcConst.MetaDataDownloadActionName, "Download")]
        [AdditionalMetadata(MvcConst.MetaDataDownloadControllerName, "DokumentListeBearbeitenBeispiel")]
        public DokumentListeViewModel PosterBelege { get; set; } = new DokumentListeViewModel();
    }
}
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using FMH.Komponente.Basis.Erweiterungen;
using FMH.Komponente.Basis.Ui.Feedback.Alert;
using FMH.Komponente.FileUpload.Models;
using FMH.Komponente.WebUI.Areas.FileUpload.Models;
using Microsoft.AspNetCore.Mvc;

namespace FMH.Komponente.WebUI.Areas.FileUpload.Controllers.Beispiele
{
    // Zeigt Umbenennen (MetaDataRename) und Mehrfachlöschen (MetaDataBulkDelete). Beide Vorgänge
    // laufen im Browser nur als Markierung: Das Template sammelt sie in DokumenteUmbenennen bzw.
    // DokumentLoeschenIds, ausgeführt werden sie erst hier in der POST-Action.
    [Area("FileUpload")]
    public class DokumentListeBearbeitenBeispielController : Controller
    {
        private static readonly ConcurrentDictionary<int, string> Dokumente =
            new ConcurrentDictionary<int, string>(
                new Dictionary<int, string>
                {
                    [5] = "Kurs.pdf",
                    [6] = "Kurs2.pdf",
                    [7] = "Kurs3.pdf",
                    [8] = "Kurs4.pdf",
                    [9] = "Kurs5.pdf",
                    [10] = "Kurs6.pdf",
                });

        private static int letzteId = 100;

        // Auch die Doku-Seite zeigt die gefüllte Liste — deshalb öffentlich.
        public static DokumentListeBearbeitenBeispielViewModel ErstelleModell()
        {
            var modell = new DokumentListeBearbeitenBeispielViewModel();

            modell.PosterBelege.Dokumente = Dokumente
                .OrderBy(eintrag => eintrag.Key)
                .Select(eintrag => new DokumentEintrag
                                   {
                                       DokumentEintragId = eintrag.Key,
                                       Name = eintrag.Value,
                                       Groesse = 30000 + (eintrag.Key * 100),
                                   })
                .ToList();

            return modell;
        }

        [HttpGet]
        public IActionResult Index()
        {
            return this.View(ErstelleModell());
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Index(DokumentListeBearbeitenBeispielViewModel modell)
        {
            var liste = modell.PosterBelege;
            var teile = new List<string>();

            if (liste.DokumentIds != null && liste.DokumentIds.Length > 0)
            {
                teile.Add($"neu: {string.Join(", ", liste.DokumentIds.Select(NameZuId))}");
            }

            // Umbenennen: Das Template schreibt je Änderung DokumentEintragId und Name in die
            // Sammlung DokumenteUmbenennen. In einem Projekt würde die neue Bezeichnung hier
            // gespeichert; die Demo meldet sie nur zurück, damit die Ausgangslage erhalten bleibt.
            if (liste.DokumenteUmbenennen != null && liste.DokumenteUmbenennen.Length > 0)
            {
                var umbenannt = liste.DokumenteUmbenennen.Select(
                    eintrag => $"{NameZuId(eintrag.DokumentEintragId)} → {eintrag.Name}");
                teile.Add($"umbenannt: {string.Join(", ", umbenannt)}");
            }

            // Löschen: Ebenfalls erst hier — im Browser wurde die Zeile nur ausgeblendet und die Id
            // gesammelt. Auch hier bleibt die Demo-Ablage bewusst unverändert, damit das Beispiel
            // beliebig oft durchgespielt werden kann.
            if (liste.DokumentLoeschenIds != null && liste.DokumentLoeschenIds.Length > 0)
            {
                teile.Add($"gelöscht: {string.Join(", ", liste.DokumentLoeschenIds.Select(NameZuId))}");
            }

            this.AddAlert(
                teile.Count == 0
                    ? "Formular übertragen – keine Änderungen."
                    : $"Formular übertragen – {string.Join(" | ", teile)}",
                AlertType.Success,
                autoHide: true);

            return this.RedirectToAction(nameof(this.Index));
        }

        /// <summary>
        /// Der Upload-Endpunkt dieses Beispiels.
        /// </summary>
        /// <returns>Json mit der vergebenen Id.</returns>
        [HttpPost]
        public IActionResult Upload()
        {
            if (this.Request.Headers.ContainsKey("IsDropzoneRequest") == false || this.Request.Form.Files.Count == 0)
            {
                return this.Json(new { id = 0 });
            }

            var datei = this.Request.Form.Files[0];
            var bezeichnung = this.Request.Form["DateiBezeichnung"].ToString();
            var name = string.IsNullOrWhiteSpace(bezeichnung) ? datei.FileName : bezeichnung;

            var id = Interlocked.Increment(ref letzteId);
            Dokumente[id] = name;

            return this.Json(new { id });
        }

        /// <summary>
        /// Der Download-Endpunkt dieses Beispiels.
        /// </summary>
        /// <param name="id">Die Id des Dokuments aus der Tabellenzeile.</param>
        /// <returns>Die Datei zum Herunterladen.</returns>
        [HttpGet]
        public IActionResult Download(int id)
        {
            if (Dokumente.TryGetValue(id, out var name) == false)
            {
                return this.NotFound();
            }

            var inhalt = Encoding.UTF8.GetBytes($"Platzhalter-Inhalt für «{name}» (Id {id}).");

            return this.File(inhalt, "text/plain", name);
        }

        private static string NameZuId(int id)
        {
            return Dokumente.TryGetValue(id, out var name) ? name : $"Id {id}";
        }
    }
}

Darstellung

attach_file

Drag files here or click here to upload.

report_problem

You can not upload any more files.

Zwei Angaben erweitern die Tabelle der bereits zugeordneten Dokumente: MetaDataRename ergänzt je Zeile eine Schaltfläche zum Umbenennen, MetaDataBulkDelete eine Auswahlspalte samt Sammel-Löschen.

[UIHint("DokumentListe")]
[Display(Name = "Poster hochladen")]
[AdditionalMetadata(MvcConst.MetaDataRename, "true")]
[AdditionalMetadata(MvcConst.MetaDataBulkDelete, "true")]
[AdditionalMetadata(MvcConst.MetaDataUploadUrl, "/FileUpload/DokumentListeBearbeitenBeispiel/Upload")]
public DokumentListeViewModel PosterBelege { get; set; } = new DokumentListeViewModel();

Was beim Absenden ankommt

[HttpPost]
public IActionResult Index(DokumentListeBearbeitenBeispielViewModel modell)
{
    var neu = modell.PosterBelege.DokumentIds;                 // frisch hochgeladen
    var umbenannt = modell.PosterBelege.DokumenteUmbenennen;   // DokumentEintragId + neuer Name
    var geloescht = modell.PosterBelege.DokumentLoeschenIds;   // zum Löschen markiert
    ...
}
  • Umbenennen schreibt je Änderung ein Paar versteckter Felder ins Formular: <Feldname>.DokumenteUmbenennen[n].DokumentEintragId und …[n].Name. Gebunden wird das an DokumenteUmbenennen (DokumentEintrag[]).
  • Löschen – einzeln wie auch als Mehrfachauswahl – hängt die Ids an <Feldname>.DokumentLoeschenIds und blendet die Zeile aus.

Was zu beachten ist

  • Im Browser wird nur markiert. Das tatsächliche Umbenennen und Löschen gehört in die POST-Action — passiert dort nichts, sieht der Benutzer die Änderung nach dem nächsten Laden nicht.
  • Das Mehrfachlöschen fragt vor dem Ausblenden über einen Bestätigungsdialog nach.
  • Beim Löschen zählt das Template das versteckte Feld <Feldname>ExistingDocuments herunter. Ist die Obergrenze dadurch wieder unterschritten, nimmt die Ablagefläche sofort wieder Dateien an.
  • Die Angaben wirken nur auf die Tabelle. Ist Dokumente leer, wird gar keine Tabelle gerendert und beide Optionen bleiben ohne sichtbare Wirkung.