RazorView should not create new ViewContext when rendering pages.
For pages that can pop back (e.g. Partial pages \ View Components), the invoking component already creates a new ViewContext. ViewStart, Pages and Layouts need to share the same Layout. This is required for sharing ViewData values (such as title). Fixes #861
This commit is contained in:
parent
3746e44dc3
commit
92e26cf8e0
|
|
@ -88,12 +88,8 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
private async Task RenderPageCoreAsync(IRazorPage page, ViewContext context)
|
||||
{
|
||||
// Activating a page might mutate the ViewContext (for instance ViewContext.ViewData) is mutated by
|
||||
// RazorPageActivator. We'll instead pass in a copy of the ViewContext.
|
||||
var pageViewContext = new ViewContext(context, context.View, context.ViewData, context.Writer);
|
||||
page.ViewContext = pageViewContext;
|
||||
_pageActivator.Activate(page, pageViewContext);
|
||||
|
||||
page.ViewContext = context;
|
||||
_pageActivator.Activate(page, context);
|
||||
await page.ExecuteAsync();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,5 +85,24 @@ test-value";
|
|||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RazorView_PassesViewContextBetweenViewAndLayout()
|
||||
{
|
||||
var expected =
|
||||
@"<title>Page title</title>
|
||||
|
||||
partial-content
|
||||
component-content";
|
||||
var server = TestServer.Create(_provider, _app);
|
||||
var client = server.Handler;
|
||||
|
||||
// Act
|
||||
var result = await client.GetAsync("http://localhost/ViewEngine/ViewPassesViewDataToLayout");
|
||||
|
||||
// Assert
|
||||
var body = await result.HttpContext.Response.ReadBodyAsStringAsync();
|
||||
Assert.Equal(expected, body.Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RenderAsync_WithoutHierarchy_ActivatesViews_WithACopyOfViewContext()
|
||||
public async Task RenderAsync_WithoutHierarchy_ActivatesViews_WithThePassedInViewContext()
|
||||
{
|
||||
// Arrange
|
||||
var viewData = new ViewDataDictionary(Mock.Of<IModelMetadataProvider>());
|
||||
|
|
@ -59,12 +59,11 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
page,
|
||||
executeViewHierarchy: false);
|
||||
var viewContext = CreateViewContext(view);
|
||||
var expectedViewData = viewContext.ViewData;
|
||||
var expectedWriter = viewContext.Writer;
|
||||
activator.Setup(a => a.Activate(page, It.IsAny<ViewContext>()))
|
||||
.Callback((IRazorPage p, ViewContext c) =>
|
||||
{
|
||||
Assert.NotSame(c, viewContext);
|
||||
Assert.Same(c, viewContext);
|
||||
c.ViewData = viewData;
|
||||
})
|
||||
.Verifiable();
|
||||
|
|
@ -74,7 +73,6 @@ namespace Microsoft.AspNet.Mvc.Razor
|
|||
|
||||
// Assert
|
||||
activator.Verify();
|
||||
Assert.Same(expectedViewData, viewContext.ViewData);
|
||||
Assert.Same(expectedWriter, viewContext.Writer);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace MvcSample.Web.Components
|
||||
{
|
||||
[ViewComponent(Name = "ComponentThatSetsTitle")]
|
||||
public class ComponentThatSetsTitle : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,5 +36,11 @@ namespace RazorWebSite.Controllers
|
|||
};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
public ViewResult ViewPassesViewDataToLayout()
|
||||
{
|
||||
ViewData["Title"] = "Controller title";
|
||||
return View("ViewWithTitle");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,11 @@
|
|||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Project.json" />
|
||||
<Content Include="Views\Shared\Components\ComponentThatSetsTitle\Default.cshtml" />
|
||||
<Content Include="Views\Shared\_LayoutWithTitle.cshtml" />
|
||||
<Content Include="Views\Shared\_Partial.cshtml" />
|
||||
<Content Include="Views\Shared\_PartialThatSetsTitle.cshtml" />
|
||||
<Content Include="Views\ViewEngine\ViewWithTitle.cshtml" />
|
||||
<Content Include="Views\ViewEngine\ViewWithNestedLayout.cshtml" />
|
||||
<Content Include="Views\ViewEngine\ViewWithLayout.cshtml" />
|
||||
<Content Include="Views\ViewEngine\ViewWithFullPath.cshtml" />
|
||||
|
|
@ -34,6 +38,7 @@
|
|||
<Content Include="Views\ViewEngine\_NestedLayout.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Components\ComponentThatSetsTitle.cs" />
|
||||
<Compile Include="Controllers\ViewEngineController.cs" />
|
||||
<Compile Include="Models\Person.cs" />
|
||||
<Compile Include="Models\Address.cs" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
ViewData["Title"] = "Component title";
|
||||
}
|
||||
component-content
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<title>@ViewBag.Title</title>
|
||||
@RenderBody()
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@{
|
||||
ViewBag.Title = "Partial title";
|
||||
}
|
||||
partial-content
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
@{
|
||||
ViewData["Title"] = "Page title";
|
||||
// The invoked partial sets a title, but this shouldn't override the current page's ViewData \ ViewBag.
|
||||
await Html.RenderPartialAsync("_PartialThatSetsTitle");
|
||||
await Component.RenderInvokeAsync("ComponentThatSetsTitle");
|
||||
Layout = "/Views/Shared/_LayoutWithTitle.cshtml";
|
||||
}
|
||||
Loading…
Reference in New Issue