Data Annotations Webforms und Bootstrap ausgereizt

Auf dem Weg zum Lazy Web Developer habe ich dieses Mal den Data Annotations auf den Zahn gefühlt. In Silverlight war das richtig geiler Scheiss.

Man kann einer Klasse per Property-Attributen Regeln mitgeben. Diese werden Data Annotations genannt. Die Regeln können sich auf Darstellung, Reihenfolge oder Gültigkeit eines Propertys beziehen.

   1:  Public Class daten1
   2:      <Key()>
   3:      Property id As Integer
   4:      <Required(ErrorMessage:="brauche Daten")>
   5:      <EmailAddress(ErrorMessage:="brauche email")>
   6:      Property email As String
   7:      <Required(ErrorMessage:="musse haben")>
   8:      Property name As String
   9:  End Class

In der richtigen Umgebung werden diese Klassenattribute ausgewertet.

ASP.NET nutzt die Klassenattribute über Dynamic Data. Eingebettet in ein Datensteuerelement wertet das DynamicEntity-Steuerelement das gebundene Objekt (Itemtype) aus.

   1:      <link href="../Content/bootstrap.css" rel="stylesheet" />
   2:      <script src="../Scripts/jquery-2.0.3.js"></script>
   3:      <script src="../Scripts/bootstrap.js"></script>
   4:  </head>
   5:  <body>
   6:      <form id="form1" runat="server">
   7:      <div>
   8:          <asp:FormView ID="FormView1" DataKeyNames="id" 
ItemType="daten1"
   9:               runat="server" DefaultMode="Insert">
  10:              <InsertItemTemplate>
  11:                  <asp:DynamicEntity runat="server" ID="eins" 
Mode="Insert"/>
  12:                  <asp:Button runat="server" CommandName="Insert" 
Text="insert" class="btn btn-default"/>
  13:              </InsertItemTemplate>
  14:          </asp:FormView>
  15:      </div>
  16:      </form>
  17:  </body>

 

Die Magie geschieht im Verzeichnis Dynamic Data. Je nach Mode (hier insert), wird das Template aus EntityTemplate geladen.

image

Dieses Template wurde von mir leicht angepasst, um den Bootstrap 3 Formular-Regularien zu folgen.

   1:  <asp:EntityTemplate runat="server" ID="EntityTemplate1">
   2:      <ItemTemplate>
   3:          <div class="form-group">
   4:              <asp:Label ID="Label1" runat="server" OnInit="Label_Init"
   5:                  OnPreRender="Label_PreRender" 
CssClass="col-sm-2 control-label" />
   6:              <div class="controls">
   7:                  <asp:DynamicControl runat="server" ID="DynamicControl"
   8:                      Mode="Edit" OnInit="DynamicControl_Init" />
   9:              </div>
  10:          </div>
  11:      </ItemTemplate>
  12:  </asp:EntityTemplate>

 

Das DynamicControl-Element wird dann passend zum Datentypen durch ein Template aus dem Verzeichnis FieldTemplate ersetzt. In diesem Beispiel Integer_Edit, Text_Edit und EMailAddress_Edit.

Folgendes deklarative ASP.NET Code-Beispiel zeigt das Text-Template. Wichtig ist Display Dynamic einzustellen, um den Platz in der Darstellung nicht zu verbrauchen, wenn keine Fehlermeldung angezeigt wird. Außerdem verwendet Bootstrap für sein error HTML Template die CSS-Klasse help-block. Ergebnis ist roter Text.

   1:  <asp:TextBox ID="TextBox1" runat="server" Text='<%# FieldValueEditString %>'
   2:       CssClass="form-control"></asp:TextBox>
   3:   
   4:  <asp:RequiredFieldValidator runat="server" ID="RequiredFieldValidator1" 
CssClass="help-block" ControlToValidate="TextBox1" Display="Dynamic" Enabled="false" />
   5:  <asp:RegularExpressionValidator runat="server" ID="RegularExpressionValidator1" 
CssClass="help-block" ControlToValidate="TextBox1" Display="Dynamic" Enabled="false" />
   6:  <asp:DynamicValidator runat="server" ID="DynamicValidator1" 
CssClass="help-block" ControlToValidate="TextBox1" Display="Dynamic" />
   7:   

Die ASP.NET Validation Controls haben allerdings ein Feature, das hier stört. Diese können mit einem Stern die Fehlerposition  und den beschreibenden Fehlertext in einem ValidationSummary Control anzeigen. Wir wollen aber den Fehlertext direkt unterhalb der Eingabe sehen.

Folgender übler Trick ersetzt per VB.NET Code im Page Load Event des ASCX Files den Stern und damit wird der volle Text gezeigt:

   1:  RegularExpressionValidator1.Text = ""
   2:  RequiredFieldValidator1.Text = ""
   3:  DynamicValidator1.Text = ""

Dann füge ich noch eine JavaScript-Datei hinzu, die CSS-Attribute austauscht für has-errors wenn die Eingabe nicht valid ist. Dies ist in einem anderen Blog-Artikel beschrieben.

   1:  var proxied = window.ValidatorValidate;
   2:   
   3:  window.ValidatorValidate = function () {
   4:   
   5:      var result = proxied.apply(this, arguments);
   6:      onAfter(arguments);
   7:   
   8:  };
   9:   
  10:  var onAfter = function (arguments) {
  11:      var control = document.getElementById(arguments[0].controltovalidate);
  12:      var validators = control.Validators;
  13:      var isValid = true;
  14:   
  15:      for (var i = 0; i < validators.length; i++) {
  16:          if (!validators[i].isvalid) {
  17:              isValid = false;
  18:              break;
  19:          }
  20:      }
  21:   
  22:      if (isValid) {
  23:          $(control).closest('.form-group').removeClass('has-error');
  24:      } else {
  25:          $(control).closest('.form-group').addClass('has-error');
  26:      }
  27:  };

Bei einem Submit des Formulars sieht das im Internet Explorer  so aus:

image

Sehr cool, was die ASP.NET Steuerelemente zusammen mit Bootstrap hier so zaubern. Allerdings sind sie einen Hauch zu intelligent. Es werden nämlich auch korrekt die neuen HTML5 Input-Typen gerendert (Type=”number” und Type=”email”). Moderne Browser validieren dann auch gleich mal selber was im IE bei falscher E-Mail-Adresse so aussieht:

image

Zuerst erscheint ein browserspezifisches Popup und wenn man das Feld per TAB verlässt, wird es rot eingerahmt.

Da wir uns hier in den Händen des Browsers befinden, sieht das dann Chrome auch deutlich anders. Die fehlerhafte Eingabe wird gar nicht mehr hervorgehoben und die Nummer wird von einem Updown Icon begleitet.

image

In den Bootstrap-Foren wurde das Thema auch diskutiert und dann geschlossen. Theoretisch wäre auch das Required-Attribut ein HTML5-Standard, hier erzeugt ASP.NET aber stattdessen ein DIV. Optisch besser aber nicht konsistent. Bei meiner Recherche habe ich ein Plugin gefunden, habe aber ein wenig Probleme für das Framework, das für die Unzulänglichkeiten von CSS erfunden wurde, ein Plugin zu verwenden, das die Unzulänglichkeiten des Frameworks fixed. Im ersten Test gibt es einen Konflikt mit meiner eigenen JS Library. Selbst wenn ich das fixe, kann schon morgen mit dem nächsten Nuget-Update wieder alles hin sein.

Kommentare sind geschlossen