[Fixes #2337] Added support for file types in input taghelper and

htmlhelper
This commit is contained in:
Ajay Bhargav Baaskaran 2015-04-08 16:54:48 -07:00
parent a9f2c937df
commit 4951235eef
14 changed files with 297 additions and 107 deletions

View File

@ -8,6 +8,7 @@ using System.Globalization;
using System.Text; using System.Text;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering.Internal;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Mvc.Rendering namespace Microsoft.AspNet.Mvc.Rendering

View File

@ -5,11 +5,12 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Text; using System.Text;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering.Internal;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Rendering namespace Microsoft.AspNet.Mvc.Rendering
{ {
@ -374,6 +375,20 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateTextBox(htmlHelper, inputType: "number"); return GenerateTextBox(htmlHelper, inputType: "number");
} }
public static string FileInputTemplate([NotNull] IHtmlHelper htmlHelper)
{
return GenerateTextBox(htmlHelper, inputType: "file");
}
public static string FileCollectionInputTemplate([NotNull] IHtmlHelper htmlHelper)
{
var htmlAttributes =
CreateHtmlAttributes(htmlHelper, className: "text-box single-line", inputType: "file");
htmlAttributes["multiple"] = "multiple";
return GenerateTextBox(htmlHelper, htmlHelper.ViewData.TemplateInfo.FormattedModelValue, htmlAttributes);
}
private static void ApplyRfc3339DateFormattingIfNeeded(IHtmlHelper htmlHelper, string format) private static void ApplyRfc3339DateFormattingIfNeeded(IHtmlHelper htmlHelper, string format)
{ {
if (htmlHelper.Html5DateRenderingMode != Html5DateRenderingMode.Rfc3339) if (htmlHelper.Html5DateRenderingMode != Html5DateRenderingMode.Rfc3339)
@ -405,6 +420,11 @@ namespace Microsoft.AspNet.Mvc.Rendering
var htmlAttributes = var htmlAttributes =
CreateHtmlAttributes(htmlHelper, className: "text-box single-line", inputType: inputType); CreateHtmlAttributes(htmlHelper, className: "text-box single-line", inputType: inputType);
return GenerateTextBox(htmlHelper, value, htmlAttributes);
}
private static string GenerateTextBox(IHtmlHelper htmlHelper, object value, object htmlAttributes)
{
return htmlHelper.TextBox( return htmlHelper.TextBox(
current: null, current: null,
value: value, value: value,

View File

@ -11,6 +11,7 @@ using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Rendering.Expressions; using Microsoft.AspNet.Mvc.Rendering.Expressions;
using Microsoft.AspNet.Mvc.Rendering.Internal;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders; using Microsoft.Framework.WebEncoders;

View File

@ -5,9 +5,9 @@ using System.Globalization;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Rendering namespace Microsoft.AspNet.Mvc.Rendering.Internal
{ {
internal class TemplateBuilder public class TemplateBuilder
{ {
private IViewEngine _viewEngine; private IViewEngine _viewEngine;
private ViewContext _viewContext; private ViewContext _viewContext;

View File

@ -7,16 +7,19 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.Rendering namespace Microsoft.AspNet.Mvc.Rendering.Internal
{ {
internal class TemplateRenderer public class TemplateRenderer
{ {
private static readonly string DisplayTemplateViewPath = "DisplayTemplates"; private const string DisplayTemplateViewPath = "DisplayTemplates";
private static readonly string EditorTemplateViewPath = "EditorTemplates"; private const string EditorTemplateViewPath = "EditorTemplates";
public const string IEnumerableOfIFormFileName = "IEnumerable`" + nameof(IFormFile);
private static readonly Dictionary<string, Func<IHtmlHelper, string>> _defaultDisplayActions = private static readonly Dictionary<string, Func<IHtmlHelper, string>> _defaultDisplayActions =
new Dictionary<string, Func<IHtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) new Dictionary<string, Func<IHtmlHelper, string>>(StringComparer.OrdinalIgnoreCase)
@ -62,6 +65,8 @@ namespace Microsoft.AspNet.Mvc.Rendering
{ typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate }, { typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate },
{ typeof(string).Name, DefaultEditorTemplates.StringTemplate }, { typeof(string).Name, DefaultEditorTemplates.StringTemplate },
{ typeof(object).Name, DefaultEditorTemplates.ObjectTemplate }, { typeof(object).Name, DefaultEditorTemplates.ObjectTemplate },
{ typeof(IFormFile).Name, DefaultEditorTemplates.FileInputTemplate },
{ IEnumerableOfIFormFileName, DefaultEditorTemplates.FileCollectionInputTemplate },
}; };
private ViewContext _viewContext; private ViewContext _viewContext;
@ -136,7 +141,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
metadata.DataTypeName metadata.DataTypeName
}; };
foreach (string templateHint in templateHints.Where(s => !string.IsNullOrEmpty(s))) foreach (var templateHint in templateHints.Where(s => !string.IsNullOrEmpty(s)))
{ {
yield return templateHint; yield return templateHint;
} }
@ -146,14 +151,27 @@ namespace Microsoft.AspNet.Mvc.Rendering
var modelType = _viewData.ModelExplorer.ModelType; var modelType = _viewData.ModelExplorer.ModelType;
var fieldType = Nullable.GetUnderlyingType(modelType) ?? modelType; var fieldType = Nullable.GetUnderlyingType(modelType) ?? modelType;
yield return fieldType.Name; foreach (var typeName in GetTypeNames(_viewData.ModelExplorer.Metadata, fieldType))
{
yield return typeName;
}
}
public static IEnumerable<string> GetTypeNames(ModelMetadata modelMetadata, Type fieldType)
{
// Not returning type name here for IEnumerable<IFormFile> since we will be returning
// a more specific name, IEnumerableOfIFormFileName.
if (typeof(IEnumerable<IFormFile>) != fieldType)
{
yield return fieldType.Name;
}
if (fieldType == typeof(string)) if (fieldType == typeof(string))
{ {
// Nothing more to provide // Nothing more to provide
yield break; yield break;
} }
else if (!metadata.IsComplexType) else if (!modelMetadata.IsComplexType)
{ {
// IsEnum is false for the Enum class itself // IsEnum is false for the Enum class itself
if (fieldType.IsEnum()) if (fieldType.IsEnum())
@ -167,36 +185,44 @@ namespace Microsoft.AspNet.Mvc.Rendering
} }
yield return "String"; yield return "String";
yield break;
} }
else if (fieldType.IsInterface()) else if (!fieldType.IsInterface())
{ {
if (typeof(IEnumerable).IsAssignableFrom(fieldType)) var type = fieldType;
{
yield return "Collection";
}
yield return "Object";
}
else
{
var isEnumerable = typeof(IEnumerable).IsAssignableFrom(fieldType);
while (true) while (true)
{ {
fieldType = fieldType.BaseType(); type = type.BaseType();
if (fieldType == null) if (type == null || type == typeof(object))
{ {
break; break;
} }
if (isEnumerable && fieldType == typeof(Object)) yield return type.Name;
{
yield return "Collection";
}
yield return fieldType.Name;
} }
} }
if (typeof(IEnumerable).IsAssignableFrom(fieldType))
{
if (typeof(IEnumerable<IFormFile>).IsAssignableFrom(fieldType))
{
yield return IEnumerableOfIFormFileName;
// Specific name has already been returned, now return the generic name.
if (typeof(IEnumerable<IFormFile>) == fieldType)
{
yield return fieldType.Name;
}
}
yield return "Collection";
}
else if (typeof(IFormFile) != fieldType && typeof(IFormFile).IsAssignableFrom(fieldType))
{
yield return nameof(IFormFile);
}
yield return "Object";
} }
private static IHtmlHelper MakeHtmlHelper(ViewContext viewContext, ViewDataDictionary viewData) private static IHtmlHelper MakeHtmlHelper(ViewContext viewContext, ViewDataDictionary viewData)

View File

@ -2,11 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.Rendering.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace Microsoft.AspNet.Mvc.TagHelpers namespace Microsoft.AspNet.Mvc.TagHelpers
@ -47,6 +48,8 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
{ nameof(Boolean), InputType.CheckBox.ToString().ToLowerInvariant() }, { nameof(Boolean), InputType.CheckBox.ToString().ToLowerInvariant() },
{ nameof(Decimal), InputType.Text.ToString().ToLowerInvariant() }, { nameof(Decimal), InputType.Text.ToString().ToLowerInvariant() },
{ nameof(String), InputType.Text.ToString().ToLowerInvariant() }, { nameof(String), InputType.Text.ToString().ToLowerInvariant() },
{ nameof(IFormFile), "file" },
{ TemplateRenderer.IEnumerableOfIFormFileName, "file" },
}; };
// Mapping from <input/> element's type to RFC 3339 date and time formats. // Mapping from <input/> element's type to RFC 3339 date and time formats.
@ -271,13 +274,22 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
format = GetFormat(modelExplorer, inputTypeHint, inputType); format = GetFormat(modelExplorer, inputTypeHint, inputType);
} }
object htmlAttributes = null;
if (string.Equals(inputType, "file") && string.Equals(inputTypeHint, TemplateRenderer.IEnumerableOfIFormFileName))
{
htmlAttributes = new Dictionary<string, object>
{
{ "multiple", "multiple" }
};
}
return Generator.GenerateTextBox( return Generator.GenerateTextBox(
ViewContext, ViewContext,
modelExplorer, modelExplorer,
For.Name, For.Name,
value: modelExplorer.Model, value: modelExplorer.Model,
format: format, format: format,
htmlAttributes: null); htmlAttributes: htmlAttributes);
} }
// Get a fall-back format based on the metadata. // Get a fall-back format based on the metadata.
@ -353,55 +365,9 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
} }
} }
yield return fieldType.Name; foreach (string typeName in TemplateRenderer.GetTypeNames(modelExplorer.Metadata, fieldType))
if (fieldType == typeof(string))
{ {
// Nothing more to provide yield return typeName;
yield break;
}
else if (!modelExplorer.Metadata.IsComplexType)
{
// IsEnum is false for the Enum class itself
if (fieldType.IsEnum())
{
// Same as fieldType.BaseType.Name in this case
yield return "Enum";
}
else if (fieldType == typeof(DateTimeOffset))
{
yield return "DateTime";
}
yield return "String";
}
else if (fieldType.IsInterface())
{
if (typeof(IEnumerable).IsAssignableFrom(fieldType))
{
yield return "Collection";
}
yield return "Object";
}
else
{
var isEnumerable = typeof(IEnumerable).IsAssignableFrom(fieldType);
while (true)
{
fieldType = fieldType.BaseType();
if (fieldType == null)
{
break;
}
if (isEnumerable && fieldType == typeof(Object))
{
yield return "Collection";
}
yield return fieldType.Name;
}
} }
} }
} }

