Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for implementing @section, @RenderSection #73

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
180 changes: 180 additions & 0 deletions ExampleAppCore/LayoutAndSections.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
using Microsoft.AspNetCore.Razor.Language.Extensions;
using RazorEngineCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;


namespace ExampleApp
{

public static class LayoutAndSections
{
/// <summary>
/// Provides an example of using @section and @RenderSection, based on the layout implementation
/// at https://github.com/adoconnection/RazorEngineCore/wiki/@Include-and-@Layout
/// </summary>
public static void Test()
{

string template = @"
@{
Layout = ""MyLayout"";
}

<h1>@Model.Title</h1>

@Include(""outer"", Model)

@section mysection
{
This is in a section
}

";

IDictionary<string, string> parts = new Dictionary<string, string>()
{
{"MyLayout", @"
LAYOUT HEADER
@RenderBody()
Before section
@RenderSection(""mysection"")
After section
LAYOUT FOOTER
"},
{"outer", "This is Outer include, <@Model.Title>, @Include(\"inner\")"},
{"inner", "This is Inner include"}
};

var razorEngine = new RazorEngine();

MyCompiledTemplate compiledTemplate = razorEngine.Compile(template, parts, (builder) =>
{
builder.Options.ProjectEngineBuilderAction = (x) => SectionDirective.Register(x);
});

string result = compiledTemplate.Run(new { Title = "Hello" });

Console.WriteLine(result);
Console.ReadKey();
}
}


public class MyTemplateBase : RazorEngineTemplateBase
{
public Func<string, object, string> IncludeCallback { get; set; }
public Func<string> RenderBodyCallback { get; set; }
public string Layout { get; set; }

public string Include(string key, object model = null)
{
return this.IncludeCallback(key, model);
}

public string RenderBody()
{
return this.RenderBodyCallback();
}

public Dictionary<string, string> Sections = new Dictionary<string, string>();

public void DefineSection(string name, Action a)
{
Debug.WriteLine("DefineSection " + name);
BeginCapture();
a();
string content = EndCapture();
Sections.Add(name, content);
Debug.WriteLine("Section content:");
Debug.WriteLine(content);
}

public string RenderSection(string name, bool required = false)
{
if (Sections.TryGetValue(name, out var content))
return content;
else if (required)
throw new Exception("Required section not found: " + name);
return string.Empty;
}
}


public class MyCompiledTemplate
{
private readonly IRazorEngineCompiledTemplate<MyTemplateBase> compiledTemplate;
private readonly Dictionary<string, IRazorEngineCompiledTemplate<MyTemplateBase>> compiledParts;

public MyCompiledTemplate(IRazorEngineCompiledTemplate<MyTemplateBase> compiledTemplate, Dictionary<string, IRazorEngineCompiledTemplate<MyTemplateBase>> compiledParts)
{
this.compiledTemplate = compiledTemplate;
this.compiledParts = compiledParts;
}

public string Run(object model)
{
return this.Run(this.compiledTemplate, model);
}

public string Run(IRazorEngineCompiledTemplate<MyTemplateBase> template, object model)
{
MyTemplateBase templateReference = null;

string result = template.Run(instance =>
{
if (!(model is AnonymousTypeWrapper))
{
model = new AnonymousTypeWrapper(model);
}

instance.Model = model;
instance.IncludeCallback = (key, includeModel) => this.Run(this.compiledParts[key], includeModel);

templateReference = instance;
});

if (templateReference.Layout == null)
{
return result;
}

return this.compiledParts[templateReference.Layout].Run(instance =>
{
if (!(model is AnonymousTypeWrapper))
{
model = new AnonymousTypeWrapper(model);
}
instance.Sections = templateReference.Sections;
instance.Model = model;
instance.IncludeCallback = (key, includeModel) => this.Run(this.compiledParts[key], includeModel);
instance.RenderBodyCallback = () => result;
});
}

public void Save()
{
/*
TODO

this.compiledTemplate.SaveToFile();
this.compiledTemplate.SaveToStream();

foreach (var compiledPart in this.compiledParts)
{
compiledPart.Value.SaveToFile();
compiledPart.Value.SaveToStream();
}
*/
}

public void Load()
{
// TODO
}
}

}
23 changes: 23 additions & 0 deletions ExampleAppCore/RazorEngineCoreExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using RazorEngineCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ExampleApp
{
public static class RazorEngineCoreExtensions
{
public static MyCompiledTemplate Compile(this RazorEngine razorEngine, string template, IDictionary<string, string> parts,
Action<IRazorEngineCompilationOptionsBuilder> builderAction = null)
{
return new MyCompiledTemplate(
razorEngine.Compile<MyTemplateBase>(template, builderAction),
parts.ToDictionary(
k => k.Key,
v => razorEngine.Compile<MyTemplateBase>(v.Value)));
}

}

}
2 changes: 1 addition & 1 deletion RazorEngineCore.Tests/RazorEngineCore.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net472;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>net472;net6.0</TargetFrameworks>

<IsPackable>false</IsPackable>
</PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions RazorEngineCore/RazorEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ protected virtual MemoryStream CreateAndCompileToStream(string templateSource, R
(builder) =>
{
builder.SetNamespace(options.TemplateNamespace);
options.ProjectEngineBuilderAction?.Invoke(builder);
});

string fileName = string.IsNullOrWhiteSpace(options.TemplateFilename) ? Path.GetRandomFileName() : options.TemplateFilename;
Expand Down
3 changes: 3 additions & 0 deletions RazorEngineCore/RazorEngineCompilationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis;
using System;
using Microsoft.AspNetCore.Razor.Language;

namespace RazorEngineCore
{
Expand All @@ -22,6 +23,8 @@ public class RazorEngineCompilationOptions
"System.Collections.Generic"
};

public Action<RazorProjectEngineBuilder> ProjectEngineBuilderAction { get; set; }

public RazorEngineCompilationOptions()
{
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
Expand Down
6 changes: 3 additions & 3 deletions RazorEngineCore/RazorEngineCore.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net5.0;netstandard2.0</TargetFrameworks>
<TargetFrameworks>net7.0;netstandard2.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2022.8.1</Version>
<Authors>Alexander Selishchev, Simon Mourier, William David Cossey, Benjamin Smith, Dag H. Baardsen, krmr, jddj007-hydra</Authors>
Expand All @@ -20,7 +20,7 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="6.0.28" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
</ItemGroup>
</Project>
22 changes: 20 additions & 2 deletions RazorEngineCore/RazorEngineTemplateBase.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
using System.Text;
using System;
using System.Text;
using System.Threading.Tasks;

namespace RazorEngineCore
{
public abstract class RazorEngineTemplateBase : IRazorEngineTemplate
{
private readonly StringBuilder stringBuilder = new StringBuilder();
private readonly StringBuilder _stringBuilder = new StringBuilder();
private StringBuilder _captureStringBuilder;
private StringBuilder stringBuilder => _captureStringBuilder ?? _stringBuilder;

protected void BeginCapture()
{
if (_captureStringBuilder != null)
throw new InvalidOperationException("Already capturing");
_captureStringBuilder = new StringBuilder();
}
protected string EndCapture()
{
if (_captureStringBuilder == null)
throw new InvalidOperationException("Not capturing");
string result = _captureStringBuilder.ToString();
_captureStringBuilder = null;
return result;
}

private string attributeSuffix = null;

Expand Down