März 2018

Band 33, Nummer 3

ASP.NET: Verwenden von Razor zum Generieren von HTML für Vorlagen in einer Single-Page-App

Von Nick Harrison

SPA-Apps (Single-Page Application, Single-Page-Webanwendung) sind sehr beliebt und das aus gutem Grund. Benutzer erwarten, dass Web-Apps schnell und ansprechend sind und auf jedem Gerät von Smartphones bis hin zu Desktops mit den größten Bildschirmen funktionieren. Darüber hinaus müssen sie sicher und visuell ansprechend sein und eine sinnvolle Aufgabe erfüllen. Das ist viel verlangt von einer Web-App. Aber das ist eigentlich nur der Ausgangspunkt.

Als Benutzer damit begannen, mehr von Webanwendungen zu erwarten, kam es zu einer Explosion von clientseitigen Frameworks, die durch die Bereitstellung einer clientseitigen Implementierung des MVVM-Musters (Model-View-ViewModel) „helfen“ sollten. Manchmal kommt es mir so vor, als gäbe es jede Woche ein neues Framework. Einige haben sich als hilfreich erwiesen, bei anderen war dies weniger der Fall. Aber sie alle implementieren das Entwurfsmuster unterschiedlich, fügen neue Features hinzu oder lösen wiederkehrende Probleme auf ihre eigene Art und Weise. Jedes Framework verfolgt einen anderen Ansatz in Bezug auf das Entwurfsmuster, mit einer einzigartigen Syntax für Vorlagen und der Einführung benutzerdefinierter Konzepte wie Factorys, Modulen und Observables. Das Ergebnis ist eine scharfe Lernkurve, die Entwickler daran hindern kann, auf dem Laufenden zu bleiben, wenn neue Frameworks in Mode oder aus der Mode kommen. Alles, was wir tun können, um die Lernkurve zu erleichtern, ist von Vorteil.

Die Tatsache, dass clientseitige Frameworks das MVVM-Muster implementieren und ASP.NET das MVC-Muster auf dem Server implementiert, hat zu einiger Verwirrung geführt. Wie kann serverseitiges MVC mit clientseitigem MVVM kombiniert werden? Für viele Entwickler ist die Antwort einfach: Beides kann nicht kombiniert werden. Der übliche Ansatz besteht darin, statische HTML-Seiten oder Fragmente zu erstellen und diese mit minimaler serverseitiger Verarbeitung dem Client zur Verfügung zu stellen. In diesem Szenario können Web-API-Controller oft die Verarbeitung ersetzen, die der MVC-Controller in der Vergangenheit übernommen hätte.

Wie Abbildung 1 zeigt, gibt es minimale Anforderungen an den Webserver und einen vollwertigen Server für die Web-API, und auf beide Server muss von außerhalb der Firewall zugegriffen werden können.

Typisches Anwendungslayout
Abbildung 1: Typisches Anwendungslayout

Diese Anordnung bietet eine gute Trennung der Belange, und viele Anwendungen wurden nach diesem Muster geschrieben, aber als Entwickler verzichten Sie auf vieles. ASP.NET hat noch viel mehr zu bieten, und seine Implementierung von MVC bietet viele Features, die immer noch relevant sind, selbst wenn eines der clientseitigen Frameworks einen Großteil der Schwerstarbeit leistet. In diesem Artikel werde ich mich auf eines dieser Features konzentrieren: die Razor-Ansichtsengine.

Razor in der SPA

Die Razor-Ansichtsengine stellt ein kleines Wunder dar, wenn es darum geht, die Generierung von HTML-Markup zu vereinfachen. Mit nur wenigen Anpassungen kann das generierte Markup leicht an die Vorlagenerwartungen für jedes Framework, das auf dem Client verwendet wird, angepasst werden. Ich werde einige Beispiele mit Angular und Knockout aufzeigen. Diese Techniken funktionieren jedoch unabhängig vom verwendeten Framework. Wenn Sie Aufrufe zurück an den Server senden, um Vorlagen für Ihre Anwendung bereitzustellen, können Sie jetzt Razor verwenden, um die Schwerstarbeit der HTML-Generierung zu erledigen.

