Revive ModelMetadata.ShowForDisplay and ModelMetadata.ShowForEdit

Fixes #679
This commit is contained in:
Pranav K 2014-06-18 17:48:50 -07:00
parent 6cc47cd5f1
commit 24e1ac7ca1
11 changed files with 261 additions and 37 deletions

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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; }
}
}

View File

@ -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.

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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()

View File

@ -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; }
}
}
}

View File

@ -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>