Razor - how to render content into a variable

2020-03-01 06:10发布

问题:

How to render a piece of Html into a variable in Razor? In Spark I used to write the following code:

<content var="t">
    <a class="tab" href="">Tab name</a>
</content>

<content var="tc">
    <div class="tabcontent">
        <p>Here goes tab content</p>
    </div>
</content>

!{tabs(t, tc)}

two variables get passed to a macro that does all the nice wrapping of the content into the tab sheet.

What's the best way to do the same in Razor?

Update: I think I got it..

In Razor, the @<text>...</text> construct can be user to produce lambda expressions, which can be reused later, which is an extended equivalent of assigning a piece of HTML to a variable. The above example can be implemented in the following way:

Func<int, object> t =
    @<text>
        <a class="tab" href="">Tab name</a>
    </text>;

Func<int, object> tc =
    @<text>
        <div class="tabcontent">
            <p>Here goes tab content</p>
        </div>
    </text>;


@tabs(t(0), tc(0))

I just can't figure out how to write parameterless lambdas (Func<object>). the int parameter in both lambdas above is a dummy. Razor seems to require one parameter (and already creates a variable "item" to denote it within the expression).

回答1:

Basically the OP already answered the problem in that you could do something like:

@{
   Func<dynamic, object> a = @<text>
       Some Text        
   </text>;
   @a(new object())
}    

If the text is for a single line only you could even use the "@:" operator, just remmebr to have a semicolon (or if it needs any closing brackets or parenthesis) on the next line, as in the following example:

@{
   Func<dynamic, object> a = @: Some Text
   ;    
   @a(new object())
}

However you can capture it directly as a string if you want

@{
    string a = ((Func<dynamic, object>)(@<text>
                    Some Text
                </text>))("").ToString();
    @a //Output directly as a string        
}

You could even encapsulate it in a function:

@functions{
     public string ToString(Func<dynamic, object> input)
     {
          return input("").ToString();
     }
}

@{
    string a = ToString(@<text>
                    Some Text
               </text>);
    @a //Output directly as a string        
 }


回答2:

Just in case anyone else finds this post (as I did), Andy's Update is almost there. In addition to the example given, all you have to do to access the 'int' in the example given is reference @item. In @<text></text> blocks, the variable item contains the model it was called on.

Here's an example of how it can be used:

@model PageData

@{
    Func<Customer, object> sayHi = 
        @<text>
             <li>Hello @(item.FirstName)!</li>
         </text>;
}

<ul>
    @foreach(var customer in Model.Customers)
    {
        sayHi(customer);
    }
</ul>

In most instances you should probably use a partial view instead of a function like this. But in the rare instances that a partial view is not possible (such as when using the RazorEngine library), this works.



回答3:

Perhaps you can use HtmlString? I don't think I like this much, but here's what I'd try as an exact translation of what you have...

@{
    var t = new HtmlString("<a class='tab' href=''>Tab name</a>");
    var tc = new HtmlString("<div class='tabcontent'><p>Here goes tab content</p></div>");
}
@tabs(t, tc)

So... I don't know what your Spark macro looks like, but it seems like an opportunity to use a helper in Razor. You might have something like:

@helper Tabs(string tabName, string tabContent)
{
    <!-- some wrapper code -->   
    <a class="tab" href="">@(tabName)</a>
    <!-- some more wrapper code -->
    <div class="tabcontent">
        <p>@(tabContent)</p>
    </div>
    <!-- still more wrapper code -->
}

Then you call that from in your page like so:

@Tabs("Tab Name", "Here goes tab content")


回答4:

If you have an IHtmlContentBuilder in your Razor view, you can use these extensions.

    public static IHtmlContentBuilder AppendRazor(this IHtmlContentBuilder htmlContentBuilder, Func<IHtmlContent, dynamic> lamda)
    {
        var html = lamda(null);
        return htmlContentBuilder.AppendHtml(html);
    }

    public static IHtmlContentBuilder AppendRazor<T>(this IHtmlContentBuilder htmlContentBuilder, Func<T, IHtmlContent> lamda, T model)
    {
        var html = lamda(model);
        return htmlContentBuilder.AppendHtml(html);
    }

Usage

@{
    HtmlContentBuilder contentBuilder = new HtmlContentBuilder();
    contentBuilder.AppendRazor(@<text><p>Hello @Model.Name<p></text>);
    contentBuilder.AppendRazor(@<text><p>Hello @item.CustomerName<p></text>, customer);
}
<div>
     @contentBuilder
</div>