Vieles wird Ihnen hier gefallen. EditorTemplates und DisplayTemplates sind immer noch Ihre Freunde, ebenso wie Gerüstbauvorlagen. Sie können immer noch Teilansichten injizieren, und der flüssige Fluss der Razor-Syntax ist noch immer vorhanden, um ihn zu Ihrem Vorteil zu nutzen. Über die Ansichten hinaus können Sie auch den MVC-Controller verwenden, der die Verarbeitungsleistung erhöhen kann, um Dinge zu beschleunigen oder eine zusätzliche Sicherheitsebene hinzuzufügen. Wenn das nicht nötig ist, können die Controller ein einfacher Übergang zur Ansicht sein.

Um zu zeigen, wie dies zu Ihrem Vorteil genutzt werden kann, werde ich die Erstellung einiger Dateneingabemasken für eine Zeiterfassungsanwendung Schritt für Schritt erläutern und zeigen, wie Razor dabei helfen kann, die Erstellung von HTML zu beschleunigen, das für die Verwendung durch Angular oder Knockout geeignet ist.

Wie Abbildung 2 zeigt, ändert sich nicht viel im Vergleich zum typischen Layout. Ich habe eine Option hinzugefügt, die zeigt, dass die MVC-Anwendung mit dieser Datenbank interagieren kann. Dies ist in diesem Stadium nicht notwendig, aber wenn es Ihre Verarbeitung vereinfacht, ist diese Option verfügbar. Allgemein sieht der Workflow wie folgt aus:

  • Abrufen einer ganzen Seite aus der MVC-Anwendung, einschließlich aller Stylesheet- und JavaScript-Verweise und minimaler Inhalte.
  • Nachdem die Seite im Browser vollständig geladen und das Framework initialisiert wurde, kann das Framework den MVC-Server aufrufen, um eine Vorlage als Teilansicht anzufordern.
  • Dieses Vorlage wird mit Razor generiert, normalerweise ohne zugehörige Daten.
  • Gleichzeitig ruft das Framework die API auf, um die Daten abzurufen, die an die von der MVC-Website erhaltene Vorlage gebunden werden.
  • Nachdem der Benutzer alle erforderlichen Bearbeitungen vorgenommen hat, sendet das Framework Aufrufe an den Web-API-Server, um die Back-End-Datenbank zu aktualisieren.

Einfache Workflowhinzufügung in Razor
Abbildung 2: Einfache Workflowhinzufügung in Razor

Dieser Workflow wiederholt sich bei Bedarf, wenn eine neue Vorlage angefordert wird. Wenn das Framework es erlaubt, eine URL für eine Vorlage anzugeben, kann MVC die Vorlage bereitstellen. Während ich Beispiele zeige, die Ansichten generieren, die für die Bindung in Angular und Knockout geeignet sind, denken Sie daran, dass dies kaum die einzigen Optionen sind.

Einrichten einer Lösung

Aus der Einrichtungsperspektive benötigen Sie mindestens drei Projekte. Ein Projekt für die Web-API, ein Projekt für die MVC-Anwendung und schließlich ein gemeinsames Projekt zum Hosten von gemeinsamem Code zwischen diesen beiden Projekten. Für die Zwecke dieses Artikels wird das gemeinsame Projekt die ViewModels hosten, sodass sie sowohl im Web als auch in der Web-API verwendet werden können. Das erste Projekt wird wie in Abbildung 3 gezeigt strukturiert.

Struktur des ersten Projekts
Abbildung 3: Struktur des ersten Projekts

In der realen Welt unterstützen Sie nur ein clientseitiges Framework. Für diesen Artikel vereinfacht es die Sache, zwei MVC-Projekte zu verwenden: ein Projekt für jedes der zu zeigenden Frameworks. Ähnliches kann passieren, wenn Sie eine Webanwendung, eine angepasste mobile Anwendung, eine SharePoint-Anwendung, eine Desktopanwendung oder ein anderes Szenario unterstützen müssen, das nicht einfach aus einem gewöhnlichen UI-Projekt heraus gerendert werden kann. Unabhängig davon muss nur die im UI-Projekt eingebettete Logik wiederholt werden, da Sie mehrere Front-Ends unterstützen.

