Revive ModelMetadata.ShowForDisplay and ModelMetadata.ShowForEdit
Fixes #679
This commit is contained in:
parent
6cc47cd5f1
commit
24e1ac7ca1
|
|
@ -259,8 +259,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
|
||||
private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo)
|
||||
{
|
||||
// TODO: add ModelMetadata.ShowForDisplay and include in this calculation (first)
|
||||
return
|
||||
metadata.ShowForDisplay &&
|
||||
!metadata.IsComplexType &&
|
||||
!templateInfo.Visited(metadata);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,8 +270,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
|||
|
||||
private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo)
|
||||
{
|
||||
// TODO: add ModelMetadata.ShowForEdit and include in this calculation (first)
|
||||
return
|
||||
metadata.ShowForEdit &&
|
||||
!metadata.IsComplexType
|
||||
&& !templateInfo.Visited(metadata);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
DisplayColumn = attributes.OfType<DisplayColumnAttribute>().FirstOrDefault();
|
||||
Editable = attributes.OfType<EditableAttribute>().FirstOrDefault();
|
||||
Required = attributes.OfType<RequiredAttribute>().FirstOrDefault();
|
||||
ScaffoldColumn = attributes.OfType<ScaffoldColumnAttribute>().FirstOrDefault();
|
||||
}
|
||||
|
||||
public DisplayAttribute Display { get; protected set; }
|
||||
|
|
@ -28,5 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
public EditableAttribute Editable { get; protected set; }
|
||||
|
||||
public RequiredAttribute Required { get; protected set; }
|
||||
|
||||
public ScaffoldColumnAttribute ScaffoldColumn { get; protected set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return base.ComputeSimpleDisplayText();
|
||||
}
|
||||
|
||||
protected override bool ComputeShowForDisplay()
|
||||
{
|
||||
return PrototypeCache.ScaffoldColumn != null
|
||||
? PrototypeCache.ScaffoldColumn.Scaffold
|
||||
: base.ComputeShowForDisplay();
|
||||
}
|
||||
|
||||
protected override bool ComputeShowForEdit()
|
||||
{
|
||||
return PrototypeCache.ScaffoldColumn != null
|
||||
? PrototypeCache.ScaffoldColumn.Scaffold
|
||||
: base.ComputeShowForEdit();
|
||||
}
|
||||
|
||||
public override string GetDisplayName()
|
||||
{
|
||||
// DisplayAttribute doesn't require you to set a name, so this could be null.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private bool _isReadOnly;
|
||||
private bool _isComplexType;
|
||||
private bool _isRequired;
|
||||
private bool _showForDisplay;
|
||||
private bool _showForEdit;
|
||||
|
||||
private bool _convertEmptyStringToNullComputed;
|
||||
private bool _nullDisplayTextComputed;
|
||||
|
|
@ -28,6 +30,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private bool _isReadOnlyComputed;
|
||||
private bool _isComplexTypeComputed;
|
||||
private bool _isRequiredComputed;
|
||||
private bool _showForDisplayComputed;
|
||||
private bool _showForEditComputed;
|
||||
|
||||
// Constructor for creating real instances of the metadata class based on a prototype
|
||||
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor)
|
||||
|
|
@ -158,6 +162,42 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
public sealed override bool ShowForDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_showForDisplayComputed)
|
||||
{
|
||||
_showForDisplay = ComputeShowForDisplay();
|
||||
_showForDisplayComputed = true;
|
||||
}
|
||||
return _showForDisplay;
|
||||
}
|
||||
set
|
||||
{
|
||||
_showForDisplay = value;
|
||||
_showForDisplayComputed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed override bool ShowForEdit
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_showForEditComputed)
|
||||
{
|
||||
_showForEdit = ComputeShowForEdit();
|
||||
_showForEditComputed = true;
|
||||
}
|
||||
return _showForEdit;
|
||||
}
|
||||
set
|
||||
{
|
||||
_showForEdit = value;
|
||||
_showForEditComputed = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected TPrototypeCache PrototypeCache { get; set; }
|
||||
|
||||
protected virtual bool ComputeConvertEmptyStringToNull()
|
||||
|
|
@ -189,5 +229,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
return base.IsComplexType;
|
||||
}
|
||||
|
||||
protected virtual bool ComputeShowForDisplay()
|
||||
{
|
||||
return base.ShowForDisplay;
|
||||
}
|
||||
|
||||
protected virtual bool ComputeShowForEdit()
|
||||
{
|
||||
return base.ShowForEdit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private IEnumerable<ModelMetadata> _properties;
|
||||
private Type _realModelType;
|
||||
private string _simpleDisplayText;
|
||||
private bool _showForDisplay = true;
|
||||
private bool _showForEdit = true;
|
||||
|
||||
public ModelMetadata([NotNull] IModelMetadataProvider provider,
|
||||
Type containerType,
|
||||
|
|
@ -164,6 +166,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
set { _simpleDisplayText = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether the property should be displayed in read-only views.
|
||||
/// </summary>
|
||||
public virtual bool ShowForDisplay
|
||||
{
|
||||
get { return _showForDisplay; }
|
||||
set { _showForDisplay = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that indicates whether the property should be displayed in editable views.
|
||||
/// </summary>
|
||||
public virtual bool ShowForEdit
|
||||
{
|
||||
get { return _showForEdit; }
|
||||
set { _showForEdit = value; }
|
||||
}
|
||||
|
||||
public virtual string TemplateHint { get; set; }
|
||||
|
||||
internal EfficientTypePropertyKey<Type, string> CacheKey
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
|
|
@ -70,5 +73,28 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
// Assert
|
||||
Assert.Equal(metadata.SimpleDisplayText, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObjectTemplate_IgnoresPropertiesWith_ScaffoldColumnFalse()
|
||||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"<div class=""display-label"">Property1</div>
|
||||
<div class=""display-field""></div>
|
||||
<div class=""display-label"">Property3</div>
|
||||
<div class=""display-field""></div>
|
||||
";
|
||||
var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn();
|
||||
var viewEngine = new Mock<IViewEngine>();
|
||||
viewEngine.Setup(v => v.FindPartialView(It.IsAny<IDictionary<string, object>>(), It.IsAny<string>()))
|
||||
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
|
||||
var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
||||
|
||||
// Act
|
||||
var result = DefaultDisplayTemplates.ObjectTemplate(htmlHelper);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.Rendering;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
|
|
@ -74,5 +77,28 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
// Assert
|
||||
Assert.Equal(metadata.SimpleDisplayText, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ObjectTemplate_IgnoresPropertiesWith_ScaffoldColumnFalse()
|
||||
{
|
||||
// Arrange
|
||||
var expected =
|
||||
@"<div class=""editor-label""><label for=""Property1"">Property1</label></div>
|
||||
<div class=""editor-field""><input class=""text-box single-line"" id=""Property1"" name=""Property1"" type=""text"" value="""" /> </div>
|
||||
<div class=""editor-label""><label for=""Property1.Property3"">Property3</label></div>
|
||||
<div class=""editor-field""> </div>
|
||||
";
|
||||
var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn();
|
||||
var viewEngine = new Mock<IViewEngine>();
|
||||
viewEngine.Setup(v => v.FindPartialView(It.IsAny<IDictionary<string, object>>(), It.IsAny<string>()))
|
||||
.Returns(ViewEngineResult.NotFound("", Enumerable.Empty<string>()));
|
||||
var htmlHelper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
||||
|
||||
// Act
|
||||
var result = DefaultEditorTemplates.ObjectTemplate(htmlHelper);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
|
@ -30,7 +32,19 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
public object ComplexInnerModel { get; set; }
|
||||
}
|
||||
|
||||
public static HtmlHelper GetHtmlHelper(object model)
|
||||
public class ObjectWithScaffoldColumn
|
||||
{
|
||||
public string Property1 { get; set; }
|
||||
|
||||
[ScaffoldColumn(false)]
|
||||
public string Property2 { get; set; }
|
||||
|
||||
[ScaffoldColumn(true)]
|
||||
public string Property3 { get; set; }
|
||||
}
|
||||
|
||||
public static HtmlHelper GetHtmlHelper(object model,
|
||||
IViewEngine viewEngine = null)
|
||||
{
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
var viewData = new ViewDataDictionary(provider);
|
||||
|
|
@ -46,6 +60,59 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
.Setup(o => o.Items)
|
||||
.Returns(new Dictionary<object, object>());
|
||||
|
||||
viewEngine = viewEngine ?? CreateViewEngine();
|
||||
|
||||
var actionContext = new ActionContext(httpContext.Object,
|
||||
new RouteData(),
|
||||
new ActionDescriptor());
|
||||
|
||||
var actionBindingContext = new ActionBindingContext(actionContext,
|
||||
provider,
|
||||
Mock.Of<IModelBinder>(),
|
||||
Mock.Of<IValueProvider>(),
|
||||
Mock.Of<IInputFormatterProvider>(),
|
||||
Enumerable.Empty<IModelValidatorProvider>());
|
||||
var urlHelper = Mock.Of<IUrlHelper>();
|
||||
var actionBindingContextProvider = new Mock<IActionBindingContextProvider>();
|
||||
actionBindingContextProvider
|
||||
.Setup(c => c.GetActionBindingContextAsync(It.IsAny<ActionContext>()))
|
||||
.Returns(Task.FromResult(actionBindingContext));
|
||||
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider
|
||||
.Setup(s => s.GetService(typeof(IViewEngine)))
|
||||
.Returns(viewEngine);
|
||||
serviceProvider
|
||||
.Setup(s => s.GetService(typeof(IUrlHelper)))
|
||||
.Returns(urlHelper);
|
||||
serviceProvider
|
||||
.Setup(s => s.GetService(typeof(IViewComponentHelper)))
|
||||
.Returns(new Mock<IViewComponentHelper>().Object);
|
||||
|
||||
httpContext
|
||||
.Setup(o => o.RequestServices)
|
||||
.Returns(serviceProvider.Object);
|
||||
|
||||
var viewContext = new ViewContext(actionContext, Mock.Of<IView>(), viewData, new StringWriter());
|
||||
|
||||
var htmlHelper = new HtmlHelper(
|
||||
viewEngine,
|
||||
provider,
|
||||
urlHelper,
|
||||
GetAntiForgeryInstance(),
|
||||
actionBindingContextProvider.Object);
|
||||
htmlHelper.Contextualize(viewContext);
|
||||
|
||||
serviceProvider
|
||||
.Setup(s => s.GetService(typeof(IHtmlHelper)))
|
||||
.Returns(htmlHelper);
|
||||
|
||||
|
||||
return htmlHelper;
|
||||
}
|
||||
|
||||
private static IViewEngine CreateViewEngine()
|
||||
{
|
||||
var view = new Mock<IView>();
|
||||
view
|
||||
.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||
|
|
@ -55,42 +122,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
})
|
||||
.Returns(Task.FromResult(0));
|
||||
|
||||
var routeDictionary = new Dictionary<string, object>();
|
||||
var viewEngine = new Mock<IViewEngine>();
|
||||
viewEngine
|
||||
.Setup(v => v.FindPartialView(routeDictionary, It.IsAny<string>()))
|
||||
.Setup(v => v.FindPartialView(It.IsAny<Dictionary<string, object>>(), It.IsAny<string>()))
|
||||
.Returns(ViewEngineResult.Found("MyView", view.Object));
|
||||
|
||||
var serviceProvider = new Mock<IServiceProvider>();
|
||||
serviceProvider
|
||||
.Setup(s => s.GetService(typeof(IViewEngine)))
|
||||
.Returns(viewEngine.Object);
|
||||
serviceProvider
|
||||
.Setup(s => s.GetService(typeof(IUrlHelper)))
|
||||
.Returns(new Mock<IUrlHelper>().Object);
|
||||
serviceProvider
|
||||
.Setup(s => s.GetService(typeof(IViewComponentHelper)))
|
||||
.Returns(new Mock<IViewComponentHelper>().Object);
|
||||
|
||||
httpContext
|
||||
.Setup(o => o.RequestServices)
|
||||
.Returns(serviceProvider.Object);
|
||||
|
||||
var actionContext = new ActionContext(httpContext.Object,
|
||||
new RouteData() { Values = routeDictionary },
|
||||
new ActionDescriptor());
|
||||
|
||||
var viewContext = new ViewContext(actionContext, view.Object, viewData, new StringWriter());
|
||||
|
||||
var htmlHelper = new HtmlHelper(
|
||||
viewEngine.Object,
|
||||
provider,
|
||||
new Mock<IUrlHelper>().Object,
|
||||
GetAntiForgeryInstance(),
|
||||
new Mock<IActionBindingContextProvider>().Object);
|
||||
htmlHelper.Contextualize(viewContext);
|
||||
|
||||
return htmlHelper;
|
||||
return viewEngine.Object;
|
||||
}
|
||||
|
||||
private static AntiForgery GetAntiForgeryInstance()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
// 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 System.ComponentModel.DataAnnotations;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class CachedDataAnnotationsModelMetadataProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void DataAnnotationsModelMetadataProvider_ReadsScaffoldColumnAttribute_ForShowForDisplay()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ScaffoldColumnModel);
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Act & Assert
|
||||
Assert.True(provider.GetMetadataForProperty(null, type, "NoAttribute").ShowForDisplay);
|
||||
Assert.True(provider.GetMetadataForProperty(null, type, "ScaffoldColumnTrue").ShowForDisplay);
|
||||
Assert.False(provider.GetMetadataForProperty(null, type, "ScaffoldColumnFalse").ShowForDisplay);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataAnnotationsModelMetadataProvider_ReadsScaffoldColumnAttribute_ForShowForEdit()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(ScaffoldColumnModel);
|
||||
var provider = new DataAnnotationsModelMetadataProvider();
|
||||
|
||||
// Act & Assert
|
||||
Assert.True(provider.GetMetadataForProperty(null, type, "NoAttribute").ShowForEdit);
|
||||
Assert.True(provider.GetMetadataForProperty(null, type, "ScaffoldColumnTrue").ShowForEdit);
|
||||
Assert.False(provider.GetMetadataForProperty(null, type, "ScaffoldColumnFalse").ShowForEdit);
|
||||
}
|
||||
|
||||
private class ScaffoldColumnModel
|
||||
{
|
||||
public int NoAttribute { get; set; }
|
||||
|
||||
[ScaffoldColumn(scaffold: true)]
|
||||
public int ScaffoldColumnTrue { get; set; }
|
||||
|
||||
[ScaffoldColumn(scaffold: false)]
|
||||
public int ScaffoldColumnFalse { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
<Compile Include="Formatters\JsonInputFormatterTest.cs" />
|
||||
<Compile Include="Internal\TypeExtensionTests.cs" />
|
||||
<Compile Include="Metadata\AssociatedMetadataProviderTest.cs" />
|
||||
<Compile Include="Metadata\CachedDataAnnotationsModelMetadataProviderTest.cs" />
|
||||
<Compile Include="Metadata\ModelMetadataTest.cs" />
|
||||
<Compile Include="ModelBindingContextTest.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs" />
|
||||
|
|
@ -60,4 +61,4 @@
|
|||
<Compile Include="ValueProviders\ValueProviderResultTest.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
</Project>
|
||||
Loading…
Reference in New Issue