2012-04-04 5 views
8

Ich versuche, eine BeginForm-Stil HTML-Helfer, die IDisposable verwendet, um anderen Code zu wickeln. Ich möchte, dass der Helfer den eingepackten Code nur dann rendert, wenn eine bestimmte Bedingung erfüllt ist (z. B. der Benutzer befindet sich in einer bestimmten Rolle).Capture eingewickelten Inhalt in BeginForm Stil Einweg-HTML-Helfer

Ich dachte, dass ich einfach den context.Writer in der Begin-Methode wechseln und in der Dispose-Methode zurückschalten könnte. Der folgende Code wird kompiliert und ausgeführt, aber der umbrochene Inhalt wird in allen Fällen gerendert. Wenn ich es durchtrete, wird der umgebrochene Inhalt nicht in den neuen StringWriter geschrieben und unterliegt daher nicht meiner Kontrolle.

public static IDisposable BeginSecure(this HtmlHelper html, ...) 
    { 
     return new SecureSection(html.ViewContext, ...); 
    } 

    private class SecureSection : IDisposable 
    { 
     private readonly ViewContext _context; 
     private readonly TextWriter _writer; 

     public SecureSection(ViewContext context, ...) 
     { 
      _context = context; 
      _writer = context.Writer; 
      context.Writer = new StringWriter(); 
     } 

     public void Dispose() 
     { 
      if (condition here) 
      { 
       _writer.Write(_context.Writer); 
      } 

      _context.Writer = _writer; 
     } 
    } 

Was versuche ich mit HTML-Helfern zu tun?

Ich weiß, dass deklarative HTML-Helfer in Rasierer würde wahrscheinlich funktionieren, aber würde Standard-HTML-Helfer-Ansatz wenn möglich, angesichts der app_code Einschränkung von Rasiermesserhelfer in MVC3 bevorzugen.

Antwort

6

Sie können den Body-Inhalt einer Hilfsmethode, die IDisposable zurückgibt, nicht bedingt rendern. Es wird immer rendern. Sie können diese Art von Helfern verwenden, wenn Sie den Körper des using-Blocks mit einem benutzerdefinierten Markup wie dem BeginForm-Helper mit dem <form>-Element umbrechen möchten.

public static class HtmlExtensions 
{ 
    public static HelperResult Secure(this HtmlHelper html, Func<object, HelperResult> template) 
    { 
     return new HelperResult(writer => 
     { 
      if (condition here) 
      { 
       template(null).WriteTo(writer); 
      } 
     }); 
    } 
} 

und dann:

könnten Sie ein templated Razor delegate stattdessen

@Html.Secure(
    @<div> 
     You will see this text only if some condition is met 
    </div> 
) 
9

Eigentlich Sie können verstecken Inhalt mit einer Struktur bedingt Begin-like. Es geht nur mit dem internen String ein bisschen durcheinander:

public class Restricted: IDisposable 
{ 
    public bool Allow { get; set; } 

    private StringBuilder _stringBuilderBackup; 
    private StringBuilder _stringBuilder; 
    private readonly HtmlHelper _htmlHelper; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="Restricted"/> class. 
    /// </summary> 
    public Restricted(HtmlHelper htmlHelper, bool allow) 
    { 
     Allow = allow; 
     _htmlHelper = htmlHelper; 
     if(!allow) BackupCurrentContent(); 
    } 

    private void BackupCurrentContent() 
    { 
     // make backup of current buffered content 
     _stringBuilder = ((StringWriter)_htmlHelper.ViewContext.Writer).GetStringBuilder(); 
     _stringBuilderBackup = new StringBuilder().Append(_stringBuilder); 
    } 

    private void DenyContent() 
    { 
     // restore buffered content backup (destroying any buffered content since Restricted object initialization) 
     _stringBuilder.Length = 0; 
     _stringBuilder.Append(_stringBuilderBackup); 
    } 

    /// <summary> 
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 
    /// </summary> 
    public void Dispose() 
    { 
     if(!Allow) 
      DenyContent(); 
    } 
} 

Dann brauchen Sie nur eine Htmlhelper zu machen, die eine Instanz der obigen Aufgabe macht

public static class RestrictedHelper 
{ 
    public static Restricted RestrictedContent(this HtmlHelper htmlHelper, bool allow) 
    { 
     return new Restricted(htmlHelper, allow); 
    } 
} 

Verwendung ist wie folgt:

@using (var restricted = Html.Restricted(true)) 
{ 
    <p>This will show up</p> 
} 
@using (var restricted = Html.Restricted(false)) 
{ 
    <p>This won't</p> 
} 

Vorteile:

  • Schreiben Sie benutzerdefinierte Logik, um Ihren Inhalt anzuzeigen/auszublenden, und übergeben Sie ihn an den eingeschränkten Konstruktor.
  • Öffentliche Eigenschaften in Ihrem eingeschränkten Objekt sind im Codeblock Ihrer Ansicht verfügbar, sodass Sie berechnete Werte dort wiederverwenden können.

Getestet mit ASP.Net MVC 4

+0

Sie sollten den "allowcontent" Code Kommentar-out, nur tun es nichts. Ansonsten bekomme ich verschachtelten Inhalt. Backup ist auch nur erforderlich, wenn Erlaubt ist "falsch" – Alex

+0

Danke @jitbit, ich habe meinen Beitrag aktualisiert. Nur ein nachträglicher Einfall: diese Secured-Klasse fügt sehr wenig Wert gegenüber einfachen integrierten if-Anweisungen hinzu ... Ich würde diesen Weg nur nehmen, wenn Sie den HTML-Code manipulieren müssen und alle anderen Optionen ausprobiert haben. Ich habe viel mit Razor gearbeitet, seit ich diesen Beitrag geschrieben habe, und finde heraus, dass die "Using" -Syntax in Razor Sprachmissbrauch und nicht Syntax-Zucker anzeigt. – Moeri