diff --git a/samples/MvcSample.Web/Views/Shared/HelloWorldPartial.cshtml b/samples/MvcSample.Web/Views/Shared/HelloWorldPartial.cshtml new file mode 100644 index 0000000000..e58af0cf87 --- /dev/null +++ b/samples/MvcSample.Web/Views/Shared/HelloWorldPartial.cshtml @@ -0,0 +1,4 @@ +@using MvcSample.Web.Models +@model User + +Hello @Model.Name from Partial \ No newline at end of file diff --git a/samples/MvcSample.Web/Views/Shared/MyView.cshtml b/samples/MvcSample.Web/Views/Shared/MyView.cshtml index 3a37319daf..0fa8b00032 100644 --- a/samples/MvcSample.Web/Views/Shared/MyView.cshtml +++ b/samples/MvcSample.Web/Views/Shared/MyView.cshtml @@ -11,6 +11,12 @@ { return "Hello World"; } + + public Task RenderHelloWorldPartial(User model) + { + // Imagine this method was a super complex method that was used as a helper method. + return Html.RenderPartialAsync("HelloWorldPartial", model); + } }
@@ -21,6 +27,12 @@

Hello @Model.Name! Happy @Model.Age birthday.

This value was retrieved asynchronously: @(await AsyncValueRetrieval())

+

Partial Async: @await Html.PartialAsync("HelloWorldPartial", Model)

+

Render Partial Async (Custom model): @{ await RenderHelloWorldPartial(new User() + { + Name="Bob" + }); + }

Getting started

diff --git a/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelper.cs index c8fbff3714..12d009e356 100644 --- a/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelper.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; using System.Net; using System.Reflection; -using System.Text; +using System.Threading.Tasks; using Microsoft.AspNet.Abstractions; namespace Microsoft.AspNet.Mvc.Rendering @@ -21,12 +23,15 @@ namespace Microsoft.AspNet.Mvc.Rendering public static readonly string ValidationSummaryValidCssClassName = "validation-summary-valid"; private ViewContext _viewContext; + private IViewEngine _viewEngine; /// /// Initializes a new instance of the class. /// - public HtmlHelper() + public HtmlHelper(IViewEngine viewEngine) { + _viewEngine = viewEngine; + // Underscores are fine characters in id's. IdAttributeDotReplacement = "_"; } @@ -125,6 +130,42 @@ namespace Microsoft.AspNet.Mvc.Rendering return TagBuilder.CreateSanitizedId(name, IdAttributeDotReplacement); } + public async Task PartialAsync([NotNull] string partialViewName, object model, ViewDataDictionary viewData) + { + using (var writer = new StringWriter(CultureInfo.CurrentCulture)) + { + await RenderPartialCoreAsync(partialViewName, model, viewData, writer); + + return new HtmlString(writer.ToString()); + } + } + + public Task RenderPartialAsync([NotNull] string partialViewName, object model, ViewDataDictionary viewData) + { + return RenderPartialCoreAsync(partialViewName, model, viewData, ViewContext.Writer); + } + + protected virtual async Task RenderPartialCoreAsync([NotNull] string partialViewName, + object model, + ViewDataDictionary viewData, + TextWriter writer) + { + // Determine which ViewData we should use to construct a new ViewData + var baseViewData = viewData ?? ViewData; + + var newViewData = new ViewDataDictionary(baseViewData, model); + + var newViewContext = new ViewContext(ViewContext) + { + ViewData = newViewData, + Writer = writer + }; + + var viewEngineResult = await _viewEngine.FindPartialView(newViewContext.ViewEngineContext, partialViewName); + + await viewEngineResult.View.RenderAsync(newViewContext); + } + /// /// Returns the HTTP method that handles form input (GET or POST) as a string. /// diff --git a/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelperOfT.cs index 08e2a5acf7..58f4c2d786 100644 --- a/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Rendering/Html/HtmlHelperOfT.cs @@ -8,8 +8,8 @@ namespace Microsoft.AspNet.Mvc.Rendering /// /// Initializes a new instance of the class. /// - public HtmlHelper() - : base() + public HtmlHelper(IViewEngine viewEngine) + : base(viewEngine) { } diff --git a/src/Microsoft.AspNet.Mvc.Rendering/Html/Partials/PartialAsyncExtensions.cs b/src/Microsoft.AspNet.Mvc.Rendering/Html/Partials/PartialAsyncExtensions.cs new file mode 100644 index 0000000000..05b7bf9797 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Rendering/Html/Partials/PartialAsyncExtensions.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.Rendering +{ + public static class PartialAsyncExtensions + { + /// + /// Renders the partial view with the parent's view data and model to a string. + /// + /// The of the model. + /// The instance that this method extends. + /// The name of the partial view to render. + /// A that represents when rendering to the has completed. + public static Task PartialAsync(this IHtmlHelper htmlHelper, [NotNull] string partialViewName) + { + return htmlHelper.PartialAsync(partialViewName, htmlHelper.ViewData.Model, viewData: null); + } + + /// + /// Renders the partial view with the given view data and, implicitly, the given view data's model to a string. + /// + /// The of the model. + /// The instance that this method extends. + /// The name of the partial view to render. + /// The that is provided to the partial view that will be rendered. + /// A that represents when rendering to the has completed. + public static Task PartialAsync(this IHtmlHelper htmlHelper, [NotNull] string partialViewName, ViewDataDictionary viewData) + { + return htmlHelper.PartialAsync(partialViewName, htmlHelper.ViewData.Model, viewData: viewData); + } + + /// + /// Renders the partial view with an empty view data and the given model to a string. + /// + /// The of the model. + /// The instance that this method extends. + /// The name of the partial view to render. + /// The model to provide to the partial view that will be rendered. + /// A that represents when rendering to the has completed. + public static Task PartialAsync(this IHtmlHelper htmlHelper, [NotNull] string partialViewName, object model) + { + return htmlHelper.PartialAsync(partialViewName, model, viewData: null); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Rendering/Html/Partials/RenderPartialAsyncExtensions.cs b/src/Microsoft.AspNet.Mvc.Rendering/Html/Partials/RenderPartialAsyncExtensions.cs new file mode 100644 index 0000000000..77a44f5619 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Rendering/Html/Partials/RenderPartialAsyncExtensions.cs @@ -0,0 +1,46 @@ +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Mvc.Rendering +{ + public static class RenderPartialAsyncExtensions + { + + /// + /// Renders the partial view with the parent's view data and model. + /// + /// The of the model. + /// The instance that this method extends. + /// The name of the partial view to render. + /// A that represents when rendering has completed. + public static Task RenderPartialAsync(this IHtmlHelper htmlHelper, [NotNull] string partialViewName) + { + return htmlHelper.RenderPartialAsync(partialViewName, htmlHelper.ViewData.Model, viewData: htmlHelper.ViewData); + } + + /// + /// Renders the partial view with the given view data and, implicitly, the given view data's model. + /// + /// The of the model. + /// The instance that this method extends. + /// The name of the partial view to render. + /// The that is provided to the partial view that will be rendered. + /// A that represents when rendering has completed. + public static Task RenderPartialAsync(this IHtmlHelper htmlHelper, [NotNull] string partialViewName, ViewDataDictionary viewData) + { + return htmlHelper.RenderPartialAsync(partialViewName, htmlHelper.ViewData.Model, viewData: viewData); + } + + /// + /// Renders the partial view with an empty view data and the given model. + /// + /// The of the model. + /// The instance that this method extends. + /// The name of the partial view to render. + /// The model to provide to the partial view that will be rendered. + /// A that represents when rendering has completed. + public static Task RenderPartialAsync(this IHtmlHelper htmlHelper, [NotNull] string partialViewName, object model) + { + return htmlHelper.RenderPartialAsync(partialViewName, model, htmlHelper.ViewData); + } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Rendering/IHtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Rendering/IHtmlHelperOfT.cs index 5fd8e96379..c28357e63c 100644 --- a/src/Microsoft.AspNet.Mvc.Rendering/IHtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Rendering/IHtmlHelperOfT.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; namespace Microsoft.AspNet.Mvc.Rendering { @@ -64,5 +65,24 @@ namespace Microsoft.AspNet.Mvc.Rendering /// object with string representation as HTML markup. /// An that represents HTML markup. HtmlString Raw(object value); + + + /// + /// Returns a partial view in string format. + /// + /// The name of the partial view to render and return. + /// A model to pass into the partial view. + /// A to pass into the partial view. + /// A task that represents when rendering of the partial view into a string has completed. + Task PartialAsync([NotNull] string partialViewName, object model, ViewDataDictionary viewData); + + /// + /// Renders a partial view. + /// + /// The name of the partial view to render. + /// A model to pass into the partial view. + /// A to pass into the partial view. + /// A task that represents when rendering has completed. + Task RenderPartialAsync([NotNull] string partialViewName, object model, ViewDataDictionary viewData); } } diff --git a/src/Microsoft.AspNet.Mvc.Rendering/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Rendering/Properties/Resources.Designer.cs index 80de399c91..a080c51674 100644 --- a/src/Microsoft.AspNet.Mvc.Rendering/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.Rendering/Properties/Resources.Designer.cs @@ -138,22 +138,6 @@ namespace Microsoft.AspNet.Mvc.Rendering return string.Format(CultureInfo.CurrentCulture, GetString("ViewData_WrongTModelType"), p0, p1); } - /// - /// The view '{0}' was not found. The following locations were searched:{1}. - /// - internal static string ViewEngine_ViewNotFound - { - get { return GetString("ViewEngine_ViewNotFound"); } - } - - /// - /// The view '{0}' was not found. The following locations were searched:{1}. - /// - internal static string FormatViewEngine_ViewNotFound(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ViewEngine_ViewNotFound"), p0, p1); - } - private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Mvc.Rendering/View/ViewContext.cs b/src/Microsoft.AspNet.Mvc.Rendering/View/ViewContext.cs index 9124a2335c..e729ede949 100644 --- a/src/Microsoft.AspNet.Mvc.Rendering/View/ViewContext.cs +++ b/src/Microsoft.AspNet.Mvc.Rendering/View/ViewContext.cs @@ -13,6 +13,10 @@ namespace Microsoft.AspNet.Mvc.Rendering private readonly FormContext _defaultFormContext = new FormContext(); private FormContext _formContext; + + public ViewContext([NotNull] ViewContext viewContext) + : this(viewContext.ServiceProvider, viewContext.HttpContext, viewContext.ViewEngineContext) + { } public ViewContext(IServiceProvider serviceProvider, HttpContext httpContext, IDictionary viewEngineContext)