Einbringen von Daten

In der Praxis werden Ihre Daten in einer Datenbank gespeichert, wahrscheinlich mithilfe eines ORM (Object Relational Mapper) wie z.B. Entity Framework. Hier werde ich Fragen der Datenpersistenz umgehen, um mich auf das Front-End zu konzentrieren. Ich werde die Web-API-Controller verwenden, um hartcodierte Werte in den Get-Aktionen zurückzugeben, und ich werde diese Beispiele in einer perfekten Welt ausführen, in der jeder API-Aufruf erfolgreich zurückgegeben wird. Das Hinzufügen einer entsprechenden Fehlerbehandlung verbleibt als Übung für Sie, den unerschrockenen Leser.

Für dieses Beispiel verwende ich ein einzelnes Ansichtsmodell, das mit Decorator-Attributen aus dem Namespace System.ComponentModel.DataAnnotations versehen ist, wie in Abbildung 4 gezeigt.

Abbildung 4: Einfaches TimeEntryViewModel

namespace TimeTracking.Common.ViewModels
{
  public class TimeEntryViewModel
  {
    [Key]
    public int Id { get; set; }
    [Required (ErrorMessage ="All time entries must always be entered")]
    [DateRangeValidator (ErrorMessage ="Date is outside of expected range",
      MinimalDateOffset =-5, MaximumDateOffset =0)]
    public DateTime StartTime { get; set; }
    [DateRangeValidator(ErrorMessage = "Date is outside of expected range",
      MinimalDateOffset = -5, MaximumDateOffset = 0)]
    public DateTime EndTime { get; set; }
    [StringLength (128, ErrorMessage =
      "Task name should be less than 128 characters")]
    [Required (ErrorMessage ="All time entries should be associated with a task")]
    public string Task { get; set; }
    public string Comment { get; set; }
  }
}

Das DateRangeValidator-Attribut stammt nicht aus dem DataAnnotations-Namespace. Dies ist kein Standardvalidierungsattribut, aber Abbildung 5 zeigt, wie einfach ein neues Validierungssteuerelement erstellt werden kann. Einmal angewendet, verhält es sich wie die Standard-Validierungssteuerelemente.

Abbildung 5: Benutzerdefiniertes Validierungssteuerelement

public class DateRangeValidator : ValidationAttribute
{
  public int MinimalDateOffset { get; set; }
  public int MaximumDateOffset { get; set; }
  protected override ValidationResult IsValid(object value,
    ValidationContext validationContext)
  {
    if (!(value is DateTime))
      return new ValidationResult("Inputted value is not a date");
    var date = (DateTime)value;
    if ((date >= DateTime.Today.AddDays(MinimalDateOffset)) &&
      date <=  DateTime.Today.AddDays(MaximumDateOffset))
      return ValidationResult.Success;
    return new ValidationResult(ErrorMessage);
  }
}

Jedes Mal, wenn das Modell validiert wird, werden alle Validierungssteuerelemente ausgeführt, einschließlich aller benutzerdefinierten Validierungssteuerelemente. Mit Razor erstellte Ansichten können diese Validierungen problemlos clientseitig einbinden, und diese Validierungen werden von der Modellbindung automatisch auf dem Server ausgewertet. Die Validierung von Benutzereingaben ist der Schlüssel zu einem sichereren System.

API-Controller

Da ich nun über ein Ansichtsmodell verfüge, bin ich bereit, einen Controller zu generieren. Ich werde den integrierten Gerüstbau verwenden, um den Controller zu erstellen. Dadurch werden Methoden für die standardmäßigen verbbasierten Aktionen (Get, Post, Put, Delete) erstellt. Für die Zwecke dieses Artikels mache ich mir keine Gedanken über die Details dieser Aktionen. Ich bin nur daran interessiert, zu überprüfen, ob ich über die Endpunkte verfüge, die vom clientseitigen Framework aufgerufen werden.

