Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
TagHelpers attribute targeting - part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
ajaybhargavb committed Mar 20, 2015
1 parent bfdeda7 commit e205890
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 178 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,6 @@
<data name="InputTagHelper_ValueRequired" xml:space="preserve">
<value>'{1}' must not be null for {0} if '{2}' is '{3}'.</value>
</data>
<data name="SelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified" xml:space="preserve">
<value>Cannot determine body for {0}. '{2}' must be null if '{1}' is null.</value>
</data>
<data name="TagHelpers_NoProvidedMetadata" xml:space="preserve">
<value>The {2} was unable to provide metadata about '{1}' expression value '{3}' for {0}.</value>
</data>
Expand Down
85 changes: 35 additions & 50 deletions src/Microsoft.AspNet.Mvc.TagHelpers/SelectTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <summary>
/// <see cref="ITagHelper"/> implementation targeting &lt;select&gt; elements with an <c>asp-for</c> attribute.
/// </summary>
[TargetElement("select", Attributes = ForAttributeName)]
public class SelectTagHelper : TagHelper
{
private const string ForAttributeName = "asp-for";
Expand Down Expand Up @@ -57,63 +58,47 @@ public class SelectTagHelper : TagHelper
/// </exception>
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (For == null)
// Note null or empty For.Name is allowed because TemplateInfo.HtmlFieldPrefix may be sufficient.
// IHtmlGenerator will enforce name requirements.
var metadata = For.Metadata;
if (metadata == null)
{
// Regular HTML <select/> element. Just make sure Items wasn't specified.
if (Items != null)
{
var message = Resources.FormatSelectTagHelper_CannotDetermineContentWhenOnlyItemsSpecified(
"<select>",
ForAttributeName,
ItemsAttributeName);
throw new InvalidOperationException(message);
}
throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata(
"<select>",
ForAttributeName,
nameof(IModelMetadataProvider),
For.Name));
}
else
{
// Note null or empty For.Name is allowed because TemplateInfo.HtmlFieldPrefix may be sufficient.
// IHtmlGenerator will enforce name requirements.
var metadata = For.Metadata;
if (metadata == null)
{
throw new InvalidOperationException(Resources.FormatTagHelpers_NoProvidedMetadata(
"<select>",
ForAttributeName,
nameof(IModelMetadataProvider),
For.Name));
}

// Base allowMultiple on the instance or declared type of the expression to avoid a
// "SelectExpressionNotEnumerable" InvalidOperationException during generation.
// Metadata.IsCollectionType() is similar but does not take runtime type into account.
var realModelType = For.ModelExplorer.ModelType;
var allowMultiple =
typeof(string) != realModelType && typeof(IEnumerable).IsAssignableFrom(realModelType);

// Ensure GenerateSelect() _never_ looks anything up in ViewData.
var items = Items ?? Enumerable.Empty<SelectListItem>();
// Base allowMultiple on the instance or declared type of the expression to avoid a
// "SelectExpressionNotEnumerable" InvalidOperationException during generation.
// Metadata.IsCollectionType() is similar but does not take runtime type into account.
var realModelType = For.ModelExplorer.ModelType;
var allowMultiple = typeof(string) != realModelType && typeof(IEnumerable).IsAssignableFrom(realModelType);

ICollection<string> selectedValues;
var tagBuilder = Generator.GenerateSelect(
ViewContext,
For.ModelExplorer,
optionLabel: null,
expression: For.Name,
selectList: items,
allowMultiple: allowMultiple,
htmlAttributes: null,
selectedValues: out selectedValues);
// Ensure GenerateSelect() _never_ looks anything up in ViewData.
var items = Items ?? Enumerable.Empty<SelectListItem>();

if (tagBuilder != null)
{
output.MergeAttributes(tagBuilder);
output.PostContent.Append(tagBuilder.InnerHtml);
}
ICollection<string> selectedValues;
var tagBuilder = Generator.GenerateSelect(
ViewContext,
For.ModelExplorer,
optionLabel: null,
expression: For.Name,
selectList: items,
allowMultiple: allowMultiple,
htmlAttributes: null,
selectedValues: out selectedValues);

// Whether or not (not being highly unlikely) we generate anything, could update contained <option/>
// elements. Provide selected values for <option/> tag helpers. They'll run next.
ViewContext.FormContext.FormData[SelectedValuesFormDataKey] = selectedValues;
if (tagBuilder != null)
{
output.MergeAttributes(tagBuilder);
output.PostContent.Append(tagBuilder.InnerHtml);
}

// Whether or not (not being highly unlikely) we generate anything, could update contained <option/>
// elements. Provide selected values for <option/> tag helpers. They'll run next.
ViewContext.FormContext.FormData[SelectedValuesFormDataKey] = selectedValues;
}
}
}
31 changes: 14 additions & 17 deletions src/Microsoft.AspNet.Mvc.TagHelpers/TextAreaTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
/// <summary>
/// <see cref="ITagHelper"/> implementation targeting &lt;textarea&gt; elements with an <c>asp-for</c> attribute.
/// </summary>
[TargetElement("textarea")]
[TargetElement("textarea", Attributes = ForAttributeName)]
public class TextAreaTagHelper : TagHelper
{
private const string ForAttributeName = "asp-for";
Expand All @@ -33,23 +33,20 @@ public class TextAreaTagHelper : TagHelper
/// <remarks>Does nothing if <see cref="For"/> is <c>null</c>.</remarks>
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (For != null)
var tagBuilder = Generator.GenerateTextArea(
ViewContext,
For.ModelExplorer,
For.Name,
rows: 0,
columns: 0,
htmlAttributes: null);

if (tagBuilder != null)
{
var tagBuilder = Generator.GenerateTextArea(
ViewContext,
For.ModelExplorer,
For.Name,
rows: 0,
columns: 0,
htmlAttributes: null);

if (tagBuilder != null)
{
// Overwrite current Content to ensure expression result round-trips correctly.
output.Content.SetContent(tagBuilder.InnerHtml);

output.MergeAttributes(tagBuilder);
}
// Overwrite current Content to ensure expression result round-trips correctly.
output.Content.SetContent(tagBuilder.InnerHtml);

output.MergeAttributes(tagBuilder);
}
}
}
Expand Down
31 changes: 0 additions & 31 deletions test/Microsoft.AspNet.Mvc.TagHelpers.Test/SelectTagHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -603,37 +603,6 @@ public async Task TagHelper_CallsGeneratorWithExpectedValues_RealModelType(
Assert.Same(selectedValues, keyValuePair.Value);
}

