Seperate view and model for enum display
This commit is contained in:
parent
52ee9afc31
commit
8f8bf5af34
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
@ -524,23 +523,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
string templateName,
|
||||
object additionalViewData)
|
||||
{
|
||||
var modelEnum = modelExplorer.Model as Enum;
|
||||
if (modelExplorer.Metadata.IsEnum && modelEnum != null)
|
||||
{
|
||||
var value = modelEnum.ToString("d");
|
||||
var enumGrouped = modelExplorer.Metadata.EnumGroupedDisplayNamesAndValues;
|
||||
Debug.Assert(enumGrouped != null);
|
||||
foreach (var kvp in enumGrouped)
|
||||
{
|
||||
if (kvp.Value == value)
|
||||
{
|
||||
// Creates a ModelExplorer with the same Metadata except that the Model is a string instead of an Enum
|
||||
modelExplorer = modelExplorer.GetExplorerForModel(kvp.Key.Name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var templateBuilder = new TemplateBuilder(
|
||||
_viewEngine,
|
||||
_bufferScope,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
|
@ -81,19 +82,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
_model = null;
|
||||
}
|
||||
|
||||
var formattedModelValue = _model;
|
||||
if (_model == null && _readOnly)
|
||||
{
|
||||
formattedModelValue = _metadata.NullDisplayText;
|
||||
}
|
||||
|
||||
var formatString = _readOnly ? _metadata.DisplayFormatString : _metadata.EditFormatString;
|
||||
|
||||
if (_model != null && !string.IsNullOrEmpty(formatString))
|
||||
{
|
||||
formattedModelValue = string.Format(CultureInfo.CurrentCulture, formatString, _model);
|
||||
}
|
||||
|
||||
// Normally this shouldn't happen, unless someone writes their own custom Object templates which
|
||||
// don't check to make sure that the object hasn't already been displayed
|
||||
if (_viewData.TemplateInfo.Visited(_modelExplorer))
|
||||
|
|
@ -108,6 +96,40 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
// though _model may have been reset to null. Otherwise we might lose track of the model type /property.
|
||||
viewData.ModelExplorer = _modelExplorer.GetExplorerForModel(_model);
|
||||
|
||||
var formattedModelValue = _model;
|
||||
if (_model == null && _readOnly)
|
||||
{
|
||||
formattedModelValue = _metadata.NullDisplayText;
|
||||
}
|
||||
else if (viewData.ModelMetadata.IsEnum)
|
||||
{
|
||||
// Cover the case where the model is an enum and we want the string value of it
|
||||
var modelEnum = _model as Enum;
|
||||
if (modelEnum != null)
|
||||
{
|
||||
var value = modelEnum.ToString("d");
|
||||
var enumGrouped = viewData.ModelMetadata.EnumGroupedDisplayNamesAndValues;
|
||||
Debug.Assert(enumGrouped != null);
|
||||
foreach (var kvp in enumGrouped)
|
||||
{
|
||||
if (kvp.Value == value)
|
||||
{
|
||||
// Creates a ModelExplorer with the same Metadata except that the Model is a string instead of an Enum
|
||||
formattedModelValue = kvp.Key.Name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var formatString = _readOnly ?
|
||||
viewData.ModelMetadata.DisplayFormatString :
|
||||
viewData.ModelMetadata.EditFormatString;
|
||||
if (_model != null && !string.IsNullOrEmpty(formatString))
|
||||
{
|
||||
formattedModelValue = string.Format(CultureInfo.CurrentCulture, formatString, formattedModelValue);
|
||||
}
|
||||
|
||||
viewData.TemplateInfo.FormattedModelValue = formattedModelValue;
|
||||
viewData.TemplateInfo.HtmlFieldPrefix = _viewData.TemplateInfo.GetFullHtmlFieldName(_htmlFieldName);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,16 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EnumValues_SerializeCorrectly()
|
||||
{
|
||||
// Arrange & Act
|
||||
var response = await Client.GetStringAsync("http://localhost/HtmlGeneration_Home/Enum");
|
||||
|
||||
// Assert
|
||||
Assert.Equal($"Vrijdag{Environment.NewLine}Month: January", response, ignoreLineEndingDifferences: true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(WebPagesData))]
|
||||
public async Task HtmlGenerationWebSite_GeneratesExpectedResults(string action, string antiforgeryPath)
|
||||
|
|
|
|||
|
|
@ -94,6 +94,94 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
viewEngine.Verify();
|
||||
}
|
||||
|
||||
public static TheoryData<FormatModel, string> EnumFormatModels
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<FormatModel, string>
|
||||
{
|
||||
{
|
||||
new FormatModel{ FormatProperty = Status.Created },
|
||||
"Value: CreatedKey"
|
||||
},
|
||||
{
|
||||
new FormatModel { FormatProperty = Status.Done },
|
||||
"Value: Done"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static TheoryData<FormatModel, string> EnumUnformattedModels
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<FormatModel, string>
|
||||
{
|
||||
{
|
||||
new FormatModel {NonFormatProperty = Status.Created },
|
||||
"CreatedKey"
|
||||
},
|
||||
{
|
||||
new FormatModel {NonFormatProperty = Status.Done },
|
||||
"Done"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EnumUnformattedModels))]
|
||||
public void Display_UsesTemplateUnFormatted(FormatModel model, string expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var view = new Mock<IView>();
|
||||
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||
.Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString()))
|
||||
.Returns(Task.FromResult(0));
|
||||
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
||||
viewEngine
|
||||
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
||||
viewEngine
|
||||
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "DisplayTemplates/Status", /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.Found("Status", view.Object))
|
||||
.Verifiable();
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
||||
|
||||
// Act
|
||||
var displayResult = helper.DisplayFor(x => x.NonFormatProperty);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(displayResult));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(EnumFormatModels))]
|
||||
public void Display_UsesTemplateFormatted(FormatModel model, string expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var view = new Mock<IView>();
|
||||
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||
.Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString()))
|
||||
.Returns(Task.FromResult(0));
|
||||
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
||||
viewEngine
|
||||
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
||||
viewEngine
|
||||
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "DisplayTemplates/Status", /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.Found("Status", view.Object))
|
||||
.Verifiable();
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
||||
|
||||
// Act
|
||||
var displayResult = helper.DisplayFor(x => x.FormatProperty);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(displayResult));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Display_UsesTemplateNameAndAdditionalViewData()
|
||||
{
|
||||
|
|
@ -423,6 +511,29 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
Assert.Equal("SomeField", HtmlContentUtilities.HtmlContentToString(displayResult));
|
||||
}
|
||||
|
||||
|
||||
public class StatusResource
|
||||
{
|
||||
public static string FaultedKey { get { return "Faulted from ResourceType"; } }
|
||||
}
|
||||
|
||||
public enum Status : byte
|
||||
{
|
||||
[Display(Name = "CreatedKey")]
|
||||
Created,
|
||||
[Display(Name = "FaultedKey", ResourceType = typeof(StatusResource))]
|
||||
Faulted,
|
||||
Done
|
||||
}
|
||||
|
||||
public class FormatModel
|
||||
{
|
||||
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "Value: {0}")]
|
||||
public Status FormatProperty { get; set; }
|
||||
|
||||
public Status NonFormatProperty { get; set; }
|
||||
}
|
||||
|
||||
private class SomeModel
|
||||
{
|
||||
public string SomeProperty { get; set; }
|
||||
|
|
@ -432,19 +543,5 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
{
|
||||
public Status Status { get; set; }
|
||||
}
|
||||
|
||||
public class StatusResource
|
||||
{
|
||||
public static string FaultedKey { get { return "Faulted from ResourceType"; } }
|
||||
}
|
||||
|
||||
private enum Status : byte
|
||||
{
|
||||
[Display(Name = "CreatedKey")]
|
||||
Created,
|
||||
[Display(Name = "FaultedKey", ResourceType = typeof(StatusResource))]
|
||||
Faulted,
|
||||
Done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
// 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 System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||
{
|
||||
public class HtmlHelperEditorExtensionsTest
|
||||
{
|
||||
[Theory]
|
||||
[MemberData(nameof(HtmlHelperDisplayExtensionsTest.EnumUnformattedModels),
|
||||
MemberType = typeof(HtmlHelperDisplayExtensionsTest))]
|
||||
public void Display_UsesTemplateUnFormatted(HtmlHelperDisplayExtensionsTest.FormatModel model, string expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var view = new Mock<IView>();
|
||||
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||
.Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString()))
|
||||
.Returns(Task.FromResult(0));
|
||||
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
||||
viewEngine
|
||||
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
||||
viewEngine
|
||||
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "EditorTemplates/Status", /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.Found("Status", view.Object))
|
||||
.Verifiable();
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
||||
|
||||
// Act
|
||||
var displayResult = helper.EditorFor(x => x.NonFormatProperty);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(displayResult));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(HtmlHelperDisplayExtensionsTest.EnumFormatModels), MemberType = typeof(HtmlHelperDisplayExtensionsTest))]
|
||||
public void Display_UsesTemplateFormatted(HtmlHelperDisplayExtensionsTest.FormatModel model, string expectedResult)
|
||||
{
|
||||
// Arrange
|
||||
var view = new Mock<IView>();
|
||||
view.Setup(v => v.RenderAsync(It.IsAny<ViewContext>()))
|
||||
.Callback((ViewContext v) => v.Writer.WriteAsync(v.ViewData.TemplateInfo.FormattedModelValue.ToString()))
|
||||
.Returns(Task.FromResult(0));
|
||||
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
|
||||
viewEngine
|
||||
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
|
||||
viewEngine
|
||||
.Setup(v => v.FindView(It.IsAny<ActionContext>(), "EditorTemplates/Status", /*isMainPage*/ false))
|
||||
.Returns(ViewEngineResult.Found("Status", view.Object))
|
||||
.Verifiable();
|
||||
var helper = DefaultTemplatesUtilities.GetHtmlHelper(model, viewEngine.Object);
|
||||
|
||||
// Act
|
||||
var displayResult = helper.EditorFor(x => x.FormatProperty);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedResult, HtmlContentUtilities.HtmlContentToString(displayResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,11 @@ namespace HtmlGenerationWebSite.Controllers
|
|||
_productsListWithSelection = new SelectList(_products, "Number", "ProductName", 2);
|
||||
}
|
||||
|
||||
public IActionResult Enum()
|
||||
{
|
||||
return View(new AClass { DayOfWeek = Models.DayOfWeek.Friday, Month = Month.FirstOne });
|
||||
}
|
||||
|
||||
public IActionResult Order()
|
||||
{
|
||||
ViewData["Items"] = _productsListWithSelection;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// 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 System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HtmlGenerationWebSite.Models
|
||||
{
|
||||
public class AClass
|
||||
{
|
||||
public DayOfWeek DayOfWeek { get; set; }
|
||||
[DisplayFormat(DataFormatString = "Month: {0}")]
|
||||
public Month Month { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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.
|
||||
|
||||
namespace HtmlGenerationWebSite.Models
|
||||
{
|
||||
public enum DayOfWeek
|
||||
{
|
||||
Monday,
|
||||
Tuesday,
|
||||
Wednesday,
|
||||
Thursday,
|
||||
Friday,
|
||||
Saturday,
|
||||
Sunday
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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 System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HtmlGenerationWebSite.Models
|
||||
{
|
||||
public enum Month
|
||||
{
|
||||
[Display(Name = "January")]
|
||||
FirstOne,
|
||||
LastOne
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@using HtmlGenerationWebSite.Models
|
||||
@model AClass
|
||||
@Html.DisplayFor(x => x.DayOfWeek)
|
||||
@Html.DisplayFor(x => x.Month)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
@model HtmlGenerationWebSite.Models.DayOfWeek
|
||||
|
||||
@switch (Model)
|
||||
{
|
||||
case HtmlGenerationWebSite.Models.DayOfWeek.Monday:
|
||||
<text>Maandag</text>
|
||||
break;
|
||||
case HtmlGenerationWebSite.Models.DayOfWeek.Tuesday:
|
||||
<text>Dinsdag</text>
|
||||
break;
|
||||
case HtmlGenerationWebSite.Models.DayOfWeek.Wednesday:
|
||||
<text>Woensdag</text>
|
||||
break;
|
||||
case HtmlGenerationWebSite.Models.DayOfWeek.Thursday:
|
||||
<text>Donderdag</text>
|
||||
break;
|
||||
case HtmlGenerationWebSite.Models.DayOfWeek.Friday:
|
||||
<text>Vrijdag</text>
|
||||
break;
|
||||
case HtmlGenerationWebSite.Models.DayOfWeek.Saturday:
|
||||
<text>Zaterdag</text>
|
||||
break;
|
||||
case HtmlGenerationWebSite.Models.DayOfWeek.Sunday:
|
||||
<text>Zondag</text>
|
||||
break;
|
||||
}
|
||||
Loading…
Reference in New Issue