Erstellen einer Ansicht

Als nächstes wende ich mich der Ansicht zu, die der integrierte Gerüstbau aus dem Ansichtsmodell generiert.

Im Projekt TimeTracking.Web füge ich einen neuen Controller hinzu und nenne ihn TimeEntryController, und ich erstelle ihn als leeren Controller. In diesem Controller erstelle ich die Edit-Aktion, indem ich den folgenden Code hinzufüge:

public ActionResult Edit()
{
  return PartialView(new TimeEntryViewModel());
}

Innerhalb dieser Methode klicke ich mit der rechten Maustaste und wähle „Add View“ aus. Im Popupfenster gebe ich an, dass ich eine Bearbeitungsvorlage wünsche, indem ich die TimeEntryViewModel-Vorlage auswähle.

Zusätzlich zur Angabe des Modells stelle ich sicher, die Erstellung einer Teilansicht anzugeben. Ich möchte, dass die generierte Ansicht nur das in der generierten Ansicht definierte Markup enthält. Dieses HTML-Fragment wird in die vorhandene Seite auf dem Client eingefügt. Ein Beispiel für das vom Gerüstbau generierte Markup wird in Abbildung 6 gezeigt.

Abbildung 6: Razor-Markup für die Bearbeitungsansicht

@model TimeTracking.Common.ViewModels.TimeEntryViewModel
@using (Html.BeginForm())
{
  @Html.AntiForgeryToken()
  <div class="form-horizontal">
    <h4>TimeEntryViewModel</h4>
    <hr />
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.HiddenFor(model => model.Id)
    <div class="form-group">
      @Html.LabelFor(model => model.StartTime, htmlAttributes:
        new { @class = "control-label col-md-2" })
      <div class="col-md-10">
        @Html.EditorFor(model => model.StartTime,
          new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.StartTime, "",
          new { @class = "text-danger" })
      </div>
    </div>
    ...
  </div>
}
<div>
  @Html.ActionLink("Back to List", "Index")
</div>

Es gibt ein paar wichtige Dinge, die Sie bei dieser generierten Ansicht beachten sollten, die von Haus aus eine reaktionsschnelle, bootstrapbasierte Benutzeroberfläche generiert. Hierzu gehören:

  • Die Formulargruppe wird für jede Eigenschaft im Ansichtsmodell wiederholt.
  • Die Id-Eigenschaft wird ausgeblendet, weil sie mit dem Key-Attribut gekennzeichnet ist, das sie als Schlüssel identifiziert, der nicht geändert werden darf.
  • Jedem Eingabefeld sind Platzhalter für Validierungsnachrichten zugeordnet, die verwendet werden können, wenn die unauffällige Validierung aktiviert ist.
  • Alle Bezeichnungen werden mit dem LabelFor-Hilfsprogramm hinzugefügt, das Metadaten für die Eigenschaft interpretiert, um die entsprechende Bezeichnung zu bestimmen. Das DisplayAttribut kann verwendet werden, um einen besseren Namen zu vergeben und die Lokalisierung zu übernehmen.
  • Jedes Eingabesteuerelement wird mit dem EditorFor-Hilfsprogramm hinzugefügt, das die Metadaten für die Eigenschaft interpretiert, um den geeigneten Editor zu bestimmen.

Die Metadaten werden zur Laufzeit ausgewertet. Dies bedeutet, dass Sie Ihrem Modell nach dem Generieren der Ansicht Attribute hinzufügen können, die zur Bestimmung der Validierungssteuerelemente, Bezeichnungen und Editoren verwendet werden.

Da es sich bei dem Gerüstbau um eine einmalige Codegenerierung handelt, kann das generierte Markup problemlos bearbeitet werden, weil dies allgemein erwartet wird.

Bindung an Knockout

