Skip to content

Commit

Permalink
Add example of writer using Razor template
Browse files Browse the repository at this point in the history
  • Loading branch information
dimabarbul committed Jul 22, 2023
1 parent d124547 commit f85fe1f
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bogus;
using Microsoft.AspNetCore.Mvc;
using XReports.Converter;
using XReports.Demos.Models.Shared;
using XReports.Demos.XReports;
using XReports.Html;
using XReports.SchemaBuilders;
using XReports.Table;

namespace XReports.Demos.Controllers.CustomWriters;

public class RazorWriterController : Controller
{
private const int RecordsCount = 10;

private readonly IServiceProvider serviceProvider;

public RazorWriterController(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}

public async Task<IActionResult> Index()
{
IReportTable<ReportCell> reportTable = this.BuildReport();
IReportTable<HtmlReportCell> htmlReportTable = this.ConvertToHtml(reportTable);
string tableHtml = await this.WriteHtmlReportToStringAsync(htmlReportTable);

return this.View(new ReportViewModel() { ReportTableHtml = tableHtml });
}

private IReportTable<ReportCell> BuildReport()
{
ReportSchemaBuilder<Entity> reportBuilder = new();
reportBuilder.AddColumn("Name", e => e.Name);
reportBuilder.AddColumn("Score", e => e.Score);

IReportTable<ReportCell> reportTable = reportBuilder.BuildVerticalSchema().BuildReportTable(this.GetData());
return reportTable;
}

private IReportTable<HtmlReportCell> ConvertToHtml(IReportTable<ReportCell> reportTable)
{
ReportConverter<HtmlReportCell> htmlConverter = new(Array.Empty<IPropertyHandler<HtmlReportCell>>());

return htmlConverter.Convert(reportTable);
}

private Task<string> WriteHtmlReportToStringAsync(IReportTable<HtmlReportCell> htmlReportTable)
{
return new RazorWriter(this.serviceProvider)
.WriteAsync(htmlReportTable, "~/Views/RazorWriter/Report.cshtml");
}

private IEnumerable<Entity> GetData()
{
return new Faker<Entity>()
.RuleFor(e => e.Name, f => f.Name.FullName())
.RuleFor(e => e.Score, f => f.Random.Decimal(0, 100))
.Generate(RecordsCount);
}

private class Entity
{
public string Name { get; set; }

public decimal Score { get; set; }
}
}
73 changes: 73 additions & 0 deletions demos/XReports.Demos/Views/RazorWriter/Index.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
@model XReports.Demos.Models.Shared.ReportViewModel

@{
ViewBag.Title = "Razor Writer";
Layout = "_DemoPageLayout";
}

@section Description
{
<p>This example writer shows how to use Razor syntax for rendering report.</p>
}

@section ReportTable
{
@Html.Raw(Model.ReportTableHtml)
}

@section Code
{
public class RazorWriter
{
private readonly ICompositeViewEngine viewEngine;
private readonly ITempDataProvider tempDataProvider;
private readonly IServiceProvider serviceProvider;

public RazorWriter(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
this.viewEngine = this.serviceProvider.GetRequiredService&lt;ICompositeViewEngine&gt;();
this.tempDataProvider = this.serviceProvider.GetRequiredService&lt;ITempDataProvider&gt;();
}

public Task&lt;string&gt; WriteAsync(IReportTable&lt;HtmlReportCell&gt; report, string viewName)
{
return this.RenderSimpleViewToStringAsync(viewName, report);
}

private async Task&lt;string&gt; RenderSimpleViewToStringAsync&lt;TModel&gt;(string viewName, TModel model)
{
ViewEngineResult viewResult = this.viewEngine.GetView(
executingFilePath: null,
viewPath: viewName,
isMainPage: true);
if (!viewResult.Success)
{
throw new ArgumentException($"Could not find view: {viewName}", nameof(viewName));
}

await using StringWriter writer = new();
ViewDataDictionary&lt;TModel&gt; viewDictionary = new(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model,
};
ViewContext viewContext = new(
new ActionContext(
new DefaultHttpContext()
{
RequestServices = this.serviceProvider,
},
new RouteData(),
new ActionDescriptor()),
viewResult.View,
viewDictionary,
new TempDataDictionary(new DefaultHttpContext(), this.tempDataProvider),
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);

return writer.ToString();
}
}
}
68 changes: 68 additions & 0 deletions demos/XReports.Demos/Views/RazorWriter/Report.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
@using XReports.Html
@using Microsoft.AspNetCore.Html
@using System.Text
@using System.Net