View File

@ -8,9 +8,11 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.Rendering.Internal;
using Microsoft.AspNet.Testing; using Microsoft.AspNet.Testing;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
using Microsoft.Framework.WebEncoders; using Microsoft.Framework.WebEncoders;
@ -78,6 +80,9 @@ namespace Microsoft.AspNet.Mvc.Core
{ "decimal", "__TextBox__ class='text-box single-line'" }, { "decimal", "__TextBox__ class='text-box single-line'" },
{ "String", "__TextBox__ class='text-box single-line'" }, { "String", "__TextBox__ class='text-box single-line'" },
{ "STRING", "__TextBox__ class='text-box single-line'" }, { "STRING", "__TextBox__ class='text-box single-line'" },
{ typeof(IFormFile).Name, "__TextBox__ class='text-box single-line' type='file'" },
{ TemplateRenderer.IEnumerableOfIFormFileName,
"__TextBox__ class='text-box single-line' type='file' multiple='multiple'" },
}; };
} }
} }

View File

@ -0,0 +1,67 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Http.Core.Collections;
using Microsoft.AspNet.Mvc.ModelBinding;
using Xunit;
namespace Microsoft.AspNet.Mvc.Rendering.Internal
{
public class TemplateRendererTest
{
public static TheoryData<Type, string[]> TypeNameData
{
get
{
return new TheoryData<Type, string[]>
{
{ typeof(string), new string[] { "String" } },
{ typeof(bool), new string[] { "Boolean", "String" } },
{ typeof(DateTime), new string[] { "DateTime", "String" } },
{ typeof(float), new string[] { "Single", "String" } },
{ typeof(double), new string[] { "Double", "String" } },
{ typeof(Guid), new string[] { "Guid", "String" } },
{ typeof(TimeSpan), new string[] { "TimeSpan", "String" } },
{ typeof(int), new string[] { "Int32", "String" } },
{ typeof(ulong), new string[] { "UInt64", "String" } },
{ typeof(Enum), new string[] { "Enum", "String" } },
{ typeof(HttpStatusCode), new string[] { "HttpStatusCode", "Enum", "String" } },
{ typeof(FormFile), new string[] { "FormFile", "IFormFile", "Object" } },
{ typeof(IFormFile), new string[] { "IFormFile", "Object" } },
{ typeof(FormFileCollection), new string[] { "FormFileCollection", typeof(List<IFormFile>).Name,
TemplateRenderer.IEnumerableOfIFormFileName, "Collection", "Object" } },
{ typeof(IFormFileCollection), new string[] { "IFormFileCollection",
TemplateRenderer.IEnumerableOfIFormFileName, "Collection", "Object" } },
{ typeof(IEnumerable<IFormFile>), new string[] { TemplateRenderer.IEnumerableOfIFormFileName,
typeof(IEnumerable<IFormFile>).Name, "Collection", "Object" } },
};
}
}
[Theory]
[MemberData(nameof(TypeNameData))]
public void GetTypeNames_ReturnsExpectedResults(Type fieldType, string[] expectedResult)
{
// Arrange
var metadataProvider = new TestModelMetadataProvider();
var metadata = metadataProvider.GetMetadataForType(fieldType);
// Act
var typeNames = TemplateRenderer.GetTypeNames(metadata, fieldType);
// Assert
var collectionAssertions = expectedResult.Select<string, Action<string>>(expected =>
actual => Assert.Equal(expected, actual));
Assert.Collection(typeNames, collectionAssertions.ToArray());
}
}
}

