This commit is contained in:
2016-08-08 14:47:35 -04:00
commit 0b0cb7c73a
156 changed files with 114318 additions and 0 deletions
@@ -0,0 +1,86 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using HtmlTags;
using Microsoft.Web.Mvc;
namespace InventoryTraker.Web.Helpers
{
public static class AngularHelperExtension
{
public static AngularHelper<TModel> Angular<TModel>(this HtmlHelper<TModel> helper)
{
return new AngularHelper<TModel>(helper);
}
}
public class AngularHelper<TModel>
{
private readonly HtmlHelper<TModel> _htmlHelper;
public AngularHelper(HtmlHelper<TModel> helper)
{
_htmlHelper = helper;
}
public AngularModelHelper<TModel> ModelFor(string expressionPrefix)
{
return new AngularModelHelper<TModel>(_htmlHelper, expressionPrefix);
}
public HtmlTag FormForModel(string expressionPrefix)
{
var modelHelper = ModelFor(expressionPrefix);
var formGroupForMethodGeneric = typeof(AngularModelHelper<TModel>)
.GetMethod("FormGroupFor");
var wrapperTag = new HtmlTag("div").NoTag();
foreach (var prop in typeof(TModel)
.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (prop.GetCustomAttributes().OfType<HiddenInputAttribute>().Any()) continue;
var formGroupForProp = formGroupForMethodGeneric
.MakeGenericMethod(prop.PropertyType);
var propertyLambda = MakeLambda(prop);
var formGroupTag = (HtmlTag)formGroupForProp.Invoke(modelHelper,
new[] { propertyLambda });
wrapperTag.Append(formGroupTag);
}
return wrapperTag;
}
//Constructs a lambda of the form x => x.PropName
private object MakeLambda(PropertyInfo prop)
{
var parameter = Expression.Parameter(typeof(TModel), "x");
var property = Expression.Property(parameter, prop);
var funcType = typeof(Func<,>).MakeGenericType(typeof(TModel), prop.PropertyType);
//x => x.PropName
return Expression.Lambda(funcType, property, parameter);
}
public UIRatingTag UIRating(string model)
{
return new UIRatingTag(model);
}
public GridTag GridFor<TController>(Expression<Action<TController>> targetAction)
where TController : Controller
{
var dataUrl = _htmlHelper.BuildUrlFromExpression(targetAction);
return new GridTag(dataUrl);
}
}
}
@@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using HtmlTags;
using Humanizer;
using InventoryTraker.Web.Utilities;
namespace InventoryTraker.Web.Helpers
{
public class AngularModelHelper<TModel>
{
protected readonly HtmlHelper Helper;
private readonly string _expressionPrefix;
public AngularModelHelper(HtmlHelper helper, string expressionPrefix)
{
Helper = helper;
_expressionPrefix = expressionPrefix;
}
/// <summary>
/// Converts an lambda expression into a camel-cased string, prefixed
/// with the helper's configured prefix expression, ie:
/// vm.model.parentProperty.childProperty
/// </summary>
public IHtmlString ExpressionFor<TProp>(Expression<Func<TModel, TProp>> property)
{
var expressionText = ExpressionForInternal(property);
return new MvcHtmlString(expressionText);
}
/// <summary>
/// Converts a lambda expression into a camel-cased AngularJS binding expression, ie:
/// {{vm.model.parentProperty.childProperty}}
/// </summary>
public IHtmlString BindingFor<TProp>(Expression<Func<TModel, TProp>> property)
{
return MvcHtmlString.Create("{{" + ExpressionForInternal(property) + "}}");
}
/// <summary>
/// Creates a div with an ng-repeat directive to enumerate the specified property,
/// and returns a new helper you can use for strongly-typed bindings on the items
/// in the enumerable property.
/// </summary>
public AngularNgRepeatHelper<TSubModel> Repeat<TSubModel>(
Expression<Func<TModel, IEnumerable<TSubModel>>> property, string variableName)
{
var propertyExpression = ExpressionForInternal(property);
return new AngularNgRepeatHelper<TSubModel>(
Helper, variableName, propertyExpression);
}
private string ExpressionForInternal<TProp>(Expression<Func<TModel, TProp>> property)
{
var camelCaseName = property.ToCamelCaseName();
var expression = !string.IsNullOrEmpty(_expressionPrefix)
? _expressionPrefix + "." + camelCaseName
: camelCaseName;
return expression;
}
public HtmlTag FormGroupFor<TProp>(Expression<Func<TModel, TProp>> property)
{
var metadata = ModelMetadata.FromLambdaExpression(property, new ViewDataDictionary<TModel>());
var name = ExpressionHelper.GetExpressionText(property);
var expression = ExpressionForInternal(property);
//Creates <div class="form-group has-feedback"
// form-group-validation="Name">
var formGroup = new HtmlTag("div")
.AddClasses("form-group", "has-feedback")
.Attr("form-group-validation", name);
var labelText = metadata.DisplayName ?? name.Humanize(LetterCasing.Title);
//Creates <label class="control-label" for="Name">Name</label>
var label = new HtmlTag("label")
.AddClass("control-label")
.Attr("for", name)
.Text(labelText);
var tagName = metadata.DataTypeName == "MultilineText"
? "textarea"
: "input";
var placeholder = metadata.Watermark ??
(labelText + "...");
//Creates <input ng-model="expression"
// class="form-control" name="Name" type="text" >
var input = new HtmlTag(tagName)
.AddClass("form-control")
.Attr("ng-model", expression)
.Attr("name", name)
.Attr("type", "text")
.Attr("placeholder", placeholder);
ApplyValidationToInput(input, metadata);
return formGroup
.Append(label)
.Append(input);
}
private void ApplyValidationToInput(HtmlTag input, ModelMetadata metadata)
{
if (metadata.IsRequired)
input.Attr("required", "");
if (metadata.DataTypeName == "EmailAddress")
input.Attr("type", "email");
if (metadata.ModelType == typeof(DateTime))
input.Attr("type", "date");
if (metadata.DataTypeName == "PhoneNumber")
input.Attr("pattern", @"[\ 0-9()-]+");
}
}
}
@@ -0,0 +1,26 @@
using System;
using System.Web.Mvc;
using HtmlTags;
namespace InventoryTraker.Web.Helpers
{
public class AngularNgRepeatHelper<TModel> : AngularModelHelper<TModel>, IDisposable
{
public AngularNgRepeatHelper(HtmlHelper helper,
string variableName, string propertyExpression)
: base(helper, variableName)
{
var div = new HtmlTag("div");
div.Attr("ng-repeat",
string.Format("{0} in {1}", variableName, propertyExpression));
div.NoClosingTag();
Helper.ViewContext.Writer.Write(div.ToString());
}
void IDisposable.Dispose()
{
Helper.ViewContext.Writer.Write("</div>");
}
}
}
+74
View File
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web.UI;
using HtmlTags;
using InventoryTraker.Web.Utilities;
namespace InventoryTraker.Web.Helpers
{
public class GridTag : HtmlTag
{
public class ColumnBuilder<T>
{
private readonly GridTag _tag;
public ColumnBuilder(GridTag tag)
{
_tag = tag;
}
public void Add<TProp>(Expression<Func<T, TProp>> property,
string columnHeader = null,
string cellFilter = null)
{
_tag._columns.Add(new ColumnDefinition
{
Field = property.ToCamelCaseName(),
Name = columnHeader,
CellFilter = cellFilter
});
}
}
private class ColumnDefinition
{
public string Field { get; set; }
public string Name { get; set; }
public string CellFilter { get; set; }
}
private readonly List<ColumnDefinition> _columns = new List<ColumnDefinition>();
public GridTag(string dataUrl)
: base("mvc-grid")
{
Attr("grid-data-url", dataUrl);
}
public new GridTag Title(string title)
{
Attr("title", title);
return this;
}
public GridTag Columns<T>(Action<ColumnBuilder<T>> configAction)
{
var builder = new ColumnBuilder<T>(this);
configAction(builder);
return this;
}
protected override void writeHtml(HtmlTextWriter html)
{
if (_columns.Any())
this.Attr("columns", _columns.ToArray().ToJson(includeNull: false));
base.writeHtml(html);
}
}
}
@@ -0,0 +1,14 @@
using System.Web;
using System.Web.Mvc;
using InventoryTraker.Web.Utilities;
namespace InventoryTraker.Web.Helpers
{
public static class JsonHtmlHelpers
{
public static IHtmlString JsonFor<T>(this HtmlHelper helper, T obj)
{
return helper.Raw(obj.ToJson());
}
}
}
@@ -0,0 +1,26 @@
using HtmlTags;
namespace InventoryTraker.Web.Helpers
{
public class UIRatingTag : HtmlTag
{
public UIRatingTag(string model) : base("rating")
{
Attr("ng-model", model);
}
public UIRatingTag Max(int max)
{
Attr("max", max);
return this;
}
public UIRatingTag NgClick(string action)
{
Attr("ng-click", action);
return this;
}
}
}