Add `HideSurroundingHtml` property to `ModelMetadata`

- #843
- add property as well as the related `[HiddenInput]` attribute
- use this property to address some TODOs in default display and editor templates
This commit is contained in:
dougbu 2014-08-10 21:33:43 -07:00
parent 3f952c153f
commit 796ff1d3d3
7 changed files with 129 additions and 9 deletions

View File

@ -186,7 +186,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string HiddenInputTemplate(IHtmlHelper html)
{
// TODO: add ModelMetadata.HideSurroundingHtml and use here (return string.Empty)
if (html.ViewData.ModelMetadata.HideSurroundingHtml)
{
return string.Empty;
}
return StringTemplate(html);
}
@ -219,7 +223,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var divTag = new TagBuilder("div");
// TODO: add ModelMetadata.HideSurroundingHtml and use here (skip this block)
if (!propertyMetadata.HideSurroundingHtml)
{
var label = propertyMetadata.GetDisplayName();
if (!string.IsNullOrEmpty(label))
@ -248,7 +252,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
builder.Append(templateBuilder.Build());
// TODO: add ModelMetadata.HideSurroundingHtml and use here (skip this block)
if (!propertyMetadata.HideSurroundingHtml)
{
builder.AppendLine(divTag.ToString(TagRenderMode.EndTag));
}

View File

@ -135,12 +135,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
public static string HiddenInputTemplate(IHtmlHelper html)
{
var viewData = html.ViewData;
// TODO: add ModelMetadata.HideSurroundingHtml and use here (set result to string.Empty)
var result = DefaultDisplayTemplates.StringTemplate(html);
var model = viewData.Model;
string result;
if (viewData.ModelMetadata.HideSurroundingHtml)
{
result = string.Empty;
}
else
{
result = DefaultDisplayTemplates.StringTemplate(html);
}
// Special-case opaque values and arbitrary binary data.
var modelAsByteArray = model as byte[];
if (modelAsByteArray != null)
@ -225,7 +231,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
{
var divTag = new TagBuilder("div");
// TODO: add ModelMetadata.HideSurroundingHtml and use here (skip this block)
if (!propertyMetadata.HideSurroundingHtml)
{
var label = html.Label(
propertyMetadata.PropertyName,
@ -258,8 +264,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
builder.Append(templateBuilder.Build());
// TODO: add ModelMetadata.HideSurroundingHtml and use here (skip this block)
// TODO: Add IHtmlHelper.ValidationMessage() and call just prior to closing the <div/> tag
if (!propertyMetadata.HideSurroundingHtml)
{
builder.Append(" ");
builder.AppendLine(divTag.ToString(TagRenderMode.EndTag));

View File

@ -0,0 +1,41 @@
// 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;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Indicates associated property or all properties of associated type should be edited using an &lt;input&gt;
/// element of type "hidden".
/// </summary>
/// <remarks>
/// When overriding a <see cref="HiddenInputAttribute"/> inherited from a base class, should apply both
/// <c>[HiddenInput(DisplayValue = true)]</c> (if the inherited attribute had <c>DisplayValue = false</c>) and a
/// <see cref="System.ComponentModel.DataAnnotations.UIHintAttribute"/> with some value other than "HiddenInput".
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class HiddenInputAttribute : Attribute
{
/// <summary>
/// Instantiates a new instance of the <see cref="HiddenInputAttribute"/> class.
/// </summary>
public HiddenInputAttribute()
{
DisplayValue = true;
}
/// <summary>
/// Gets or sets a value indicating whether to display the value as well as provide a hidden &lt;input&gt;
/// element. The default value is <c>true</c>.
/// </summary>
/// <remarks>
/// If <c>false</c>, also causes the default <see cref="object"/> display and editor templates to return HTML
/// lacking the usual per-property &lt;div&gt; wrapper around the associated property and the default display
/// "HiddenInput" template to return <c>string.Empty</c> for the associated property. Thus the default
/// <see cref="object"/> display template effectively skips the property and the default <see cref="object"/>
/// editor template returns only the hidden &lt;input&gt; element for the property.
/// </remarks>
public bool DisplayValue { get; set; }
}
}

View File

@ -16,6 +16,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
DisplayFormat = attributes.OfType<DisplayFormatAttribute>().FirstOrDefault();
DisplayColumn = attributes.OfType<DisplayColumnAttribute>().FirstOrDefault();
Editable = attributes.OfType<EditableAttribute>().FirstOrDefault();
HiddenInput = attributes.OfType<HiddenInputAttribute>().FirstOrDefault();
Required = attributes.OfType<RequiredAttribute>().FirstOrDefault();
ScaffoldColumn = attributes.OfType<ScaffoldColumnAttribute>().FirstOrDefault();
}
@ -28,6 +29,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public EditableAttribute Editable { get; protected set; }
/// <summary>
/// Gets (or sets in subclasses) <see cref="HiddenInputAttribute"/> found in collection passed to the
/// <see cref="CachedDataAnnotationsMetadataAttributes(IEnumerable{Attribute})"/> constructor, if any.
/// </summary>
public HiddenInputAttribute HiddenInput { get; protected set; }
public RequiredAttribute Required { get; protected set; }
public ScaffoldColumnAttribute ScaffoldColumn { get; protected set; }

View File

@ -67,6 +67,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return base.ComputeDisplayName();
}
/// <summary>
/// Calculate <see cref="ModelMetadata.HideSurroundingHtml"/> based on presence of an
/// <see cref="HiddenInputAttribute"/> and its <see cref="HiddenInputAttribute.DisplayValue"/> value.
/// </summary>
/// <returns>Calculated <see cref="ModelMetadata.HideSurroundingHtml"/> value. <c>true</c> if an
/// <see cref="HiddenInputAttribute"/> exists and its <see cref="HiddenInputAttribute.DisplayValue"/> value is
/// <c>false</c>; <c>false</c> otherwise.</returns>
protected override bool ComputeHideSurroundingHtml()
{
if (PrototypeCache.HiddenInput != null)
{
return !PrototypeCache.HiddenInput.DisplayValue;
}
return base.ComputeHideSurroundingHtml();
}
protected override bool ComputeIsReadOnly()
{
if (PrototypeCache.Editable != null)

View File

@ -19,6 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private string _nullDisplayText;
private string _description;
private string _displayName;
private bool _hideSurroundingHtml;
private bool _isReadOnly;
private bool _isComplexType;
private bool _isRequired;
@ -29,6 +30,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private bool _nullDisplayTextComputed;
private bool _descriptionComputed;
private bool _displayNameComputed;
private bool _hideSurroundingHtmlComputed;
private bool _isReadOnlyComputed;
private bool _isComplexTypeComputed;
private bool _isRequiredComputed;
@ -134,6 +136,27 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
}
/// <inheritdoc />
public sealed override bool HideSurroundingHtml
{
get
{
if (!_hideSurroundingHtmlComputed)
{
_hideSurroundingHtml = ComputeHideSurroundingHtml();
_hideSurroundingHtmlComputed = true;
}
return _hideSurroundingHtml;
}
set
{
_hideSurroundingHtml = value;
_hideSurroundingHtmlComputed = true;
}
}
public sealed override bool IsReadOnly
{
get
@ -241,6 +264,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return base.DisplayName;
}
/// <summary>
/// Calculate the <see cref="HideSurroundingHtml"/> value.
/// </summary>
/// <returns>Calculated <see cref="HideSurroundingHtml"/> value.</returns>
protected virtual bool ComputeHideSurroundingHtml()
{
return base.HideSurroundingHtml;
}
protected virtual bool ComputeIsReadOnly()
{
return base.IsReadOnly;

View File

@ -69,6 +69,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public virtual string EditFormatString { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the "HiddenInput" display template should return
/// <c>string.Empty</c> (not the expression value) and whether the "HiddenInput" editor template should not
/// also return the expression value (together with the hidden &lt;input&gt; element).
/// </summary>
/// <remarks>
/// If <c>true</c>, also causes the default <see cref="object"/> display and editor templates to return HTML
/// lacking the usual per-property &lt;div&gt; wrapper around the associated property. Thus the default
/// <see cref="object"/> display template effectively skips the property and the default <see cref="object"/>
/// editor template returns only the hidden &lt;input&gt; element for the property.
/// </remarks>
public virtual bool HideSurroundingHtml { get; set; }
public virtual bool IsComplexType
{
get { return !ValueProviderResult.CanConvertFromString(ModelType); }