View File

@ -47,6 +47,8 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
[InlineData("Link", null)] [InlineData("Link", null)]
// Testing the ScriptTagHelper // Testing the ScriptTagHelper
[InlineData("Script", null)] [InlineData("Script", null)]
// Testing InputTagHelper with File
[InlineData("Input", null)]
public async Task MvcTagHelpers_GeneratesExpectedResults(string action, string antiForgeryPath) public async Task MvcTagHelpers_GeneratesExpectedResults(string action, string antiForgeryPath)
{ {
// Arrange // Arrange

View File

@ -0,0 +1,22 @@
<html>
<head>
<meta charset="utf-8" />
<title>File Input</title>
</head>
<body>
<h2>Input Tag Helper Test</h2>
<input name="InterfaceFile" type="file" id="InterfaceFile" value="" />
<input name="InterfaceFiles" type="file" id="InterfaceFiles" multiple="multiple" value="" />
<input name="ConcreteFile" type="file" id="ConcreteFile" value="" />
<input name="ConcreteFiles" type="file" id="ConcreteFiles" multiple="multiple" value="" />
<input name="EnumerableFiles" type="file" id="EnumerableFiles" multiple="multiple" value="" />
<input class="text-box single-line" id="InterfaceFile" name="InterfaceFile" type="file" value="" />
<input class="text-box single-line" id="InterfaceFiles" multiple="multiple" name="InterfaceFiles" type="file" value="" />
<input class="text-box single-line" id="ConcreteFile" name="ConcreteFile" type="file" value="" />
<input class="text-box single-line" id="ConcreteFiles" multiple="multiple" name="ConcreteFiles" type="file" value="" />
<input class="text-box single-line" id="EnumerableFiles" multiple="multiple" name="EnumerableFiles" type="file" value="" />
</body>
</html>

View File

@ -5,8 +5,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.Rendering.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.WebEncoders; using Microsoft.Framework.WebEncoders;
using Moq; using Moq;
@ -618,33 +620,48 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
Assert.Equal(expectedTagName, output.TagName); Assert.Equal(expectedTagName, output.TagName);
} }
public static TheoryData<string, string, string> InputTypeData
{
get
{
return new TheoryData<string, string, string>
{
{ null, null, "text" },
{ "Byte", null, "number" },
{ null, null, "text" },
{ "Byte", null, "number" },
{ "custom-datatype", null, "text" },
{ "Custom-Datatype", null, "text" },
{ "date", null, "date" }, // No date/time special cases since ModelType is string.
{ "datetime", null, "datetime" },
{ "datetime-local", null, "datetime-local" },
{ "DATETIME-local", null, "datetime-local" },
{ "Decimal", "{0:0.00}", "text" },
{ "Double", null, "number" },
{ "Int16", null, "number" },
{ "Int32", null, "number" },
{ "int32", null, "number" },
{ "Int64", null, "number" },
{ "SByte", null, "number" },
{ "Single", null, "number" },
{ "SINGLE", null, "number" },
{ "string", null, "text" },
{ "STRING", null, "text" },
{ "text", null, "text" },
{ "TEXT", null, "text" },
{ "time", null, "time" },
{ "UInt16", null, "number" },
{ "uint16", null, "number" },
{ "UInt32", null, "number" },
{ "UInt64", null, "number" },
{ nameof(IFormFile), null, "file" },
{ TemplateRenderer.IEnumerableOfIFormFileName, null, "file" },
};
}
}
[Theory] [Theory]
[InlineData(null, null, "text")] [MemberData(nameof(InputTypeData))]
[InlineData("Byte", null, "number")]
[InlineData("custom-datatype", null, "text")]
[InlineData("Custom-Datatype", null, "text")]
[InlineData("date", null, "date")] // No date/time special cases since ModelType is string.
[InlineData("datetime", null, "datetime")]
[InlineData("datetime-local", null, "datetime-local")]
[InlineData("DATETIME-local", null, "datetime-local")]
[InlineData("Decimal", "{0:0.00}", "text")]
[InlineData("Double", null, "number")]
[InlineData("Int16", null, "number")]
[InlineData("Int32", null, "number")]
[InlineData("int32", null, "number")]
[InlineData("Int64", null, "number")]
[InlineData("SByte", null, "number")]
[InlineData("Single", null, "number")]
[InlineData("SINGLE", null, "number")]
[InlineData("string", null, "text")]
[InlineData("STRING", null, "text")]
[InlineData("text", null, "text")]
[InlineData("TEXT", null, "text")]
[InlineData("time", null, "time")]
[InlineData("UInt16", null, "number")]
[InlineData("uint16", null, "number")]
[InlineData("UInt32", null, "number")]
[InlineData("UInt64", null, "number")]
public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributes( public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributes(
string dataTypeName, string dataTypeName,
string expectedFormat, string expectedFormat,
@ -679,6 +696,15 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
metadataProvider: metadataProvider); metadataProvider: metadataProvider);
var tagBuilder = new TagBuilder("input", new HtmlEncoder()); var tagBuilder = new TagBuilder("input", new HtmlEncoder());
Dictionary<string, object> htmlAttributes = null;
if (string.Equals(dataTypeName, TemplateRenderer.IEnumerableOfIFormFileName))
{
htmlAttributes = new Dictionary<string, object>
{
{ "multiple", "multiple" }
};
}
htmlGenerator htmlGenerator
.Setup(mock => mock.GenerateTextBox( .Setup(mock => mock.GenerateTextBox(
tagHelper.ViewContext, tagHelper.ViewContext,
@ -686,7 +712,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
tagHelper.For.Name, tagHelper.For.Name,
null, // value null, // value
expectedFormat, expectedFormat,
null)) // htmlAttributes htmlAttributes)) // htmlAttributes
.Returns(tagBuilder) .Returns(tagBuilder)
.Verifiable(); .Verifiable();

View File

@ -171,5 +171,10 @@ namespace MvcTagHelpersWebSite.Controllers
{ {
return View(); return View();
} }
public IActionResult Input()
{
return View();
}
} }
} }