Damit das generierte Markup mit Knockout funktioniert, muss ich dem generierten Markup einige Attribute hinzufügen. Ich werde mich nicht weiter in die interne Funktionsweise von Knockout vertiefen, sondern nur darauf hinweisen, dass die Bindung ein data-bind-Attribut verwendet. Die Bindungsdeklaration gibt die Art der Bindung und dann die zu verwendende Eigenschaft an. Ich muss den Eingabesteuerelementen ein data-bind-Attribut hinzufügen. Wenn Sie auf das generierte Markup zurückblicken, erkennen Sie, wie das Klassenattribut hinzugefügt wird. Nach dem gleichen Verfahren kann ich die EditorFor-Funktion ändern, wie hier im Code gezeigt:

@Html.EditorFor(model => model.StartTime,
  new { htmlAttributes = new { @class = "form-control",
         data_bind = "text: StartTime" } })

Mit dem generierten Markup aus dem integrierten Gerüstbau ist dies die einzige Änderung, die erforderlich ist, um Knockout-Bindungen hinzuzufügen.

Bindung an Angular

Die Datenbindung mit Angular funktioniert ähnlich. Ich kann ein ng-model-Attribut oder ein data-ng-model-Attribut hinzufügen. Durch das data-ng-model-Attribut bleibt das Markup mit HTML5 konform, aber das ng-bind-Attribut wird immer noch häufig verwendet. In beiden Fällen ist der Wert für das Attribut einfach der Name der zu bindenden Eigenschaft. Um die Bindung an einen Angular-Controller zu unterstützen, ändere ich die EditorFor-Funktion, indem ich den folgenden Code verwende:

@Html.EditorFor(model => model.StartTime,
  new { htmlAttributes = new { @class     = "form-control",
                                  data_ng-model = "StartTime" } })

Es gibt noch ein paar kleinere Optimierungen, die bei der Definition der Anwendung und des Controllers ins Spiel kommen. Verwenden Sie den Beispielcode für das vollständige Arbeitsbeispiel, um diese Änderungen im Kontext zu untersuchen.

Sie können eine ähnliche Technik anwenden, damit das generierte Markup mit jedem MVVM-Framework funktioniert, das Sie verwenden.

Ändern des Gerüstbaus

Da der Gerüstbau T4 zum Generieren der Ausgabe verwendet, kann ich ändern, was generiert wird, um zu verhindern, dass jede generierte Ansicht bearbeitet werden muss. Die verwendeten Vorlagen werden im Installationsverzeichnis für Visual Studio gespeichert. Für Visual Studio 2017 finden Sie diese Vorlagen hier:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\
  Common7\IDE\Extensions\Microsoft\Web\Mvc\Scaffolding\Templates\MvcView

Sie könnten diese Vorlagen direkt bearbeiten, aber das würde sich auf jedes Projekt auswirken, an dem Sie von Ihrem Computer aus arbeiten, und jeder andere Benutzer, der am Projekt arbeitet, würde nicht von den Änderungen an den Vorlagen profitieren. Stattdessen sollten Sie die T4-Vorlagen dem Projekt hinzufügen und die lokale Kopie die Standardimplementierung überschreiben lassen.

Kopieren Sie einfach die gewünschten Vorlagen in einen Ordner mit dem Namen „CodeTemplates“ im Stammordner Ihres Projekts. Es stehen Vorlagen für C# und für Visual Basic zur Verfügung. Die Sprache spiegelt sich im Dateinamen wider. Sie benötigen die Vorlagen nur für eine Sprache. Wenn Sie den Gerüstbau aus Visual Studio aufrufen, wird zunächst im Ordner „CodeTemplates“ nach einer Vorlage gesucht, die verwendet werden kann. Wenn dort keine Vorlage gefunden wird, sucht die Gerüstbauengine unter dem Visual Studio-Installationsverzeichnis.

T4 ist ein leistungsfähiges Tool für die Generierung von Text im Allgemeinen, nicht nur von Code. Das Erlernen ist ein riesiges Thema für sich, aber machen Sie sich keine Sorgen, wenn Sie noch nicht mit T4 vertraut sind. Dies sind sehr kleine Änderungen an den Vorlagen. Sie müssen sich nicht in die Interna vertiefen, um zu verstehen, wie die Magie von T4 funktioniert, aber Sie müssen eine Erweiterung herunterladen, um Unterstützung für die Bearbeitung von T4-Vorlagen in Visual Studio hinzuzufügen. Der Tangible T4-Editor (bit.ly/2Flqiqiqt) und der Devart T4-Editor (bit.ly/2qTO4GU) bieten beide hervorragende Communityversionen ihrer T4-Editoren für die Bearbeitung von T4-Vorlagen mit Syntaxhervorhebung, durch die es einfacher wird, vorlagensteuernden Code von Code zu trennen, der von der Vorlage erstellt wird.

