This repository has been archived by the owner on Dec 14, 2018. It is now read-only.
/
OptionTagHelper.cs
130 lines (115 loc) · 5.21 KB
/
OptionTagHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// 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;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.TagHelpers.Internal;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Mvc.TagHelpers
{
/// <summary>
/// <see cref="ITagHelper"/> implementation targeting <option> elements.
/// </summary>
/// <remarks>
/// This <see cref="ITagHelper"/> works in conjunction with <see cref="SelectTagHelper"/>. It reads elements
/// content but does not modify that content. The only modification it makes is to add a <c>selected</c> attribute
/// in some cases.
/// </remarks>
public class OptionTagHelper : TagHelper
{
/// <summary>
/// Creates a new <see cref="OptionTagHelper"/>.
/// </summary>
/// <param name="generator">The <see cref="IHtmlGenerator"/>.</param>
public OptionTagHelper(IHtmlGenerator generator)
{
Generator = generator;
}
/// <inheritdoc />
public override int Order => -1000;
protected IHtmlGenerator Generator { get; }
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
/// <summary>
/// Specifies a value for the <option> element.
/// </summary>
/// <remarks>
/// Passed through to the generated HTML in all cases.
/// </remarks>
public string Value { get; set; }
/// <inheritdoc />
/// <remarks>
/// Does nothing unless <see cref="TagHelperContext.Items"/> contains a
/// <see cref="SelectTagHelper"/> <see cref="Type"/> entry and that entry is a non-<c>null</c>
/// <see cref="CurrentValues"/> instance. Also does nothing if the associated <option> is already
/// selected.
/// </remarks>
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
// Pass through attributes that are also well-known HTML attributes.
if (Value != null)
{
output.CopyHtmlAttribute(nameof(Value), context);
}
// Nothing to do if this <option/> is already selected.
if (!output.Attributes.ContainsName("selected"))
{
// Is this <option/> element a child of a <select/> element the SelectTagHelper targeted?
object formDataEntry;
context.Items.TryGetValue(typeof(SelectTagHelper), out formDataEntry);
// ... And did the SelectTagHelper determine any selected values?
var currentValues = formDataEntry as CurrentValues;
if (currentValues?.Values != null && currentValues.Values.Count != 0)
{
// Select this <option/> element if value attribute or (if no value attribute) body matches a
// selected value. Body is encoded as-needed while executing child content. But TagHelperOutput
// itself encodes attribute values later, when start tag is generated.
bool selected;
if (Value != null)
{
selected = currentValues.Values.Contains(Value);
}
else
{
if (currentValues.ValuesAndEncodedValues == null)
{
// Include encoded versions of all selected values when comparing with body.
var allValues = new HashSet<string>(currentValues.Values, StringComparer.OrdinalIgnoreCase);
foreach (var selectedValue in currentValues.Values)
{
allValues.Add(Generator.Encode(selectedValue));
}
currentValues.ValuesAndEncodedValues = allValues;
}
TagHelperContent childContent;
if (output.IsContentModified)
{
// Another tag helper has modified the body. Use what they wrote.
childContent = output.Content;
}
else
{
childContent = await output.GetChildContentAsync();
}
selected = currentValues.ValuesAndEncodedValues.Contains(childContent.GetContent());
}
if (selected)
{
output.Attributes.Add("selected", "selected");
}
}
}
}
}
}