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

Commit e938f5b

Browse files
author
N. Taylor Mullen
committed
Add FormTagHelper.
- Added the FormTagHelper. - Utilized the IHtmlGenerator to share base functionality with the HTMLHelper counterparts. #1246
1 parent e2b8400 commit e938f5b

File tree

1 file changed

+125
-0
lines changed

1 file changed

+125
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.AspNet.Mvc.Rendering;
8+
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
9+
using Microsoft.AspNet.Razor.TagHelpers;
10+
11+
namespace Microsoft.AspNet.Mvc.TagHelpers
12+
{
13+
/// <summary>
14+
/// <see cref="ITagHelper"/> implementation targeting &lt;form&gt; elements.
15+
/// </summary>
16+
[ContentBehavior(ContentBehavior.Append)]
17+
public class FormTagHelper : TagHelper
18+
{
19+
[Activate]
20+
private ViewContext ViewContext { get; set; }
21+
22+
[Activate]
23+
private AntiForgery AntiForgery { get; set; }
24+
25+
[Activate]
26+
private IHtmlGenerator Generator { get; set; }
27+
28+
/// <summary>
29+
/// The name of the action method.
30+
/// </summary>
31+
/// <remarks>
32+
/// If value contains a '/' this <see cref="ITagHelper"/> will do nothing.
33+
/// </remarks>
34+
public string Action { get; set; }
35+
36+
/// <summary>
37+
/// The name of the controller.
38+
/// </summary>
39+
public string Controller { get; set; }
40+
41+
/// <summary>
42+
/// The HTTP method for processing the form, either GET or POST.
43+
/// </summary>
44+
public string Method { get; set; }
45+
46+
/// <inheritdoc />
47+
/// <remarks>Does nothing if <see cref="Action"/> contains a '/'.</remarks>
48+
public override void Process(TagHelperContext context, TagHelperOutput output)
49+
{
50+
// If Action contains a '/' it means the user is attempting to use the FormTagHelper as a normal form.
51+
if (Action != null && Action.Contains('/'))
52+
{
53+
RestoreBoundHtmlAttributes(context, output);
54+
}
55+
else
56+
{
57+
// TODO: Make this behavior optional once https://github.com/aspnet/Razor/issues/121 is completed.
58+
output.Content = AntiForgery.GetHtml(ViewContext.HttpContext).ToString();
59+
60+
var routeValues = PullRouteValues(output.Attributes);
61+
var tagBuilder = Generator.GenerateForm(ViewContext,
62+
Action,
63+
Controller,
64+
routeValues,
65+
Method,
66+
htmlAttributes: new Dictionary<string, object>());
67+
68+
TagHelperOutputHelper.MergeAttributes(output, tagBuilder);
69+
}
70+
}
71+
72+
// TODO: We will not need this method once https://github.com/aspnet/Razor/issues/89 is completed.
73+
private static Dictionary<string, object> PullRouteValues(IDictionary<string, string> htmlAttributes)
74+
{
75+
var routeAttributePrefix = "route-";
76+
77+
// We're only interested in HTML attributes that have the desired routeAttributePrefix.
78+
var routeAttributes = htmlAttributes.Where(attribute =>
79+
attribute.Key.StartsWith(routeAttributePrefix, StringComparison.OrdinalIgnoreCase));
80+
81+
// We need to remove any route based HTML attributes from the HTML attributes dictionary because
82+
// they shouldn't be treated as HTML attributes, they're route values.
83+
foreach (var attribute in routeAttributes)
84+
{
85+
htmlAttributes.Remove(attribute.Key);
86+
}
87+
88+
// We build a Dictionary<string, object> because Generator.GenerateForm does not accept a
89+
// Dictionary <string, string>.
90+
return routeAttributes.ToDictionary(attribute => attribute.Key.Substring(routeAttributePrefix.Length),
91+
attribute => (object)attribute.Value);
92+
}
93+
94+
private void RestoreBoundHtmlAttributes(TagHelperContext context, TagHelperOutput output)
95+
{
96+
var attributesToRestore = new List<string>();
97+
98+
if (Action != null)
99+
{
100+
attributesToRestore.Add(nameof(Action));
101+
}
102+
103+
if (Controller != null)
104+
{
105+
attributesToRestore.Add(nameof(Controller));
106+
}
107+
108+
if (Method != null)
109+
{
110+
attributesToRestore.Add(nameof(Method));
111+
}
112+
113+
foreach (var attributeName in attributesToRestore)
114+
{
115+
// We need to look for the KeyValuePair<string, object> attribute so we can ensure that the attribute
116+
// that we re-add to the output object has the same attribute name as the one the user typed.
117+
var entry = context.AllAttributes.Single(attribute =>
118+
attribute.Key.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
119+
var originalAttribute = new KeyValuePair<string, string>(entry.Key, entry.Value.ToString());
120+
121+
output.Attributes.Add(originalAttribute);
122+
}
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)