Convert string to html attributes - razor

Is their a way to convert a string (this string can change and contains asp-route-... attributes) to a list of html attributes? The razor engine should use all the asp-route-... attributes to convert to a correct url. I have the following code but that doesn't work.
#{
var Attributes = ViewData["Attributes"] as Dictionary<string,string>;
var AttributeRoute = "";
#foreach (var key in Attributes.Keys)
{
AttributeRoute += "asp-route-"+key+"=\""+Attributes[key]+"\" ";
}
}
...
#AttributeRoute #Prints output (ex. asp-route-testkey="testvalue")
<a class='item' #AttributeRoute>test</a> #Doesn't print the list of attributes

Solved it myself by doing the following:
1. Create custom Taghelper class
namespace test
{
[HtmlTargetElement("special-link")]
public class SpecialLinkTagHelper : TagHelper
{
[ViewContext]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
//Create a tag
output.TagName="a";
//Get the parameters
string parameters="";
Dictionary<string,string> Parameters = (Dictionary<string,string>)this.ViewContext.ViewData["Attributes"];
foreach(KeyValuePair<string, string> pair in Parameters){
parameters+= pair.Key+"="+pair.Value+"&";
}
output.Attributes.SetAttribute("href", "?"+parameters);
}
}
}
2. Create Link (in a .cshtml file)
<special-link>link</special-link>
Hope this can help someone!

Related

How to include CSS style inside HTML style (code generated via ASP.Net MVC Razor page)