Wenn Sie die Datei „Edit.cs.t4“ öffnen, erkennen Sie Codeblöcke zum Steuern der Vorlage und Codeblöcke, die das Markup darstellen. Ein großer Teil des Codes, der die Vorlage steuert, stellt eine bedingte Verarbeitung für die Behandlung von Sonderfällen wie die Unterstützung von Teilseiten und die Verarbeitung von speziellen Eigenschaften wie Fremdschlüsseln, Enumerationen und booleschen Werten dar. Abbildung 7 zeigt ein Beispiel der Vorlage in Visual Studio, wenn eine entsprechende Erweiterung verwendet wird. Der Code, der die Vorlage steuert, wird in den zugeklappten Abschnitten ausgeblendet, wodurch es einfacher wird, die Ausgabe zu erkennen.

Die T4-Bearbeitungsvorlage von Visual Studio
Abbildung 7: Die T4-Bearbeitungsvorlage von Visual Studio

Glücklicherweise müssen Sie keine Ablaufverfolgung durch diese Bedingungen ausführen. Suchen Sie stattdessen nach dem generierten Markup, das Sie ändern möchten. In diesem Fall geht es mir um sechs verschiedene Anweisungen. Sie werden als Referenz in Abbildung 8 extrahiert.

Abbildung 8: Originalcode für die Generierung der Editoren

@Html.DropDownList("<#= property.PropertyName #>", null,
  htmlAttributes: new { @class = "form-control" })
