Willkommen bei ASP.NET Zone. Anmelden | Registrieren | Hilfe

MVC Html Helper á la Fluent Interface

MVC Html HelpersHelferlein können das Leben ganz schön vereinfachen.

Das zeigt sich an diversen Beispielen. Wer hat sich noch nicht eine LabeledTextbox oder ein Control in der Art in klassischen Webforms fabriziert, um sich damit eine Menge Tipparbeit zu sparen?

Natürlich geht das auch mit MVC.

Ist es nicht cool, wie Telerik und Co. Das machen?

<%= Html.MeineSuperAnwendung().MeinMegaControl().DasAllesKann %>

Korrekt! Die eierlegende Wollmilchsau unter den eigenen Controls kann das!

Wie das geht?

Ganz einfach: Man nehme eine statische Klasse, die das Control instanziert und zurückgibt.

Die fluent Schreibweise ist nichts anderes als eine Methode, die das eigene Objekt zurückgibt.

Gegeben eine Klasse Button mit folgender Methode:

public Button IchBinEinFluentDing()
{
    return this;
}
var button = new Button().IchBinEinFluentDing().IchBinEinFluentDing()


Klingt nicht nur simple, ist es auch. Gut, nicht ganz, ein Zwischenschritt wird noch benötigt.

Die „Container“ Klasse, die die einzelnen Controls zurückgeben kann. Somit wird nicht auf ein Controltyp beschränkt.

public class MyHtmlHelpers
{
    private HtmlHelper _helper;
    public MyHtmlHelpers(HtmlHelper helper)
    {
        this._helper = helper;
    }
    public CoolButton CoolButton(string text)
    {
        return new CoolButton(this._helper, text);
    }
    ...
}


Wie bereits verraten, die statische Klasse (Extension), die die Helpers zurück gibt:

public static class MyProductExtension
{
    public static MyHtmlHelpers MyProduct(this HtmlHelper helper)
    {
        return new MyHtmlHelpers (helper);
    }
}


Lange Rede – kurzer Sinn:

Html.MyProduct().CoolButton()

Gut, die Schreibweise ist klar, aber das Ganze macht noch keinen Sinn. Der Button muss her!

public class CoolButton
{
    private IDictionary<string, object> _attributes;
    private string _text;
    private HtmlHelper _helper;
    public CoolButton(HtmlHelper helper, string text)
    {
        this._text = text;
        this._helper = helper;
        this._attributes = new Dictionary<string, object>();
    }


Ein Text für den Button, das html helper objekt - das später benötigt wird und das Kernstück – die Attribute. Das ganze Ding soll ja letztendlich flexibel sein.

public CoolButton SetAttribute(string key, object value)
{
    if (!this._attributes.ContainsKey(key))
    {
        this._attributes.Add(key, value.ToString());
    }
    return this;
}


Natürlich soll der Button auch navigieren können – Angaben á la MVC + Id:

public CoolButton RouteTo(string controller, string action, object itemId)
{
    var urlHelper = new UrlHelper(this._helper.ViewContext.RequestContext);
    string url;
    if (itemId == null)
    {
        url = urlHelper.Action(action, controller);
    }
    else
    {
        url = urlHelper.Action(action, controller, new { id = itemId });
    }
            
    this._attributes.Add("onclick", string.Format("window.location='{0}';return false;", url));
    return this;
}


Natürlich macht es nicht immer Sinn ein onclick event zu haben, aber es soll ja letztendlich der Sinn der Attribute erklärt werden.

Es können weitere solcher Methoden folgen, bspw. Um die css Klasse oder ein client event zu setzen.

Last but not least – Rendern.
Aufgrund des Attribute Dictionarys recht einfach: Stück für Stück zusammensetzen.

public string Render()
{
    var attributeBuilder = new StringBuilder();
    foreach (KeyValuePair<string, object> attribute in _attributes)
    {
        attributeBuilder.AppendFormat(" {0}=\"{1}\"", attribute.Key, attribute.Value);
    }
    return string.Format("<button {0}>{1}</button>", attributeBuilder.ToString(), this._text);
}


Für jedes KeyValuePair des Dictionarys wird folgendes Konstrukt erstellt: Key=“Value“, onclick=“TuWas();“

Das Ganze sieht dann in etwa so aus:

<%= Html.MyProduct().CoolButton("Edit")
                        .SetAttribute("class", "editButton")
                        .RouteTo("User", "Edit", Model.User.Id)
                        .Render() %>


Man kann darüber streiten, ob eine Fluent Schreibweise wie eben beschrieben Sinn macht, ob sie sauber ist, oder ob sie nur ein Workaround ist, bis etwas Neues Erfunden wird?

In ASP.NET Webforms Zeiten hab ich mir teilweise meine Controls per Render Überschreibung mit dem Writer selbst erzeugt (Fragt jetzt nicht warum – ich wollte einfach die Kontrolle über das Ding haben ;-) ). Das ist ja ebenfalls suboptimal und im Gegensatz dazu sind HTML Helpers ein Genuss :-)

Anbei gibt’s wie immer ein etwas ausführlicheres Beispiel.

Veröffentlicht Dienstag, 8. Juni 2010 23:45 von Roberto
Abgelegt unter: ,

Kommentare

# re: MVC Html Helper á la Fluent Interface

Hi Roberto,

schöner Artikel - mir gefällt das so sehr gut.

Eine einzige kleine Anmerkung - Du schreibst: "Die fluent Schreibweise ist nichts anderes als eine Methode, die die eigene Klasse zurückgibt."

Es wird aber nicht die eigene Klasse, sondern das eigene Objekt zurückgegeben - ein kleiner, aber feiner Unterschied ;-).

Viele Grüße,

Golo

Mittwoch, 9. Juni 2010 09:10 by Golo Roden

# re: MVC Html Helper á la Fluent Interface

Hallo Golo!

Erstmal Vielen Dank für deinen Kommentar.

Da hast Du natürlich Recht - hab es gleich ausgebessert - Danke für den Hinweis.

War wohl ein Denkfehler um 12 Uhr Nachts ;-)

Grüße,

Roberto

Mittwoch, 9. Juni 2010 09:25 by Roberto
Anonyme Kommentare sind nicht zugelassen