18

I am trying to update this tutorial on implementing Facebooks BigPipe to razor.

There is a html helper extension that adds a pagelet to a list, and then outputs a holding div to the response. The idea is that later on the content of this pagelet is rendered to a string, and then injected into this holding div via javascript.

public static void RegisterPagelet(this HtmlHelper helper, Pagelet pagelet) {
    var context = helper.ViewContext.HttpContext;
    List<Pagelet> pagelets = (List<Pagelet>)context.Items["Pagelets"];
    if (pagelets == null) {
        pagelets = new List<Pagelet>();
        context.Items["Pagelets"] = pagelets;
    }
    pagelets.Add(pagelet);
    context.Response.Write("<div id=\"" + pagelet.Container + "\"></div>");
}

In the example this function is called like this:

<div id="textHolder">
    <% Html.RegisterPagelet(myPagelet); %>
</div>

Which adds the pagelet to the lists, and outputs the holding div to the response stream.

So

<div id="textHolder">
    <div id="pageletPlaceHolder"></div>
</div>

However, when I try the same in Razor:

<div id="textHolder">
    @{ Html.RegisterPagelet(myPagelet);  }
</div>

The div placeholder appears at the top of the body, outside the textHolder div. Why is this? How can I get this to behave like the webforms view where the response is output inside the div?

Thanks.

collapsar
  • 15,446
  • 3
  • 28
  • 56
Terry
  • 832
  • 1
  • 11
  • 25

4 Answers4

41

A Razor view is rendered inside-out. Basically it writes content to temporary buffers which get written to the response stream when the top most layout page is reached. Thus, writing directly to the response stream from your HtmlHelper extension, will output it out of order.

The solution is to use:

helper.ViewContext.Writer.Write("<div id=\"" + pagelet.Container + "\"></div>");
James Hull
  • 3,611
  • 2
  • 25
  • 36
4

Change your method to be not void, but returning MvcHtmlString

public static MvcHtmlString OutputText(this HtmlHelper helper, string text) {
     return New MvcHtmlString(text);
}

Than use this as you used to

<div id="textHolder">
    @Html.OutputText("FooBar");
</div>

Idea is inspired by the fact that almost every input(and other) extension method in MVC returns MvcHtmlString

archil
  • 37,513
  • 7
  • 61
  • 81
  • I just updated the question to make it a little clearer of what I am trying to do. I didn't want to go this route because whilst it will work for this initial placeholder, later on in the request I process the content for this placeholder and want to output that to the response. I don't want to return a string because it's possible it could be a very large string and I was concerned about performance. – Terry Apr 10 '11 at 10:30
  • String is created anyways :). You could separate destination string creation logic and its usage logic. Usage logic may be two methods, first for writing generated string directly into response stream and another for creating MvcHtmlString for razor usage. Also, in fully other way, you could create Partial view for Pagelet. Which can be used by other razor views and also directly written in response – archil Apr 10 '11 at 10:39
  • After I call the RegisterPagelet helper, at the end of my layout view I call another function called ExecutePagelet. This does a parallel loop over the pagelets, rendering each one, then writing it to the response and flushing it. I don't think a string will help me there. Is the fact that response write behaves so different in razor a bug? – Terry Apr 10 '11 at 10:52
  • I don't know that is bug or not, but i'm pretty sure Partial View is the best choise in your case. Later you could just call PartialView() from controller to directly write into response stream. – archil Apr 10 '11 at 11:12
0

You should use the ViewBag and put the string in there, then output it.

In controller:

ViewBag.Foo = Bar;

In view:

<div>
@ViewBag.Foo
</div>
Bas
  • 25,270
  • 7
  • 45
  • 82
-4

I wouldn't bother doing it. The end result you want is t have FooBar written within your div, so why not just write it into your div? Why do you need to use Response.Write?

Matthew Abbott
  • 57,234
  • 9
  • 99
  • 126
  • I saw a tutorial on using Pagelets (Facebooks term for serving pages in chunks which are processed in parallel server side) and I am trying to update it to razor. The actual helper function is passed a pagelet, and adds this to a list stored in the http context. After the pagelet has been added to this list, I need to output a skeleton div, which I can inject the content into later. Hence I use response.write rather than return a string. – Terry Apr 10 '11 at 10:20
  • 1
    Because there are times when it is completely valid to be able to write into the output stream explicitly. – Craig Sep 23 '13 at 00:41