diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs
index 4c6db3048e..fdf26952a6 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentMetadata.cs
@@ -59,6 +59,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
public readonly static string ChangeAttribute = "Components.Bind.ChangeAttribute";
public readonly static string ExpressionAttribute = "Components.Bind.ExpressionAttribute";
+
+ public readonly static string IsInvariantCulture = "Components.Bind.IsInvariantCulture";
+
+ public readonly static string Format = "Components.Bind.Format";
}
public static class ChildContent
diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs
index 6ea32c7171..2a732e612c 100644
--- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs
+++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/TagHelperDescriptorExtensions.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
namespace Microsoft.AspNetCore.Razor.Language.Components
{
@@ -115,6 +116,43 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
return result;
}
+ ///
+ /// Gets a value that indicates where the tag helper is a bind tag helper with a default
+ /// culture value of .
+ ///
+ /// The .
+ ///
+ /// true if this tag helper is a bind tag helper and defaults in
+ ///
+ public static bool IsInvariantCultureBindTagHelper(this TagHelperDescriptor tagHelper)
+ {
+ if (tagHelper == null)
+ {
+ throw new ArgumentNullException(nameof(tagHelper));
+ }
+
+ return
+ tagHelper.Metadata.TryGetValue(ComponentMetadata.Bind.IsInvariantCulture, out var text) &&
+ bool.TryParse(text, out var result) &&
+ result;
+ }
+
+ ///
+ /// Gets the default format value for a bind tag helper.
+ ///
+ /// The .
+ /// The format, or null.
+ public static string GetFormat(this TagHelperDescriptor tagHelper)
+ {
+ if (tagHelper == null)
+ {
+ throw new ArgumentNullException(nameof(tagHelper));
+ }
+
+ tagHelper.Metadata.TryGetValue(ComponentMetadata.Bind.Format, out var result);
+ return result;
+ }
+
public static bool IsChildContentTagHelper(this TagHelperDescriptor tagHelper)
{
if (tagHelper == null)
diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs
index 6a9d72377b..8ad405c6b5 100644
--- a/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs
+++ b/src/Razor/Microsoft.CodeAnalysis.Razor/src/BindTagHelperDescriptorProvider.cs
@@ -72,6 +72,12 @@ namespace Microsoft.CodeAnalysis.Razor
// These mappings are also provided by attributes. Primarily these are used by
// and so we have a special case for input elements and their type attributes.
//
+ // Additionally, our mappings tell us about cases like where
+ // we need to treat the value as an invariant culture value. In general the HTML5 field
+ // types use invariant culture values when interacting with the DOM, in contrast to
+ // which is free-form text and is most likely to be
+ // culture-sensitive.
+ //
// 4. For components, we have a bit of a special case. We can infer a syntax that matches
// case #2 based on property names. So if a component provides both 'Value' and 'ValueChanged'
// we will turn that into an instance of bind.
@@ -254,6 +260,19 @@ namespace Microsoft.CodeAnalysis.Razor
(string)attribute.ConstructorArguments[2].Value,
(string)attribute.ConstructorArguments[3].Value));
}
+ else if (attribute.AttributeClass == bindInputElement && attribute.ConstructorArguments.Length == 6)
+ {
+ results.Add(new ElementBindData(
+ type.ContainingAssembly.Name,
+ type.ToDisplayString(),
+ "input",
+ (string)attribute.ConstructorArguments[0].Value,
+ (string)attribute.ConstructorArguments[1].Value,
+ (string)attribute.ConstructorArguments[2].Value,
+ (string)attribute.ConstructorArguments[3].Value,
+ (bool)attribute.ConstructorArguments[4].Value,
+ (string)attribute.ConstructorArguments[5].Value));
+ }
}
}
@@ -287,6 +306,8 @@ namespace Microsoft.CodeAnalysis.Razor
builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.Bind.RuntimeName;
builder.Metadata[ComponentMetadata.Bind.ValueAttribute] = entry.ValueAttribute;
builder.Metadata[ComponentMetadata.Bind.ChangeAttribute] = entry.ChangeAttribute;
+ builder.Metadata[ComponentMetadata.Bind.IsInvariantCulture] = entry.IsInvariantCulture ? bool.TrueString : bool.FalseString;
+ builder.Metadata[ComponentMetadata.Bind.Format] = entry.Format;
if (entry.TypeAttribute != null)
{
@@ -512,7 +533,9 @@ namespace Microsoft.CodeAnalysis.Razor
string typeAttribute,
string suffix,
string valueAttribute,
- string changeAttribute)
+ string changeAttribute,
+ bool isInvariantCulture = false,
+ string format = null)
{
Assembly = assembly;
TypeName = typeName;
@@ -521,6 +544,8 @@ namespace Microsoft.CodeAnalysis.Razor
Suffix = suffix;
ValueAttribute = valueAttribute;
ChangeAttribute = changeAttribute;
+ IsInvariantCulture = isInvariantCulture;
+ Format = format;
}
public string Assembly { get; }
@@ -530,6 +555,8 @@ namespace Microsoft.CodeAnalysis.Razor
public string Suffix { get; }
public string ValueAttribute { get; }
public string ChangeAttribute { get; }
+ public bool IsInvariantCulture { get; }
+ public string Format { get; }
}
private class BindElementDataVisitor : SymbolVisitor
diff --git a/src/Razor/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs b/src/Razor/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs
index 328808dd40..e037ab2fd1 100644
--- a/src/Razor/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs
+++ b/src/Razor/Microsoft.CodeAnalysis.Razor/test/BindTagHelperDescriptorProviderTest.cs
@@ -649,6 +649,8 @@ namespace Test
Assert.Equal("checkbox", bind.Metadata[ComponentMetadata.Bind.TypeAttribute]);
Assert.True(bind.IsInputElementBindTagHelper());
Assert.False(bind.IsInputElementFallbackBindTagHelper());
+ Assert.False(bind.IsInvariantCultureBindTagHelper());
+ Assert.Null(bind.GetFormat());
var rule = Assert.Single(bind.TagMatchingRules);
Assert.Equal("input", rule.TagName);
@@ -681,6 +683,46 @@ namespace Test
Assert.Equal(":format", parameter.DisplayName);
}
+ [Fact]
+ public void Execute_BindOnInputElementWithTypeAttributeAndSuffixAndInvariantCultureAndFormat_CreatesDescriptor()
+ {
+ // Arrange
+ var compilation = BaseCompilation.AddSyntaxTrees(Parse(@"
+using Microsoft.AspNetCore.Components;
+
+namespace Test
+{
+ [BindInputElement(""number"", null, ""value"", ""onchange"", isInvariantCulture: true, format: ""0.00"")]
+ public class BindAttributes
+ {
+ }
+}
+"));
+
+ Assert.Empty(compilation.GetDiagnostics());
+
+ var context = TagHelperDescriptorProviderContext.Create();
+ context.SetCompilation(compilation);
+
+ var provider = new BindTagHelperDescriptorProvider();
+
+ // Act
+ provider.Execute(context);
+
+ // Assert
+ var matches = GetBindTagHelpers(context);
+ matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
+ var bind = Assert.Single(matches);
+
+ Assert.Equal("value", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]);
+ Assert.Equal("onchange", bind.Metadata[ComponentMetadata.Bind.ChangeAttribute]);
+ Assert.Equal("number", bind.Metadata[ComponentMetadata.Bind.TypeAttribute]);
+ Assert.True(bind.IsInputElementBindTagHelper());
+ Assert.False(bind.IsInputElementFallbackBindTagHelper());
+ Assert.True(bind.IsInvariantCultureBindTagHelper());
+ Assert.Equal("0.00", bind.GetFormat());
+ }
+
[Fact]
public void Execute_BindFallback_CreatesDescriptor()
{
diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components.netstandard2.0.cs
index 92b9f1f55d..caa5697b2b 100644
--- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components.netstandard2.0.cs
+++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components.netstandard2.0.cs
@@ -38,10 +38,13 @@ namespace Microsoft.AspNetCore.Components
public sealed partial class BindInputElementAttribute : System.Attribute
{
public BindInputElementAttribute(string type, string suffix, string valueAttribute, string changeAttribute) { }
+ public BindInputElementAttribute(string type, string suffix, string valueAttribute, string changeAttribute, bool isInvariantCulture, string format) { }
public string ChangeAttribute { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string Suffix { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public string ValueAttribute { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
+ public bool IsInvariantCulture { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
+ public string Format { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
public static partial class BindMethods
{