I'm still at my early beginning with ASP.Net MVC. So, I'm not sure if I'm following good practices or not.
I want to generate HTML code based on CSS style
Razor page:
#foreach (p in ...)
{
<td style="#Helper.CustomBackground(p) #Helper.CustomComplexStyle(p)">...</td>
}
CSS classes:
.complexStyleA {
// ...
}
.complexStyleB { }
C# helper method:
// None of the styling method has the "style" attribute, so I can concatenate several custom-styles
public static class Helper
{
// Basic direct styling
public static string CustomBackground(object p) => "{ background: red; }";
// More complex styling based on CSS ones
public static string CustomComplexStyle(object p) => "{ // complexStyleA, complexStyleB, etc. based on p; }"
}
I don't really mind generated all the code inside the HTML tags, but generate a table with several thousand lines and can obviously reduce the file size!
Thanks for any insights :-)
UPDATE
I think that I will do something as follow :
<td #Html.Raw(#Hepers.ConcatenateCSSClasses(#CustomComplexStyle(p),
#OtherCustomComplexStyle(p),
...))>
...
</td>
where #concatenateCSSClasses() removes all " class=" ans #...Syle(p) returns "class=\"...StyleXYZ\"", unless I can have duplicated tag attributes.. Any better ideas?
UPDATE with cleaner methods
public static string HTMLTagAttribute(FieldType fieldType, Field field = null)
{
string output = "";
// Makes distinction between hearder and body
if (field == null)
{
output += HTMLHeaderTagClassAttributes(fieldType);
output += HTMLHeaderTagStyleAttributes(fieldType);
}
else
{
output += HTMLBodyTagClassAttributes(fieldType, field);
output += HTMLBodyTagStyleAttributes(fieldType, field);
}
return output;
}
Body only shown with clear distinction between classes and (inline styles). I don't detail the samll method HTMLWidth(), HTMLBackground(), etc. which are self explanatory.
#region HTML Body Tag Attribute Methods
private static string HTMLBodyTagStyleAttributes(FieldType fieldType, Field field)
{
AttributeValues style = new AttributeValues("style");
style += HTMLWidth(fieldType);
style += HTMLBackground(fieldType, field);
style += HTMLAlign(fieldType);
return style.ToString();
}
private static string HTMLBodyTagClassAttributes(FieldType fieldType, Field field)
{
AttributeValues cls = new AttributeValues("class");
cls += HTMLTopBorder(fieldType, field);
cls += HTMLSideBorder(fieldType);
return cls.ToString();
}
#endregion
Class which helps concatenate class and style attribute values (with no extra space).
/// <summary>Class to help format HMTL class and style attribute values</summary>
class AttributeValues
{
private readonly string _attribute = "";
private readonly List<string> _values = new List<string>();
// TODO - Make distinction bewteen class and styles
#region Constructors
public AttributeValues(string attribute)
{
_attribute = attribute;
}
#endregion
public static AttributeValues operator +(AttributeValues values, string value)
{
if (value != "") values._values.Add(value);
return values;
}
public static bool operator ==(AttributeValues values, string value) => values == value;
public static bool operator !=(AttributeValues values, string value) => values != value;
#region Public Overridden Methods
public override string ToString()
{
string values = "";
foreach (string value in _values)
{
if (values != "") values += " ";
values += value;
}
return values != "" ? $"{ _attribute }=\"{ values }\"" :"" ;
}
public override bool Equals(object obj) => base.Equals(obj);
public override int GetHashCode() => base.GetHashCode();
#endregion
}
And finally in the Razor page (apparently, I have to wrap it in HTML.Raw() because of the " (I agree that it could be refractor within the th, but doesn't make the code easier):
<td Html.Raw(Helpers.HTMLTagAttribute(Enums.FieldType.Account))>..</td>

Adding a dynamic list of parameters with Html helper

I'd like to be able to feed in a list of parameter names and values into a Html.Actionlink but the helper doesn't create the parameters as I would like. Any ideas how to do this?
public class ParameterNameValue
{
public string ParameterName { get; set; }
public string ParameterValue { get; set; }
}
View
#foreach (var action in post.FeedActions)
{
var parameters = "";
foreach (var param in action.Parameters)
{
parameters += param.ParameterName + "=" + param.ParameterValue + ",";
}
#Html.ActionLink(#action.Label, action.ActionName,
new { controller = action.Controller, id = action.CommunityId, slug = action.Slug,
Fromfeed=true,parameters }, new { #class = action.Classes })
}
yields a link like this:
Whereas I need the parameters part to look like:
?FromFeed=true&MatchId=1234&InnerId=5678
edit: I got it working by just manually creating the tag, but no doubt there's a nice way of doing this by creating a custom helper.
#action.Label
I'd suggest you to extend the classic ActionLink helper with a prototype similar to this (add a parameter for your specific class) :
public static MvcHtmlString ActionLinkCustom(this HtmlHelper html, string linkText, string actionName, string controllerName, object routeValues, List<ParameterNameValue> yourOtherValues)
In the code, check if you got any custom values. If such, add them to the RouteValuesDictionnary. Then use the classic ActionLink helper providing this modified RouteValuesDictionnary.
Note : you can work on the routeValues using this
IDictionary<string, object> RouteValues = HtmlHelper.ObjectToDictionary(routeValues);

Reading JSON values from web browser?

I have a random JSON generated online and I am able to print all the values. But how do I read each array separately? For example, the below JSON contains different attributes, how do I read the string name that is an array containing 4 values.
JSON reader:
public class JsonHelper
{
public static T[] getJsonArray<T>(string json)
{
string newJson = "{ \"array\": " + json + "}";
Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(newJson);
return wrapper.array;
}
[System.Serializable]
private class Wrapper<T>
{
public T[] array;
}
}
[System.Serializable]
public class RootObject
{
public string name;
public string height;
public string mass ;
}
The below script is used to access the JSON online through RESTApi GET service. I am able to receive the whole text but how I read one single value of name or height or mass?
Script:
using UnityEngine.Networking;
using System.Linq;
using System.Linq.Expressions;
using UnityEngine.UI;
using System.IO;
public class GetData : MonoBehaviour {
// Use this for initialization
void Start () {
StartCoroutine(GetNames());
}
IEnumerator GetNames()
{
string GetNameURL = "https://swapi.co/api/people/1/?format=json";
using(UnityWebRequest www = UnityWebRequest.Get(GetNameURL))
{
// www.chunkedTransfer = false;
yield return www.Send();
if(www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
if(www.isDone)
{
string jsonResult = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);
Debug.Log(jsonResult); //I am getting the result here
}
}
}
}
}
Your API call to 'https://swapi.co/api/people/1/?format=json' returns a single object, not an array.
So after you get your json, you can access name and height etc like:
if (www.isDone)
{
string jsonResult = System.Text.Encoding.UTF8.GetString(www.downloadHandler.data);
Debug.Log(jsonResult); //I am getting the result here
RootObject person = JsonUtility.FromJson<RootObject>(jsonResult);
// then you can access each property
Debug.Log(person.name);
Debug.Log(person.height);
}

Table TagHelper for an IEnumerable Model (without using Reflection)

I'm trying to create a table tag helper that can parse columns and rows from the given model automatically.
This is how it should (theoretically be used):
<table for="#Model">
</table>
and this should automatically show column names and the rows.
Generating column names wasn't that difficult since I'm passing the model
[HtmlTargetElement("table", Attributes = "for")]
public class DataTableTagHelper :TagHelper
{
[HtmlAttributeName("for")]
public ModelExpression For { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
foreach (var item in For.Metadata.ElementMetadata.Properties)
{
// generate html for theader using item.Name
}
}
}
But getting the values of the model isn't as easy.
Is there a way to get the values of those properties?
I'm trying to avoid reflection, because I don't think generating HTML code though reflection with every request is a good idea.
We get the property's value by passing the model to its property's PropertyGetter.
public override void Process(TagHelperContext context, TagHelperOutput output)
{
foreach (var prop in For.Metadata.Properties)
{
var propertyName = prop.Name;
var propertyValue = prop.PropertyGetter(For.Model);
}
return Task.CompletedTask;
}
If the model implements IEnumerable, then we need to pass each item to its PropertyGetter.
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
foreach (var item in For.Model as IEnumerable)
{
foreach (var prop in For.Metadata.ElementMetadata.Properties)
{
var name = prop.Name;
var value = prop.PropertyGetter(item);
}
}
return Task.CompletedTask;
}

