Skip to content

Commit 78cfcb9

Browse files
committed
Added support for Nested Razor Layouts
Updated the RazorPageResolver ResolveAndExecuteRazorPage function to recursively render layouts until it finds one with no RazorPage Layout value assigned.
1 parent 834df66 commit 78cfcb9

File tree

2 files changed

+45
-24
lines changed

2 files changed

+45
-24
lines changed

src/ServiceStack.Razor/Managers/RazorPageResolver.cs

+31-19
Original file line numberDiff line numberDiff line change
@@ -135,42 +135,54 @@ public IRazorView ResolveAndExecuteRazorPage(IHttpRequest httpReq, IHttpResponse
135135
httpRes.StatusCode = (int)HttpStatusCode.NotFound;
136136
return null;
137137
}
138-
138+
139139
using (var writer = new StreamWriter(httpRes.OutputStream, UTF8EncodingWithoutBom))
140140
{
141141
var page = CreateRazorPageInstance(httpReq, httpRes, model, razorPage);
142142

143143
var includeLayout = !(httpReq.GetParam(QueryStringFormatKey) ?? "").Contains(NoTemplateFormatValue);
144144
if (includeLayout)
145145
{
146-
using (var ms = new MemoryStream())
147-
using (var childWriter = new StreamWriter(ms, UTF8EncodingWithoutBom))
146+
var result = ExecuteRazorPageWithLayout(httpReq, httpRes, model, page, () =>
148147
{
149-
//child page needs to execute before master template to populate ViewBags, sections, etc
150-
page.WriteTo(childWriter);
148+
return httpReq.GetItem(LayoutKey) as string
149+
?? page.Layout
150+
?? DefaultLayoutName;
151+
});
152+
153+
writer.Write(result.Item2);
154+
return result.Item1;
155+
}
151156

152-
var layout = httpReq.GetItem(LayoutKey) as string
153-
?? page.Layout
154-
?? DefaultLayoutName;
157+
page.WriteTo(writer);
158+
return page;
159+
}
160+
}
161+
162+
private Tuple<IRazorView, string> ExecuteRazorPageWithLayout(IHttpRequest httpReq, IHttpResponse httpRes, object model, IRazorView page, Func<string> layout)
163+
{
164+
using (var ms = new MemoryStream())
165+
{
166+
using (var childWriter = new StreamWriter(ms, UTF8EncodingWithoutBom))
167+
{
168+
//child page needs to execute before master template to populate ViewBags, sections, etc
169+
page.WriteTo(childWriter);
170+
var childBody = ms.ToArray().FromUtf8Bytes();
155171

156-
var childBody = ms.ToArray().FromUtf8Bytes();
157-
var layoutPage = this.viewManager.GetPageByName(layout, httpReq, model);
172+
var layoutName = layout();
173+
if (!String.IsNullOrEmpty(layoutName))
174+
{
175+
var layoutPage = this.viewManager.GetPageByName(layoutName, httpReq, model);
158176
if (layoutPage != null)
159177
{
160178
var layoutView = CreateRazorPageInstance(httpReq, httpRes, model, layoutPage);
161-
162179
layoutView.SetChildPage(page, childBody);
163-
layoutView.WriteTo(writer);
164-
return layoutView;
180+
return ExecuteRazorPageWithLayout(httpReq, httpRes, model, layoutView, () => layoutView.Layout);
165181
}
166-
167-
writer.Write(childBody);
168-
return page;
169182
}
170-
}
171183

172-
page.WriteTo(writer);
173-
return page;
184+
return Tuple.Create(page, childBody);
185+
}
174186
}
175187
}
176188

tests/ServiceStack.ServiceHost.Tests/Formats_Razor/IntroductionLayoutRazorTests.cs

+14-5
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,29 @@ comes from ^^^websiteTemplate.</p>
132132
}
133133

134134
[Test]
135-
public void Simple_Nested_Layout_Example()
135+
public void Nested_Layout_Example_With_Sections()
136136
{
137137
var websiteTemplate2 =
138-
@"<div id=""body2"">@RenderBody()</div>".Replace("\r\n", "").Replace("\t", "");
138+
@"<div id=""body2"">@RenderBody()</div>
139+
@RenderSection(""Section1"")"
140+
.Replace("\r\n", "").Replace("\t", "");
139141

140142
var websiteTemplate1 =
141143
@"@{Layout=""websiteTemplate2"";}
142-
<div id=""body1"">@RenderBody()</div>".Replace("\r\n", "").Replace("\t", "");
144+
<div id=""body1"">@RenderBody()</div>
145+
@RenderSection(""Section1"")
146+
@section Section1 {<div>Menu2</div>}"
147+
.Replace("\r\n", "").Replace("\t", "");
143148

144149
var pageTemplate =
145150
@"@{Layout=""websiteTemplate1"";}
146-
<h1>@DateTime.Now.Year</h1>".Replace("\r\n", "").Replace("\t", "");
151+
<h1>@DateTime.Now.Year</h1>
152+
@section Section1 {<div>Menu1</div>}"
153+
.Replace("\r\n", "").Replace("\t", "");
147154

148-
var expectedHtml = (@"<div id=""body2""><div id=""body1""><h1>" + DateTime.Now.Year + "</h1></div></div>")
155+
var expectedHtml = (@"<div id=""body2""><div id=""body1"">
156+
<h1>" + DateTime.Now.Year + @"</h1></div>
157+
<div>Menu1</div></div><div>Menu2</div>")
149158
.Replace("\r\n", "").Replace("\t", "");
150159

151160
RazorFormat.AddFileAndPage("/views/websiteTemplate2.cshtml", websiteTemplate2);

0 commit comments

Comments
 (0)