@Html.DropDownList("<#= property.PropertyName #>", String.Empty)
@Html.EditorFor(model => model.<#= property.PropertyName #>)
@Html.EnumDropDownListFor(model => model.<#= property.PropertyName #>,
  htmlAttributes: new { @class = "form-control" })
@Html.EditorFor(model => model.<#= property.PropertyName #>,
  new { htmlAttributes = new { @class = "form-control" } })
@Html.EditorFor(model => model.<#= property.PropertyName #>)

Sobald Sie die Updates zur Unterstützung Ihres spezifischen clientseitigen Frameworks vorgenommen haben, unterstützen alle neuen Ansichten, die mit der Vorlage generiert werden, Ihr Framework automatisch.

Clientseitige Validierung

Als ich die generierte Ansicht untersucht habe, habe ich die ValidationMessageFor-Funktionsaufrufe übersprungen, die für jede Eigenschaft vorgenommen werden. Diese Aufrufe generieren Platzhalter, um alle Validierungsnachrichten anzuzeigen, die bei der Auswertung von clientseitigen Validierungen erstellt werden. Diese Validierungen basieren auf den Validierungsattributen, die dem Modell hinzugefügt wurden. Um diese clientseitigen Validierungen zu aktivieren, müssen lediglich Verweise auf die jquery-Validierungsskripts hinzugefügt werden:

@Scripts.Render("~/bundles/jqueryval")

Das jqueryval-Bundle wird in der Klasse BundleConfig aus dem Ordner „App_Start“ definiert.

Wenn Sie versuchen, das Formular ohne Eingabe von Daten zu übermitteln, werden die erforderlichen Feldüberprüfungen ausgelöst, um die Übermittlung zu verhindern.

Wenn Sie clientseitig eine andere Validierungsstrategie bevorzugen, z.B. Bootstrapformularvalidierung (bit.ly/2CZmRqR), können Sie die T4-Vorlage leicht ändern, um die ValidationMessageFor-Aufrufe nicht auszugeben. Wenn Sie den nativen Validierungsansatz nicht verwenden, müssen Sie auch nicht auf das jqueryval-Bundle verweisen, da es nicht mehr benötigt wird.

Editor-Vorlagen

Da ich das Eingabesteuerelement durch den Aufruf des EditorFor-HTML-Hilfsprogramms angebe, gebe ich nicht explizit an, welches Eingabesteuerelement verwendet werden soll. Stattdessen überlasse ich es dem MVC-Framework, das am besten geeignete Eingabesteuerelement für die angegebene Eigenschaft basierend auf dem Datentyp oder Attributen wie UIHint zu verwenden. Ich kann die Auswahl des Editors auch direkt beeinflussen, indem ich explizit eine EditorTemplate erstelle. Auf diese Weise kann ich auf globaler Ebene steuern, wie Eingaben bestimmter Typen behandelt werden.

Der Standard-Editor für eine DateTime-Eigenschaft ist ein Textfeld. Nicht der ideale Editor, aber das kann ich ja ändern.

Ich füge dem Ordner „\Views\Shared\EditorTemplates“ eine Teilansicht mit dem Namen „DateTime.cshtml“ hinzu. Das dieser Datei hinzugefügte Markup wird als Editor für jede Eigenschaft vom Typ DateTime verwendet. Ich füge das in Abbildung 9 gezeigte Markup der Teilansicht hinzu und füge den folgenden Code unten in der Datei „Layout.cshtml“ hinzu:

<script>
  $(document).ready(function () {
    $(".date").datetimepicker();
  });
</script>
Figure 9 Markup for a DateTime Editor
@model DateTime?
<div class="container">
  <div class="row">
    <div class='col-md-10'>
      <div class="form-group ">
        <div class='input-group date'>
         <span class="input-group-addon">
           <span class="glyphicon glyphicon-calendar"></span>
         </span>
         @Html.TextBox("", (Model.HasValue ?
           Model.Value.ToShortDateString() :
           string.Empty), new
           {
             @class = "form-control"
           })
        </div>
      </div>
    </div>
  </div>
</div>

Nachdem diese Codeelemente hinzugefügt wurden, verfüge ich über einen recht netten Datums- und Uhrzeit-Editor, mit dessen Hilfe ich eine DateTime-Eigenschaft bearbeiten kann. Abbildung 10 zeigt diesen neuen Editor in Aktion.

Eine aktivierte Datumsauswahl
Abbildung 10: Eine aktivierte Datumsauswahl

Zusammenfassung

Wie Sie gesehen haben, kann Razor eine Menge dazu beitragen, die Erstellung von Ansichten in jedem beliebigen clientseitigen MVVM-Framework, das Sie verwenden möchten, zu vereinfachen und zu optimieren. Sie haben die Freiheit, Anwendungsstile und -konventionen zu unterstützen, und Features wie Gerüstbau und EditorTemplates sorgen für Konsistenz in Ihrer Anwendung. Sie ermöglichen außerdem die integrierte Unterstützung für Validierungen auf der Grundlage von Attributen, die Ihrem Ansichtsmodell hinzugefügt wurden, wodurch Ihre Anwendung sicherer wird.

Werfen Sie einen zweiten Blick auf ASP.NET MVC, und Sie werden viele Bereiche finden, die immer noch relevant und nützlich sind, selbst wenn sich die Landschaft der Webanwendungen weiter verändert.


Nick Harrison ist Software Consultant und lebt zusammen mit seiner Frau Tracy und einer Tochter in Columbia (South Carolina, USA). Er ist ein Full-Stack-Entwickler, der seit 2002 mit .NET arbeitet, um Geschäftslösungen zu erstellen. Folgen Sie ihm auf Twitter: @Neh123us. Dort kündigt er auch seine Blogbeiträge, veröffentlichten Arbeiten und Vorträge an.

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Lide Winburn (Softdocks Inc.)
Lide Winburn ist Cloudarchitekt bei Softdocs, Inc. und unterstützt Bildungseinrichtungen dabei, papierlos zu arbeiten. In seiner Freizeit spielt er im Amateurhockeyteam und ist zusammen mit seiner Familie begeisterter Kinogänger.


Diesen Artikel im MSDN Magazine-Forum diskutieren