MVC6 alternative to #Html.DisplayFor

MVC6 introduces Tag Helpers which is a better way compared to using #Html.EditorFor, etc. However I have not found any Tag Helper that would be an alternative to #Html.DisplayFor.
Of course I can use a variable directly on a Razor page, such as #Model.BookingCode. But this does not allow to control formatting.
With MVC6, what's conceptually correct way for displaying a value of a model property?
#Html.DisplayFor still exists and can still be used.
The difference between HtmlHelpers and TagHelpers is that HtmlHelpers choose which html elements to render for you whereas TagHelpers work with html tags that you add yourself so you have more full control over what html element is used. You do have some control over the markup using templates with HtmlHelpers but you have more control with TagHelpers.
So you should think in terms of what html markup do I want to wrap this model property in and add that markup around the property itself using #Model.Property with some markup around it or continue using DisplayFor if you prefer to let the helper decide.
You can create your own tag helper
namespace MyDemo.TagHelpers
{
[HtmlTargetElement("p", Attributes = ForAttributeName)]
public class DisplayForTagHelper : TagHelper
{
private const string ForAttributeName = "asp-for";
[HtmlAttributeName(ForAttributeName)]
public ModelExpression For { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
var text = For.ModelExplorer.GetSimpleDisplayText();
output.Content.SetContent(text);
}
}
}
Add use it in view:
<p asp-for="MyProperty" class="form-control-static"></p>
I have been using this as a display tag helper.
[HtmlTargetElement("*", Attributes = ForAttributeName)]
public class DisplayForTagHelper : TagHelper
{
private const string ForAttributeName = "asp-display-for";
private readonly IHtmlHelper _html;
public DisplayForTagHelper(IHtmlHelper html)
{
_html = html;
}
[HtmlAttributeName(ForAttributeName)]
public ModelExpression Expression { get; set; }
public IHtmlHelper Html
{
get
{
(_html as IViewContextAware)?.Contextualize(ViewContext);
return _html;
}
}
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (output == null)
throw new ArgumentNullException(nameof(output));
var type = Expression.Metadata.UnderlyingOrModelType;
if (type.IsPrimitive)
{
output.Content.SetContent(Expression.ModelExplorer.GetSimpleDisplayText());
}
// Special Case for Personal Use
else if (typeof(Dictionary<string, string>).IsAssignableFrom(type))
{
output.Content.SetHtmlContent(Html?.Partial("Dictionary", Expression.ModelExplorer.Model));
}
else
{
var htmlContent = Html.GetHtmlContent(Expression);
output.Content.SetHtmlContent(htmlContent);
}
}
}
public static class ModelExpressionExtensions
{
public static IHtmlContent GetHtmlContent(this IHtmlHelper html, ModelExpression expression)
{
var ViewEngine = html.ViewContext.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
var BufferScope = html.GetFieldValue<IViewBufferScope>();
var htmlContent = new TemplateBuilder(ViewEngine, BufferScope, html.ViewContext, html.ViewContext.ViewData, expression.ModelExplorer, expression.Name, null, true, null).Build();
return htmlContent;
}
public static TValue GetFieldValue<TValue>(this object instance)
{
var type = instance.GetType();
var field = type.GetFields(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance).FirstOrDefault(e => typeof(TValue).IsAssignableFrom(e.FieldType));
return (TValue)field?.GetValue(instance);
}
}
try below code
public class movie
{
public int ID { get; set; }
[DisplayName("Movie Title")]
public string Title { get; set; }
}
///////////////////////////////////////////////////
#model IEnumerable<MvcMovie.Models.Movie>
<h1>Show List Movies</h1>
<label asp-for="ToList()[0].Title">< /label>
#foreach (var movie in Model)
{
#movie.Title
}