Define ILayoutComponent so there's compile-time validation that layouts are defined correctly

This commit is contained in:
Steve Sanderson 2018-02-16 13:06:19 +00:00
parent 7139cb70c5
commit f54df27c21
4 changed files with 40 additions and 8 deletions

View File

@ -14,6 +14,7 @@
*/
using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.Layouts;
using System;
namespace Microsoft.AspNetCore.Mvc
@ -28,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor
{
// This is temporary and exists only to support TemporaryLayoutPass.
// It will be removed when we can add Blazor-specific directives.
public object Layout<TLayout>() where TLayout : IComponent
public object Layout<TLayout>() where TLayout : ILayoutComponent
=> throw new NotImplementedException();
// Similar temporary mechanism as above

View File

@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.Components;
using Microsoft.AspNetCore.Blazor.RenderTree;
namespace Microsoft.AspNetCore.Blazor.Layouts
{
/// <summary>
/// Indicates that the type represents a layout.
/// </summary>
public interface ILayoutComponent : IComponent
{
/// <summary>
/// Gets or sets the content to be rendered inside the layout.
/// </summary>
RenderFragment Body { get; set; }
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Blazor.Components;
using System;
namespace Microsoft.AspNetCore.Blazor.Layouts
@ -13,22 +12,22 @@ namespace Microsoft.AspNetCore.Blazor.Layouts
public class LayoutAttribute : Attribute
{
/// <summary>
/// The type of the layout. The type always implements <see cref="IComponent"/>.
/// The type of the layout. The type always implements <see cref="ILayoutComponent"/>.
/// </summary>
public Type LayoutType { get; private set; }
/// <summary>
/// Constructs an instance of <see cref="LayoutAttribute"/>.
/// </summary>
/// <param name="layoutType">The type of the layout. This must implement <see cref="IComponent"/>.</param>
/// <param name="layoutType">The type of the layout. This must implement <see cref="ILayoutComponent"/>.</param>
public LayoutAttribute(Type layoutType)
{
LayoutType = layoutType ?? throw new ArgumentNullException(nameof(layoutType));
if (!typeof(IComponent).IsAssignableFrom(layoutType))
if (!typeof(ILayoutComponent).IsAssignableFrom(layoutType))
{
throw new ArgumentException($"Invalid layout type: {layoutType.FullName} " +
$"does not implement {typeof(IComponent).FullName}.");
$"does not implement {typeof(ILayoutComponent).FullName}.");
}
}
}

View File

@ -375,7 +375,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
public void SupportsLayoutDeclarationsViaTemporarySyntax()
{
// Arrange/Act
var testComponentTypeName = typeof(TestComponent).FullName.Replace('+', '.');
var testComponentTypeName = typeof(TestLayout).FullName.Replace('+', '.');
var component = CompileToComponent(
$"@(Layout<{testComponentTypeName}>())" +
$"Hello");
@ -384,7 +384,7 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
// Assert
var layoutAttribute = component.GetType().GetCustomAttribute<LayoutAttribute>();
Assert.NotNull(layoutAttribute);
Assert.Equal(typeof(TestComponent), layoutAttribute.LayoutType);
Assert.Equal(typeof(TestLayout), layoutAttribute.LayoutType);
Assert.Collection(frames,
frame => AssertFrame.Text(frame, "Hello"));
}
@ -540,6 +540,19 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
}
}
public class TestLayout : ILayoutComponent
{
public RenderFragment Body { get; set; }
public void Init(RenderHandle renderHandle)
{
}
public void SetParameters(ParameterCollection parameters)
{
}
}
public interface ITestInterface { }
}
}