@model XReports.Table.IReportTable<XReports.Html.HtmlReportCell>

@{
Layout = null;
}

@functions
{
public static HtmlString WriteAttributes(HtmlReportCell cell)
{
StringBuilder sb = new();
if (cell.CssClasses.Count > 0)
{
sb
.Append(" class=\"")
.AppendJoin(' ', cell.CssClasses)
.Append('"');
}

if (cell.Styles.Count > 0)
{
sb
.Append(" style=\"")
.AppendJoin(';', cell.Styles.Select(s => $"{s.Key}:{s.Value}"))
.Append('"');
}

if (cell.Attributes.Count > 0)
{
sb
.AppendJoin(
' ',
cell.Attributes.Select(a => $"{a.Key}=\"{WebUtility.HtmlEncode(a.Value)}\""));
}

return new HtmlString(sb.ToString());
}
}

<table class="table table-striped table-sm">
<thead>
@foreach (IEnumerable<HtmlReportCell> row in Model.HeaderRows)
{
<tr>
@foreach (HtmlReportCell cell in row)
{
<th @WriteAttributes(cell)>@(cell.IsHtml ? Html.Raw(cell.GetValue<string>()) : cell.GetValue<string>())</th>
}
</tr>
}
</thead>
<tbody>
@foreach (IEnumerable<HtmlReportCell> row in Model.Rows)
{
<tr>
@foreach (HtmlReportCell cell in row)
{
<td @WriteAttributes(cell)>@(cell.IsHtml ? Html.Raw(cell.GetValue<string>()) : cell.GetValue<string>())</td>
}
</tr>
}
</tbody>
</table>
6 changes: 6 additions & 0 deletions demos/XReports.Demos/Views/Shared/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
<a class="dropdown-item" asp-controller="NamedConverters" asp-action="Index">Named Converters</a>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown">Custom Writers</a>
<div class="dropdown-menu">
<a class="dropdown-item" asp-controller="RazorWriter" asp-action="Index">Razor Writer</a>
</div>
</li>
</ul>
</div>
</div>
Expand Down
70 changes: 70 additions & 0 deletions demos/XReports.Demos/XReports/RazorWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using XReports.Html;
using XReports.Table;

namespace XReports.Demos.XReports;

public class RazorWriter
{
private readonly ICompositeViewEngine viewEngine;
private readonly ITempDataProvider tempDataProvider;
private readonly IServiceProvider serviceProvider;

public RazorWriter(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
this.viewEngine = this.serviceProvider.GetRequiredService<ICompositeViewEngine>();
this.tempDataProvider = this.serviceProvider.GetRequiredService<ITempDataProvider>();
}

public Task<string> WriteAsync(IReportTable<HtmlReportCell> report, string viewName)
{
return this.RenderSimpleViewToStringAsync(viewName, report);
}

private async Task<string> RenderSimpleViewToStringAsync<TModel>(string viewName, TModel model)
{
ViewEngineResult viewResult = this.viewEngine.GetView(
executingFilePath: null,
viewPath: viewName,
isMainPage: true);
if (!viewResult.Success)
{
throw new ArgumentException($"Could not find view: {viewName}", nameof(viewName));
}

await using StringWriter writer = new();
ViewDataDictionary<TModel> viewDictionary = new(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model,
};
ViewContext viewContext = new(
new ActionContext(
new DefaultHttpContext()
{
RequestServices = this.serviceProvider,
},
new RouteData(),
new ActionDescriptor()),
viewResult.View,
viewDictionary,
new TempDataDictionary(new DefaultHttpContext(), this.tempDataProvider),
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);

return writer.ToString();
}
}

0 comments on commit f85fe1f

Please sign in to comment.