4

I have this helper:

public class PanelSection : IDisposable
{
    protected HtmlHelper _helper;
    private string f;
    public PanelSection(HtmlHelper helper, string title, string subTitle, bool footer)
    {
        _helper = helper;
        f = footer ? "" : "</div>";        //If footer is true, we end body ourselves, if footer is false, we end both - body and panel automatically

        _helper.ViewContext.Writer.Write(
            "<div class='panel panel-default'><div class='panel-heading'><h2>" + title + "</h2></div><div class='panel-body'>"
        );
        if (!string.IsNullOrEmpty(subTitle))
            _helper.ViewContext.Writer.Write(
            "<h4>" + subTitle + "</h4><hr/>"
        );
    }

    public void Dispose()
    {
        _helper.ViewContext.Writer.Write("</div>" + f);
    }
}

If footer is set to True, that means I will end panel-body myself, so on dispose it writes one less div. This should let me have panel-footer when I need it, or have table outside of body. But on when I do this I get

Parser Error Message: Encountered end tag "div" with no matching start tag.

My razor code looks like this:

    using (Html.BeginPanel(@Resources.Contract, @Resources.CreateNew, true))
{ //starts panel, starts panel body
    <b>Body content</b>
    </div> //end of body
    <div class="panel-footer">
        <a href="@Url.Action("Index")" class="btn btn-default" role="button">@Resources.Back</a>
    </div>
 } //end of panel

Obvious solution would be to simply not open panel body on that helper, and for panel body use different helper. But I'm still interested why it gives me that error. It should generate good html without any errors, but it looks like it process everything before changing helper into html, sees an extra div and throws parse error. Why is that? Is there any way to make this work?

hutchonoid
  • 32,982
  • 15
  • 99
  • 104
Erndob
  • 2,512
  • 20
  • 32
  • 3
    Why add the closing div in the dispose method, move that into your normal code and use dispose for what it's meant for – 3dd Aug 11 '15 at 09:56
  • @3dd Isn't thats how Html.BeginForm works? If you know another solution on how to have custom tags on "}" please share. – Erndob Aug 11 '15 at 09:59
  • @Erndob Html.BeginForm is an extension method and will therefore not implement IDisposable. I can't find the source code for it in referencesource.microsoft.com, but the mono implementation is https://github.com/mono/aspnetwebstack/blob/master/src/System.Web.Mvc/Html/FormExtensions.cs – Andy Nichols Aug 11 '15 at 10:15
  • @AndyNichols interesting. Because in other stackoverflow questions people said to use dispose to achieve this result. Now you telling it's bad. http://stackoverflow.com/questions/7196276/creating-mvc3-razor-helper-like-helper-beginform http://stackoverflow.com/questions/8292497/how-can-i-create-a-html-helper-like-html-beginform – Erndob Aug 11 '15 at 10:23
  • 1
    Well, [Html.BeginForm](https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/Html/FormExtensions.cs) returns an [MvcForm](https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/Html/MvcForm.cs) that implements `IDisposable`, where the closing `` tag is added. Regarding the OP problem, I don't know how to solve it, maybe there is a way for you to inspect the html for panel-footer elements inside the Dispose method? (Although the intention seems confusing to me anyway, having that `` hanging there that might be required or not...) – Daniel J.G. Aug 11 '15 at 10:27
  • @DanielJ.G. Agreed. I remade it into two different helpers for panel and panel body. But I still dont understand why it doesn't work in this way. Helper gives htmlstring, and with that string inserted into DOM it shouldn't throw any errors, but it does. In IDE itself I get just warning about closing div without opening. – Erndob Aug 11 '15 at 10:39
  • I would say you get the error because the cshtml has to be parsed and a C# class has to be generated and compiled before the view is rendered for the first time. For example if you introduce a code error like passing an integer as the title, you will still get the parse exception about the closing div, since the page has to be parsed first. – Daniel J.G. Aug 11 '15 at 11:39

1 Answers1

2

Unbalanced razor tags can be output using the @: syntax.

Providing everything else compiles you can output the razor as follows:

using (Html.BeginPanel(@Resources.Contract, @Resources.CreateNew, true))
{ 
   //starts panel, starts panel body
   <b>Body content</b>
   @:</div> //end of body
   <div class="panel-footer">
      <a href="@Url.Action("Index")" class="btn btn-default" role="button">@Resources.Back</a>
  </div>
} //end of panel

Note the @:</div>.

hutchonoid
  • 32,982
  • 15
  • 99
  • 104