View File

@ -0,0 +1,23 @@
// 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.Collections.Generic;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Http.Core.Collections;
namespace MvcTagHelpersWebSite.Models
{
public class Folder
{
public IFormFile InterfaceFile { get; set; }
public IFormFileCollection InterfaceFiles { get; set; }
public FormFile ConcreteFile { get; set; }
public FormFileCollection ConcreteFiles { get; set; }
public IEnumerable<IFormFile> EnumerableFiles { get; set; }
}
}

View File

@ -0,0 +1,26 @@
@using MvcTagHelpersWebSite.Models
@model Folder
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
<html>
<head>
<meta charset="utf-8" />
<title>File Input</title>
</head>
<body>
<h2>Input Tag Helper Test</h2>
<input asp-for="InterfaceFile" name="InterfaceFile" />
<input asp-for="InterfaceFiles" name="InterfaceFiles" />
<input asp-for="ConcreteFile" name="ConcreteFile" />
<input asp-for="ConcreteFiles" name="ConcreteFiles" />
<input asp-for="EnumerableFiles" name="EnumerableFiles" />
@Html.EditorFor(m => m.InterfaceFile)
@Html.EditorFor(m => m.InterfaceFiles)
@Html.EditorFor(m => m.ConcreteFile)
@Html.EditorFor(m => m.ConcreteFiles)
@Html.EditorFor(m => m.EnumerableFiles)
</body>
</html>