[Fact]
public async Task ProcessAsync_Throws_IfForNotBoundButItemsIs()
{
// Arrange
var contextAttributes = new Dictionary<string, object>();
var originalAttributes = new Dictionary<string, string>();
var expectedTagName = "select";
var expectedMessage = "Cannot determine body for <select>. 'asp-items' must be null if 'asp-for' is null.";

var tagHelperContext = new TagHelperContext(
contextAttributes,
items: new Dictionary<object, object>(),
uniqueId: "test",
getChildContentAsync: () =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent);
});
var output = new TagHelperOutput(expectedTagName, originalAttributes);
var tagHelper = new SelectTagHelper
{
Items = Enumerable.Empty<SelectListItem>(),
};

// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => tagHelper.ProcessAsync(tagHelperContext, output));
Assert.Equal(expectedMessage, exception.Message);
}

public class NameAndId
{
public NameAndId(string name, string id)
Expand Down
61 changes: 0 additions & 61 deletions test/Microsoft.AspNet.Mvc.TagHelpers.Test/TextAreaTagHelperTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,67 +154,6 @@ public async Task Process_GeneratesExpectedOutput(
Assert.Equal(expectedTagName, output.TagName);
}

[Fact]
public async Task TagHelper_LeavesOutputUnchanged_IfForNotBound()
{
// Arrange
var expectedAttributes = new Dictionary<string, string>
{
{ "class", "form-control" },
};
var expectedPreContent = "original pre-content";
var expectedContent = "original content";
var expectedPostContent = "original post-content";
var expectedTagName = "textarea";

var metadataProvider = new TestModelMetadataProvider();
var modelExplorer = metadataProvider
.GetModelExplorerForType(typeof(Model), model: null)
.GetExplorerForProperty(nameof(Model.Text));

var modelExpression = new ModelExpression(nameof(Model.Text), modelExplorer);
var tagHelper = new TextAreaTagHelper();

var tagHelperContext = new TagHelperContext(
allAttributes: new Dictionary<string, object>(),
items: new Dictionary<object, object>(),
uniqueId: "test",
getChildContentAsync: () =>
{
var tagHelperContent = new DefaultTagHelperContent();
tagHelperContent.SetContent("Something");
return Task.FromResult<TagHelperContent>(tagHelperContent);
});
var output = new TagHelperOutput(expectedTagName, expectedAttributes)
{
SelfClosing = true,
};
output.PreContent.SetContent(expectedPreContent);
output.Content.SetContent(expectedContent);
output.PostContent.SetContent(expectedPostContent);

var htmlGenerator = new TestableHtmlGenerator(metadataProvider)
{
ValidationAttributes =
{
{ "valid", "from validation attributes" },
}
};
Model model = null;
var viewContext = TestableHtmlGenerator.GetViewContext(model, htmlGenerator, metadataProvider);
tagHelper.ViewContext = viewContext;
tagHelper.Generator = htmlGenerator;

// Act
await tagHelper.ProcessAsync(tagHelperContext, output);

// Assert
Assert.Equal(expectedAttributes, output.Attributes);
Assert.Equal(expectedContent, output.Content.GetContent());
Assert.True(output.SelfClosing);
Assert.Equal(expectedTagName, output.TagName);
}

public class NameAndId
{
public NameAndId(string name, string id)
Expand Down

0 comments on commit e205890

Please sign in to comment.