更新文本框和下拉列表框TagHelper组件lambda表达式解析

This commit is contained in:
何镇汐
2018-02-13 11:49:47 +08:00
parent 3af0a48328
commit 0cc00533f2
21 changed files with 414 additions and 61 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2010
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Util.Samples.Webs", "sample\Util.Samples.Webs\Util.Samples.Webs.csproj", "{323B64B7-5CE0-4222-B9ED-9F418730897C}"
EndProject
@@ -33,6 +33,7 @@ namespace Donau.Services.Dtos.Customers {
/// 余额
/// </summary>
[DataMember]
[StringLength( 6, ErrorMessage = "昵称输入过长,不能超过30位" )]
[Display( Name = "余额" )]
public double Balance { get; set; }
@@ -25,8 +25,9 @@
<util-form id="form" on-submit="onChange(form)">
<mat-card>
<mat-card-content>
@x.TextBox( t => t.Name )
@x.Select( t => t.Nation )
<util-textbox for="Name"></util-textbox>
<util-select for="Gender"></util-select>
<util-select for="Nation"></util-select>
@x.Radio( t => t.Gender )
</mat-card-content>
<mat-card-content>
@@ -1,10 +1,13 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Util.Helpers;
using Util.Ui.Attributes;
using Util.Ui.Components;
using Util.Ui.Configs;
using Util.Ui.Extensions;
using Util.Ui.Operations.Forms;
using Util.Ui.Operations.Forms.Validations;
@@ -22,10 +25,10 @@ namespace Util.Ui.Material.Commons.Internal {
/// <param name="control">表单控件</param>
/// <param name="expression">属性表达式</param>
/// <param name="member">成员</param>
public static void InitModelControl<TModel, TProperty>( IFormControl control, Expression<Func<TModel, TProperty>> expression, MemberInfo member ) {
public static void InitControl<TModel, TProperty>( IFormControl control, Expression<Func<TModel, TProperty>> expression, MemberInfo member ) {
control.Name( Util.Helpers.String.FirstLowerCase( Lambda.GetName( expression ) ) );
control.Placeholder( Reflection.GetDisplayNameOrDescription( member ) );
InitModel( control,expression );
InitModel( control, expression );
InitRequired( control, expression );
}
@@ -40,24 +43,22 @@ namespace Util.Ui.Material.Commons.Internal {
}
/// <summary>
/// 初始化必填项验证
/// 获取模型绑定
/// </summary>
public static void InitRequired<TModel, TProperty>( IRequired control, Expression<Func<TModel, TProperty>> expression ) {
var attribute = Lambda.GetAttribute<RequiredAttribute>( expression );
if ( attribute == null )
return;
control.Required( attribute.ErrorMessage );
/// <typeparam name="TModel">模型类型</typeparam>
/// <typeparam name="TProperty">属性类型</typeparam>
/// <param name="expression">属性表达式</param>
public static string GetModel<TModel, TProperty>( Expression<Func<TModel, TProperty>> expression ) {
return GetModel( GetModelName<TModel>(), GetPropertyName( expression ) );
}
/// <summary>
/// 获取模型绑定
/// </summary>
/// <typeparam name="TModel">模型类型</typeparam>
/// <typeparam name="TProperty">属性类型</typeparam>
public static string GetModel<TModel, TProperty>( Expression<Func<TModel, TProperty>> expression ) {
var modelName = Util.Helpers.String.FirstLowerCase( GetModelName<TModel>() );
var propertyName = Util.Helpers.String.FirstLowerCase( GetPropertyName( expression ) );
if ( string.IsNullOrWhiteSpace( modelName ) || string.IsNullOrWhiteSpace( propertyName ) )
private static string GetModel( string modelName, string propertyName ) {
modelName = Util.Helpers.String.FirstLowerCase( modelName );
propertyName = Util.Helpers.String.FirstLowerCase( propertyName );
if( string.IsNullOrWhiteSpace( modelName ) || string.IsNullOrWhiteSpace( propertyName ) )
return string.Empty;
return $"{modelName}&&{modelName}.{propertyName}";
}
@@ -67,10 +68,16 @@ namespace Util.Ui.Material.Commons.Internal {
/// </summary>
private static string GetModelName<TModel>() {
Type type = Common.GetType<TModel>();
if ( type.GetCustomAttribute<ModelAttribute>() is ModelAttribute attribute ) {
return GetModelName( type );
}
/// <summary>
/// 获取模型名称
/// </summary>
private static string GetModelName( Type modelType ) {
if( modelType.GetCustomAttribute<ModelAttribute>() is ModelAttribute attribute )
return attribute.Ignore ? string.Empty : attribute.Model;
}
return typeof( TModel ).Name;
return modelType.Name;
}
/// <summary>
@@ -78,7 +85,85 @@ namespace Util.Ui.Material.Commons.Internal {
/// </summary>
private static string GetPropertyName<TModel, TProperty>( Expression<Func<TModel, TProperty>> expression ) {
var attribute = Lambda.GetAttribute<ModelAttribute>( expression );
return attribute == null ? Lambda.GetName( expression ) : attribute.Ignore ? string.Empty : attribute.Model; ;
return GetPropertyName( attribute, Lambda.GetName( expression ) );
}
/// <summary>
/// 获取属性名称
/// </summary>
private static string GetPropertyName( ModelAttribute attribute,string propertyName ) {
return attribute == null ? propertyName : attribute.Ignore ? string.Empty : attribute.Model; ;
}
/// <summary>
/// 初始化必填项验证
/// </summary>
public static void InitRequired<TModel, TProperty>( IRequired control, Expression<Func<TModel, TProperty>> expression ) {
var attribute = Lambda.GetAttribute<RequiredAttribute>( expression );
if( attribute == null )
return;
control.Required( attribute.ErrorMessage );
}
/// <summary>
/// 初始化模型配置
/// </summary>
/// <param name="config">配置</param>
/// <param name="expression">属性表达式</param>
/// <param name="member">成员</param>
public static void InitConfig( IConfig config, ModelExpression expression, MemberInfo member ) {
config.SetAttribute( UiConst.Name, Util.Helpers.String.FirstLowerCase( expression.Name ) );
config.SetAttribute( UiConst.Placeholder, Reflection.GetDisplayNameOrDescription( member ) );
InitModel( config, expression );
InitRequired( config, expression );
}
/// <summary>
/// 初始化模型
/// </summary>
public static void InitModel( IConfig config, ModelExpression expression ) {
var model = GetModel( expression );
if( string.IsNullOrWhiteSpace( model ) )
return;
config.SetAttribute( UiConst.Model, model );
}
/// <summary>
/// 获取模型绑定
/// </summary>
public static string GetModel( ModelExpression expression ) {
return GetModel( GetModelName( expression ), GetPropertyName( expression ) );
}
/// <summary>
/// 获取模型名称
/// </summary>
private static string GetModelName( ModelExpression expression ) {
Type type = expression.Metadata.ContainerType;
return GetModelName( type );
}
/// <summary>
/// 获取属性名称
/// </summary>
private static string GetPropertyName( ModelExpression expression ) {
var attribute = expression.Metadata.ModelType.GetCustomAttribute<ModelAttribute>();
return GetPropertyName( attribute, expression.Name );
}
/// <summary>
/// 初始化必填项验证
/// </summary>
/// <param name="config">配置</param>
/// <param name="expression">属性表达式</param>
public static void InitRequired( IConfig config, ModelExpression expression ) {
if ( expression.Metadata.IsRequired == false )
return;
var attribute = expression.GetValidationAttribute<RequiredAttribute>();
if( attribute == null )
return;
config.SetAttribute( UiConst.Required, true );
config.SetAttribute( UiConst.RequiredMessage, attribute.ErrorMessage );
}
}
}
@@ -61,7 +61,7 @@ namespace Util.Ui.Material.Extensions {
/// <typeparam name="TComponent">组件类型</typeparam>
/// <param name="component">组件实例</param>
public static TComponent Bool<TComponent>( this TComponent component ) where TComponent : IItem {
return component.Add( "是", "true" ).Add( "否", "false" );
return component.Add( "是", true ).Add( "否", false );
}
}
}
@@ -29,8 +29,9 @@ namespace Util.Ui.Material.Forms.Configs {
/// 添加项集合
/// </summary>
/// <param name="items">列表项集合</param>
public void AddItems( params Item[] items ) {
public SelectConfig AddItems( params Item[] items ) {
Items.AddRange( items );
return this;
}
/// <summary>
@@ -37,7 +37,7 @@ namespace Util.Ui.Material.Forms.Models {
/// 初始化
/// </summary>
private void Init() {
Helper.InitModelControl( this, _expression, _memberInfo );
Helper.InitControl( this, _expression, _memberInfo );
InitType();
}
@@ -39,7 +39,7 @@ namespace Util.Ui.Material.Forms.Models {
/// 初始化
/// </summary>
private void Init() {
Helper.InitModelControl( this, _expression, _memberInfo );
Helper.InitControl( this, _expression, _memberInfo );
InitType();
InitValidation();
}
@@ -1,8 +1,10 @@
using Util.Ui.Builders;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Util.Ui.Builders;
using Util.Ui.Configs;
using Util.Ui.Material.Commons.Configs;
using Util.Ui.Material.Forms.Builders;
using Util.Ui.Material.Forms.Configs;
using Util.Ui.Material.Forms.Resolvers;
namespace Util.Ui.Material.Forms.Renders {
/// <summary>
@@ -26,12 +28,23 @@ namespace Util.Ui.Material.Forms.Renders {
/// 获取标签生成器
/// </summary>
protected override TagBuilder GetTagBuilder() {
ResolveExpression();
var builder = new SelectWrapperBuilder();
base.Config( builder );
ConfigSelect( builder );
return builder;
}
/// <summary>
/// 解析属性表达式
/// </summary>
private void ResolveExpression() {
if( _config.Contains( UiConst.For ) == false )
return;
var expression = _config.GetValue<ModelExpression>( UiConst.For );
SelectExpressionResolver.Init( expression, _config );
}
/// <summary>
/// 配置
/// </summary>
@@ -54,9 +67,19 @@ namespace Util.Ui.Material.Forms.Renders {
/// 配置数据源
/// </summary>
private void ConfigDataSource( SelectWrapperBuilder builder ) {
AddItems();
builder.AddAttribute( "[dataSource]", _config.GetValue( UiConst.DataSource ) );
}
/// <summary>
/// 添加项集合
/// </summary>
private void AddItems() {
if( _config.Items.Count == 0 )
return;
_config.SetAttribute( UiConst.DataSource, Util.Helpers.Json.ToJson( _config.Items, true ) );
}
/// <summary>
/// 配置重置项
/// </summary>
@@ -1,9 +1,11 @@
using Util.Ui.Builders;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Util.Ui.Builders;
using Util.Ui.Configs;
using Util.Ui.Material.Commons.Configs;
using Util.Ui.Material.Enums;
using Util.Ui.Material.Forms.Builders;
using Util.Ui.Material.Forms.Configs;
using Util.Ui.Material.Forms.Resolvers;
namespace Util.Ui.Material.Forms.Renders {
/// <summary>
@@ -27,6 +29,7 @@ namespace Util.Ui.Material.Forms.Renders {
/// 获取标签生成器
/// </summary>
protected override TagBuilder GetTagBuilder() {
ResolveExpression();
var builder = CreateBuilder();
base.Config( builder );
ConfigTextArea( builder );
@@ -35,6 +38,16 @@ namespace Util.Ui.Material.Forms.Renders {
return builder;
}
/// <summary>
/// 解析属性表达式
/// </summary>
private void ResolveExpression() {
if( _config.Contains( UiConst.For ) == false )
return;
var expression = _config.GetValue<ModelExpression>( UiConst.For );
TextBoxExpressionResolver.Init( expression, _config );
}
/// <summary>
/// 创建标签生成器
/// </summary>
@@ -130,7 +143,5 @@ namespace Util.Ui.Material.Forms.Renders {
private void ConfigMaxLength( TagBuilder builder ) {
builder.AddAttribute( "[maxLength]", _config.GetValue( UiConst.MaxLength ) );
}
}
}
@@ -0,0 +1,84 @@
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Util.Helpers;
using Util.Ui.Configs;
using Util.Ui.Extensions;
using Util.Ui.Material.Commons.Internal;
using Util.Ui.Material.Forms.Configs;
namespace Util.Ui.Material.Forms.Resolvers {
/// <summary>
/// 下拉列表表达式解析器
/// </summary>
public class SelectExpressionResolver {
/// <summary>
/// 初始化下拉列表表达式解析器
/// </summary>
/// <param name="expression">属性表达式</param>
/// <param name="config">配置</param>
private SelectExpressionResolver( ModelExpression expression, SelectConfig config ) {
if( expression == null || config == null )
return;
_expression = expression;
_config = config;
_memberInfo = expression.GetMemberInfo();
}
/// <summary>
/// 属性表达式
/// </summary>
private readonly ModelExpression _expression;
/// <summary>
/// 配置
/// </summary>
private readonly SelectConfig _config;
/// <summary>
/// 成员
/// </summary>
private readonly MemberInfo _memberInfo;
/// <summary>
/// 初始化
/// </summary>
/// <param name="expression">属性表达式</param>
/// <param name="config">配置</param>
public static void Init( ModelExpression expression, SelectConfig config ) {
new SelectExpressionResolver( expression, config ).Init();
}
/// <summary>
/// 初始化
/// </summary>
private void Init() {
Helper.InitConfig( _config, _expression, _memberInfo );
InitType();
}
/// <summary>
/// 根据类型初始化
/// </summary>
private void InitType() {
if ( Reflection.IsBool( _memberInfo ) )
InitBool();
else if ( Reflection.IsEnum( _memberInfo ) )
InitEnum();
}
/// <summary>
/// 初始化布尔类型
/// </summary>
private void InitBool() {
_config.AddItems( new Item( "是", true ) ).AddItems( new Item( "否", false ) );
}
/// <summary>
/// 初始化枚举类型
/// </summary>
private void InitEnum() {
_config.AddItems( Util.Helpers.Enum.GetItems( _expression.Metadata.ModelType ).ToArray() );
}
}
}
@@ -0,0 +1,103 @@
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Util.Helpers;
using Util.Ui.Configs;
using Util.Ui.Extensions;
using Util.Ui.Material.Commons.Internal;
using Util.Ui.Material.Forms.Configs;
namespace Util.Ui.Material.Forms.Resolvers {
/// <summary>
/// 文本框表达式解析器
/// </summary>
public class TextBoxExpressionResolver {
/// <summary>
/// 初始化文本框表达式解析器
/// </summary>
/// <param name="expression">属性表达式</param>
/// <param name="config">配置</param>
private TextBoxExpressionResolver( ModelExpression expression, TextBoxConfig config ) {
if( expression == null || config == null )
return;
_expression = expression;
_config = config;
_memberInfo = expression.GetMemberInfo();
}
/// <summary>
/// 属性表达式
/// </summary>
private readonly ModelExpression _expression;
/// <summary>
/// 配置
/// </summary>
private readonly TextBoxConfig _config;
/// <summary>
/// 成员
/// </summary>
private readonly MemberInfo _memberInfo;
/// <summary>
/// 初始化
/// </summary>
/// <param name="expression">属性表达式</param>
/// <param name="config">配置</param>
public static void Init( ModelExpression expression, TextBoxConfig config ) {
new TextBoxExpressionResolver( expression, config ).Init();
}
/// <summary>
/// 初始化
/// </summary>
private void Init() {
Helper.InitConfig( _config, _expression, _memberInfo );
InitType();
InitValidation();
}
/// <summary>
/// 根据类型初始化
/// </summary>
private void InitType() {
if( Reflection.IsNumber( _memberInfo ) )
_config.SetAttribute( UiConst.Type, "number" );
}
/// <summary>
/// 初始化验证
/// </summary>
private void InitValidation() {
InitStringLength();
InitEmail();
}
/// <summary>
/// 初始化字符串长度验证
/// </summary>
private void InitStringLength() {
var attribute = _expression.GetValidationAttribute<StringLengthAttribute>();
if( attribute == null )
return;
if( attribute.MinimumLength > 0 )
_config.SetAttribute( UiConst.MinLength, attribute.MinimumLength );
if( attribute.MaximumLength > 0 )
_config.SetAttribute( UiConst.MaxLength, attribute.MaximumLength );
}
/// <summary>
/// 初始化电子邮件验证
/// </summary>
private void InitEmail() {
var attribute = _expression.GetValidationAttribute<EmailAddressAttribute>();
if( attribute == null )
return;
_config.SetAttribute( UiConst.Type, "email" );
if ( attribute.ErrorMessage.Contains( "field is not a valid e-mail address" ) )
return;
_config.SetAttribute( UiConst.EmailMessage, attribute.ErrorMessage );
}
}
}
@@ -39,24 +39,6 @@ namespace Util.Ui.Material.Forms {
return new SelectRender( _config );
}
/// <summary>
/// 渲染前操作
/// </summary>
/// <param name="writer">流写入器</param>
/// <param name="encoder">编码</param>
protected override void RenderBefore( TextWriter writer, HtmlEncoder encoder ) {
AddItems();
}
/// <summary>
/// 添加项集合
/// </summary>
private void AddItems() {
if( _config.Items.Count == 0 )
return;
this.DataSource( Util.Helpers.Json.ToJson( _config.Items,true ) );
}
/// <summary>
/// 绑定枚举
/// </summary>
@@ -1,4 +1,5 @@
using Util.Ui.Enums;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Util.Ui.Enums;
using Util.Ui.Material.Enums;
using Util.Ui.TagHelpers;
@@ -56,6 +57,10 @@ namespace Util.Ui.Material.Forms.TagHelpers {
/// </summary>
public string OnSuffixIconClick { get; set; }
/// <summary>
/// 属性表达式
/// </summary>
public ModelExpression For { get; set; }
/// <summary>
/// 模型绑定
/// </summary>
public string Model { get; set; }
+1 -1
View File
@@ -76,7 +76,7 @@ namespace Util.Ui.Configs {
/// <typeparam name="T">目标类型</typeparam>
/// <param name="name">属性名</param>
public T GetValue<T>( string name ) {
return Util.Helpers.Convert.To<T>( GetValue( name ) );
return Contains( name ) ? Util.Helpers.Convert.To<T>( AllAttributes[name].Value ) : default(T);
}
/// <summary>
+4
View File
@@ -183,5 +183,9 @@
/// 垂直
/// </summary>
public const string Vertical = "vertical";
/// <summary>
/// for
/// </summary>
public const string For = "for";
}
}
@@ -0,0 +1,30 @@
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
namespace Util.Ui.Extensions {
/// <summary>
/// 模型表达式扩展
/// </summary>
public static partial class Extensions {
/// <summary>
/// 获取成员
/// </summary>
/// <param name="expression">模型表达式</param>
public static MemberInfo GetMemberInfo( this ModelExpression expression ) {
var members = expression.Metadata.ContainerType.GetMember( expression.Metadata.PropertyName );
return members.Length == 0 ? null : members[0];
}
/// <summary>
/// 获取验证特性
/// </summary>
/// <param name="expression">模型表达式</param>
public static TAttribute GetValidationAttribute<TAttribute>( this ModelExpression expression ) {
var attribute = expression.Metadata.ValidatorMetadata.FirstOrDefault( t => t is TAttribute );
if( attribute == null )
return default( TAttribute );
return (TAttribute)attribute;
}
}
}
+13 -5
View File
@@ -89,15 +89,23 @@ namespace Util.Helpers {
}
/// <summary>
/// 获取描述项集合,文本设置为Description,值为Value
/// 获取项集合,文本设置为Description,值为Value
/// </summary>
/// <typeparam name="TEnum">枚举类型</typeparam>
public static List<Item> GetItems<TEnum>() {
TypeInfo enumType = Common.GetType<TEnum>().GetTypeInfo();
return GetItems( Common.GetType<TEnum>() );
}
/// <summary>
/// 获取项集合,文本设置为Description,值为Value
/// </summary>
/// <param name="type">枚举类型</param>
public static List<Item> GetItems( Type type ) {
TypeInfo enumType = type.GetTypeInfo();
ValidateEnum( enumType );
var result = new List<Item>();
foreach( var field in enumType.GetFields() )
AddItem<TEnum>( result, field );
AddItem( type, result, field );
return result.OrderBy( t => t.SortId ).ToList();
}
@@ -112,10 +120,10 @@ namespace Util.Helpers {
/// <summary>
/// 添加描述项
/// </summary>
private static void AddItem<TEnum>( ICollection<Item> result, FieldInfo field ) {
private static void AddItem( Type type, ICollection<Item> result, FieldInfo field ) {
if( !field.FieldType.GetTypeInfo().IsEnum )
return;
var value = GetValue<TEnum>( field.Name );
var value = GetValue( type, field.Name );
var description = Reflection.GetDescription( field );
result.Add( new Item( description, value, value ) );
}
+18 -3
View File
@@ -138,7 +138,7 @@ namespace Util.Tests.Helpers {
}
/// <summary>
/// 测试获取描述项集合
/// 测试获取项集合
/// </summary>
[Fact]
public void TestGetItems() {
@@ -153,7 +153,22 @@ namespace Util.Tests.Helpers {
}
/// <summary>
/// 测试获取描述项集合 - 可空枚举
/// 测试获取项集合
/// </summary>
[Fact]
public void TestGetItems_Type() {
var items = Util.Helpers.Enum.GetItems(typeof( EnumSample ) );
Assert.Equal( 5, items.Count );
Assert.Equal( "A", items[0].Text );
Assert.Equal( 1, items[0].Value );
Assert.Equal( "D4", items[3].Text );
Assert.Equal( 4, items[3].Value );
Assert.Equal( "E5", items[4].Text );
Assert.Equal( 5, items[4].Value );
}
/// <summary>
/// 测试获取项集合 - 可空枚举
/// </summary>
[Fact]
public void TestGetItems_Nullable() {
@@ -168,7 +183,7 @@ namespace Util.Tests.Helpers {
}
/// <summary>
/// 测试获取描述项集合 - 验证枚举类型
/// 测试获取项集合 - 验证枚举类型
/// </summary>
[Fact]
public void TestGetItems_Validate() {
@@ -203,8 +203,8 @@ namespace Util.Ui.Tests.Material.Forms {
public void TestBool() {
var result = new String();
result.Append( "<mat-radio-wrapper [dataSource]=\"[" );
result.Append( "{'text':'是','value':'true'}," );
result.Append( "{'text':'否','value':'false'}" );
result.Append( "{'text':'是','value':true}," );
result.Append( "{'text':'否','value':false}" );
result.Append( "]\"></mat-radio-wrapper>" );
Assert.Equal( result.ToString(), GetResult( _component.Bool() ) );
}
@@ -335,8 +335,8 @@ namespace Util.Ui.Tests.Material.Forms {
public void TestBool() {
var result = new String();
result.Append( "<mat-select-wrapper [dataSource]=\"[" );
result.Append( "{'text':'是','value':'true'}," );
result.Append( "{'text':'否','value':'false'}" );
result.Append( "{'text':'是','value':true}," );
result.Append( "{'text':'否','value':false}" );
result.Append( "]\"></mat-select-wrapper>" );
Assert.Equal( result.ToString(), GetResult( _component.Bool() ) );
}