Reputation: 739
I've been using Chris Pratt's MergeHtmlAttributes Html Helper Extension method for a while in my asp.net mvc 5 editor templates. I started the process of switching over an application to Asp.net core 1.1(.net framework 4.5.2). And The htmlhelperExtension isn't working for me.
public static partial class HtmlHelperExtensions
{
//https://cpratt.co/html-editorfor-and-htmlattributes/
public static IDictionary<string, object> MergeHtmlAttributes(this HtmlHelper helper, object htmlAttributesObject, object defaultHtmlAttributesObject)
{
var concatKeys = new string[] { "class" };
var htmlAttributesDict = htmlAttributesObject as IDictionary<string, object>;
var defaultHtmlAttributesDict = defaultHtmlAttributesObject as IDictionary<string, object>;
RouteValueDictionary htmlAttributes = (htmlAttributesDict != null)
? new RouteValueDictionary(htmlAttributesDict)
: HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributesObject);
RouteValueDictionary defaultHtmlAttributes = (defaultHtmlAttributesDict != null)
? new RouteValueDictionary(defaultHtmlAttributesDict)
: HtmlHelper.AnonymousObjectToHtmlAttributes(defaultHtmlAttributesObject);
foreach (var item in htmlAttributes)
{
if (concatKeys.Contains(item.Key))
{
defaultHtmlAttributes[item.Key] = (defaultHtmlAttributes[item.Key] != null)
? string.Format("{0} {1}", defaultHtmlAttributes[item.Key], item.Value)
: item.Value;
}
else
{
if(item.Key?.ToString() == "divClass")
{
continue;
}
defaultHtmlAttributes[item.Key] = item.Value;
}
}
return defaultHtmlAttributes;
}
}
When I copy the class over it flags the statment: using System.Web.Mvc; -Cannot resolve symbol MVC.
And after removing that using statement I get the message cannot resolve symbol "HtmlHelper" in MergeHtmlAttributes(this HtmlHelper helper, ...)
I have the option of adding either
Microsoft.AspNetCore.Mvc.ViewFeatures.HtmlHelper
or .HtmlHelper<Tmodel>
I chose .HtmlHelper
.
After that it refers to the line RouteValueDictionary htmlAttributes = and says it cannot convert IDictionary<string, object>
to system.web.Routing.RouteValueDictionary
. And I should change the type to IDictionary<string, object>
or cast to RouteValueDictionary
. Either way I get the following error when trying to use MergeHtmlAttributes in one of my editor templates.
'IHtmlHelper<object>'
does not contain a definition for 'MergeHtmlAttributes' and the best extension method overload 'HtmlHelperExtensions.MergeHtmlAttributes(HtmlHelper, object, object)' requires a receiver of type 'HtmlHelper'
This line is throwing the error-> var htmlAttributes = Html.MergeHtmlAttributes(ViewData, defaultHtmlAttributesObject);
Is there a way to get this to work in asp.net core or is there a different method to achieve the same results? Here's an example of one of my editor templates so you can see the MergeHtmlAttributes being used. If I can't build the template like this anymore is there a newer/better way to do it using tag helpers? I really like having the labelfor, txtboxfor, ValidationMessageFor, etc all in one html helper.
@model int?
@{
var defaultHtmlAttributesObject = new { @class = "form-control" };
var htmlAttributes = Html.MergeHtmlAttributes(ViewData, defaultHtmlAttributesObject);
object divClass;
ViewData.TryGetValue("divClass", out divClass);
if (divClass == null) { divClass = ""; }
IDictionary<string, object> validationAttributes = Html.GetUnobtrusiveValidationAttributes("");
Html.ViewContext.FormContext.RenderedField(ViewData.TemplateInfo.GetFullHtmlFieldName(null), false);
}
<div class="form-group @divClass @(Html.ValidationErrorFor(x => x, " has-error"))">
@Html.LabelFor(x => x, new { @class = "control-label" })
@if (validationAttributes.ContainsKey("data-val-required"))
{<span class="text-danger">*</span>}
@Html.TextBoxFor(x => x, htmlAttributes)
@Html.ValidationMessageFor(model => model, "", new { @class = "text-danger" })
</div>
F.Y.I. while converting to asp.net core 1.1 (and .net framework 4.5.2) I ended up putting the connection string in it's app.config file which allowed EF6 to work with Asp.net core so I can keep using the EF code I had built, for whatever reason it wouldn't find the connection string in appsettings.json.
Upvotes: 3
Views: 2523
Reputation: 739
Initially I didn't realize Microsoft was using IHtmlHelper now instead of HtmlHelper.
Once I modified the code to reflect that change and found Microsoft's mvc repository on github so that I was able to find their implementation of AnonymousObjectToHtmlAttributes it all fell together.
The below code is working for me, but I might have to make some changes for edge cases I haven't yet thought of.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Routing;
using Microsoft.AspNetCore.Mvc.Rendering;
public static class HtmlHelperExtensions
{
//http://cpratt.co/html-editorfor-and-htmlattributes/
/// <summary>
/// This is used with the EditorTemplates to copy the page element's htmlAttributes over to the editorTemplates
/// while still being able to specify values to always use (like @class = "form-control") in the editorTemplates.
/// </summary>
public static IDictionary<string, object> MergeHtmlAttributes(this IHtmlHelper helper, object htmlAttributesObject, object defaultHtmlAttributesObject)
{
var concatKeys = new[] { "class" };
var htmlAttributesDict = htmlAttributesObject as IDictionary<string, object>;
var defaultHtmlAttributesDict = defaultHtmlAttributesObject as IDictionary<string, object>;
RouteValueDictionary htmlAttributes = new RouteValueDictionary(htmlAttributesDict != null
? htmlAttributesDict
: AnonymousObjectToHtmlAttributes(htmlAttributesObject));
RouteValueDictionary defaultHtmlAttributes = new RouteValueDictionary(defaultHtmlAttributesDict != null
? defaultHtmlAttributesDict
: AnonymousObjectToHtmlAttributes(defaultHtmlAttributesObject));
foreach (var item in htmlAttributes)
{
if (concatKeys.Contains(item.Key))
{
defaultHtmlAttributes[item.Key] = defaultHtmlAttributes[item.Key] != null
? string.Format("{0} {1}", defaultHtmlAttributes[item.Key], item.Value)
: item.Value;
}
else
{
if(item.Key == "divClass")
{
continue;
}
defaultHtmlAttributes[item.Key] = item.Value;
}
}
return defaultHtmlAttributes;
}
private static IDictionary<string, object> AnonymousObjectToHtmlAttributes(object htmlAttributes)
{
var dictionary = htmlAttributes as IDictionary<string, object>;
if (dictionary != null)
{
return new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase);
}
dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
if (htmlAttributes != null)
{
var stringAttributes = htmlAttributes.ToString();
stringAttributes = stringAttributes.Replace("{", "").Replace("}", "");
string[] attributesArray = stringAttributes.Split(new[] { ','}, StringSplitOptions.RemoveEmptyEntries);
foreach (var helper in attributesArray)
{
string[] attribKeyValue = helper.Trim().Split(' ');
dictionary[attribKeyValue.First()] = attribKeyValue.Last();
}
}
return dictionary;
}
}
Upvotes: 3