diff --git a/sample/Util.Samples.Webs/Typings/util/material/textarea-wrapper.component.ts b/sample/Util.Samples.Webs/Typings/util/material/textarea-wrapper.component.ts index c8c1f756..b04ab1cc 100644 --- a/sample/Util.Samples.Webs/Typings/util/material/textarea-wrapper.component.ts +++ b/sample/Util.Samples.Webs/Typings/util/material/textarea-wrapper.component.ts @@ -5,6 +5,7 @@ import { Component, Input, ViewChild } from '@angular/core'; import { NgModel } from '@angular/forms'; import { FormControlWrapperBase } from './base/form-control-wrapper-base'; +import { MessageConfig } from '../config/message-config'; /** * Mat多行文本框包装器 @@ -83,7 +84,7 @@ export class TextareaWrapperComponent extends FormControlWrapperBase { if (this.inputModel.hasError('required')) return this.requiredMessage; if (this.inputModel.hasError('minlength')) - return this.minLengthMessage; + return this.minLengthMessage || MessageConfig.minLengthMessage.replace(/\{0\}/, String(this.minLength)); return ""; } } \ No newline at end of file diff --git a/sample/Util.Samples.Webs/Views/Home/Demo.cshtml b/sample/Util.Samples.Webs/Views/Home/Demo.cshtml index 2dbb3037..12ed92e2 100644 --- a/sample/Util.Samples.Webs/Views/Home/Demo.cshtml +++ b/sample/Util.Samples.Webs/Views/Home/Demo.cshtml @@ -24,11 +24,11 @@
- + +
- @(x.TextBox().Name( "b" ).Prefix( "$" ).MinLength( 5 ).MaxLength( 8 ).ShowClearButton( false ).ReadOnly( false ) - .Placeholder( "你不好" ).Hint( "左边" ).Hint( "右边", true ).Model( "model.value" ).Required()) + @x.TextBox().Prefix( "$" ).Placeholder( "你不好" ).Model( "model.value" ).Required().ToTextArea( 5,6 )
diff --git a/src/Util.Ui.Abstractions/Components/ITextBox.cs b/src/Util.Ui.Abstractions/Components/ITextBox.cs index 573f732a..c8de2638 100644 --- a/src/Util.Ui.Abstractions/Components/ITextBox.cs +++ b/src/Util.Ui.Abstractions/Components/ITextBox.cs @@ -5,6 +5,6 @@ namespace Util.Ui.Components { /// /// 文本框 /// - public interface ITextBox : IFormControl, IEmail, IMinLength, IMaxLength, IReadOnly { + public interface ITextBox : IFormControl, IMinLength, IMaxLength, IReadOnly { } } diff --git a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IEmail.cs b/src/Util.Ui.Abstractions/Operations/Forms/Validations/IEmail.cs deleted file mode 100644 index eaba0c74..00000000 --- a/src/Util.Ui.Abstractions/Operations/Forms/Validations/IEmail.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Util.Ui.Operations.Forms.Validations { - /// - /// 电子邮件验证 - /// - public interface IEmail { - } -} diff --git a/src/Util.Ui.Angular/Material/Configs/MaterialConst.cs b/src/Util.Ui.Angular/Material/Configs/MaterialConst.cs index 3e2529fa..990c8716 100644 --- a/src/Util.Ui.Angular/Material/Configs/MaterialConst.cs +++ b/src/Util.Ui.Angular/Material/Configs/MaterialConst.cs @@ -43,5 +43,13 @@ /// 是否显示清除按钮 /// public const string ShowClearButton = "show-clear-button"; + /// + /// 最小行数 + /// + public const string MinRows = "min-rows"; + /// + /// 最大行数 + /// + public const string MaxRows = "max-rows"; } } diff --git a/src/Util.Ui.Angular/Material/Extensions/Extensions.TextBox.cs b/src/Util.Ui.Angular/Material/Extensions/Extensions.TextBox.cs index d8f5ca04..807392cb 100644 --- a/src/Util.Ui.Angular/Material/Extensions/Extensions.TextBox.cs +++ b/src/Util.Ui.Angular/Material/Extensions/Extensions.TextBox.cs @@ -2,7 +2,7 @@ using Util.Ui.Components.Internal; using Util.Ui.Configs; using Util.Ui.Material.Configs; -using Util.Ui.Operations.Forms.Validations; +using Util.Ui.Material.Forms.Configs; namespace Util.Ui.Material.Extensions { /// @@ -41,7 +41,7 @@ namespace Util.Ui.Material.Extensions { /// 组件类型 /// 组件实例 /// 电子邮件验证错误消息 - public static TComponent Email( this TComponent component,string message = null ) where TComponent : IComponent,IEmail { + public static TComponent Email( this TComponent component,string message = null ) where TComponent : ITextBox { var option = component as IOptionConfig; option?.Config( config => { config.SetAttribute( UiConst.Type, "email" ); @@ -63,5 +63,22 @@ namespace Util.Ui.Material.Extensions { } ); return component; } + + /// + /// 转换为多行文本框 + /// + /// 组件类型 + /// 组件实例 + /// 最小行数 + /// 最大行数 + public static TComponent ToTextArea( this TComponent component, int? minRows = null,int? maxRows = null ) where TComponent : ITextBox { + var option = component as IOptionConfig; + option?.Config( config => { + config.IsTextArea = true; + config.SetAttribute( MaterialConst.MinRows, minRows ); + config.SetAttribute( MaterialConst.MaxRows, maxRows ); + } ); + return component; + } } } diff --git a/src/Util.Ui.Angular/Material/Forms/Builders/TextAreaWrapperBuilder.cs b/src/Util.Ui.Angular/Material/Forms/Builders/TextAreaWrapperBuilder.cs new file mode 100644 index 00000000..5129612b --- /dev/null +++ b/src/Util.Ui.Angular/Material/Forms/Builders/TextAreaWrapperBuilder.cs @@ -0,0 +1,14 @@ +using Util.Ui.Builders; + +namespace Util.Ui.Material.Forms.Builders { + /// + /// Mat多行文本框包装器生成器 + /// + public class TextAreaWrapperBuilder : TagBuilder { + /// + /// 初始化Mat多行文本框包装器生成器 + /// + public TextAreaWrapperBuilder() : base( "mat-textarea-wrapper" ) { + } + } +} diff --git a/src/Util.Ui.Angular/Material/Forms/Configs/TextBoxConfig.cs b/src/Util.Ui.Angular/Material/Forms/Configs/TextBoxConfig.cs index 1235e0b9..10c7d510 100644 --- a/src/Util.Ui.Angular/Material/Forms/Configs/TextBoxConfig.cs +++ b/src/Util.Ui.Angular/Material/Forms/Configs/TextBoxConfig.cs @@ -18,5 +18,10 @@ namespace Util.Ui.Material.Forms.Configs { /// TagHelper上下文 public TextBoxConfig( Context context ) : base( context ) { } + + /// + /// 是否多行文本框 + /// + public bool IsTextArea { get; set; } } } diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/SelectRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/SelectRender.cs index 9de645c7..5c1ca6b3 100644 --- a/src/Util.Ui.Angular/Material/Forms/Renders/SelectRender.cs +++ b/src/Util.Ui.Angular/Material/Forms/Renders/SelectRender.cs @@ -28,14 +28,14 @@ namespace Util.Ui.Material.Forms.Renders { protected override ITagBuilder GetTagBuilder() { var builder = new SelectWrapperBuilder(); base.Config( builder ); - Config( builder ); + ConfigSelect( builder ); return builder; } /// /// 配置 /// - private void Config( SelectWrapperBuilder builder ) { + private void ConfigSelect( SelectWrapperBuilder builder ) { ConfigUrl( builder ); ConfigDataSource( builder ); ConfigResetOption( builder ); diff --git a/src/Util.Ui.Angular/Material/Forms/Renders/TextBoxRender.cs b/src/Util.Ui.Angular/Material/Forms/Renders/TextBoxRender.cs index 47523d97..ffc87333 100644 --- a/src/Util.Ui.Angular/Material/Forms/Renders/TextBoxRender.cs +++ b/src/Util.Ui.Angular/Material/Forms/Renders/TextBoxRender.cs @@ -27,16 +27,36 @@ namespace Util.Ui.Material.Forms.Renders { /// 获取标签生成器 /// protected override ITagBuilder GetTagBuilder() { - var builder = new TextBoxWrapperBuilder(); + var builder = CreateBuilder(); base.Config( builder ); - Config( builder ); + ConfigTextArea( builder ); + ConfigTextBox( builder ); return builder; } /// - /// 配置 + /// 创建标签生成器 /// - private void Config( TextBoxWrapperBuilder builder ) { + private TagBuilder CreateBuilder() { + if( _config.IsTextArea ) + return new TextAreaWrapperBuilder(); + return new TextBoxWrapperBuilder(); + } + + /// + /// 配置多行文本框 + /// + private void ConfigTextArea( TagBuilder builder ) { + if( _config.IsTextArea == false ) + return; + builder.AddAttribute( "[minRows]", _config.GetValue( MaterialConst.MinRows ) ); + builder.AddAttribute( "[maxRows]", _config.GetValue( MaterialConst.MaxRows ) ); + } + + /// + /// 配置文本框 + /// + private void ConfigTextBox( TagBuilder builder ) { ConfigType( builder ); ConfigReadOnly( builder ); ConfigShowClearButton( builder ); @@ -67,7 +87,7 @@ namespace Util.Ui.Material.Forms.Renders { /// /// 配置验证操作 /// - private void ConfigValidations( TextBoxWrapperBuilder builder ) { + private void ConfigValidations( TagBuilder builder ) { ConfigEmail( builder ); ConfigMinLength( builder ); ConfigMaxLength( builder ); @@ -83,7 +103,7 @@ namespace Util.Ui.Material.Forms.Renders { /// /// 配置最小长度验证 /// - private void ConfigMinLength( TextBoxWrapperBuilder builder ) { + private void ConfigMinLength( TagBuilder builder ) { builder.AddAttribute( "[minLength]", _config.GetValue( UiConst.MinLength ) ); builder.AddAttribute( "minLengthMessage", _config.GetValue( UiConst.MinLengthMessage ) ); } @@ -91,8 +111,10 @@ namespace Util.Ui.Material.Forms.Renders { /// /// 配置最大长度验证 /// - private void ConfigMaxLength( TextBoxWrapperBuilder builder ) { + private void ConfigMaxLength( TagBuilder builder ) { builder.AddAttribute( "[maxLength]", _config.GetValue( UiConst.MaxLength ) ); } + + } } diff --git a/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextAreaTagHelper.cs b/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextAreaTagHelper.cs new file mode 100644 index 00000000..ae9d3e20 --- /dev/null +++ b/src/Util.Ui.Angular/Material/Forms/TagHelpers/TextAreaTagHelper.cs @@ -0,0 +1,50 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Material.Forms.Configs; +using Util.Ui.Material.Forms.Renders; +using Util.Ui.Renders; +using Util.Ui.TagHelpers; + +namespace Util.Ui.Material.Forms.TagHelpers { + /// + /// 文本框 + /// + [HtmlTargetElement( "util-textarea" )] + public class TextAreaTagHelper : FormControlTagHelperBase { + /// + /// 是否显示清除按钮 + /// + public bool ShowClearButton { get; set; } + /// + /// 只读 + /// + public bool Readonly { get; set; } + /// + /// 最小长度 + /// + public string MinLength { get; set; } + /// + /// 最小长度错误消息 + /// + public string MinLengthMessage { get; set; } + /// + /// 最大长度 + /// + public string MaxLength { get; set; } + /// + /// 最小行数 + /// + public string MinRows { get; set; } + /// + /// 最大行数 + /// + public string MaxRows { get; set; } + + /// + /// 获取渲染器 + /// + /// 上下文 + protected override IRender GetRender( Context context ) { + return new TextBoxRender( new TextBoxConfig( context ) { IsTextArea = true } ); + } + } +} diff --git a/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextAreaTagHelperTest.cs b/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextAreaTagHelperTest.cs new file mode 100644 index 00000000..89b2083c --- /dev/null +++ b/test/Util.Ui.Tests/Material/Forms/TagHelpers/TextAreaTagHelperTest.cs @@ -0,0 +1,328 @@ +using Microsoft.AspNetCore.Razor.TagHelpers; +using Util.Ui.Configs; +using Util.Ui.Enums; +using Util.Ui.Material.Configs; +using Util.Ui.Material.Enums; +using Util.Ui.Material.Forms.TagHelpers; +using Util.Ui.Tests.XUnitHelpers; +using Xunit; +using Xunit.Abstractions; +using String = Util.Helpers.String; + +namespace Util.Ui.Tests.Material.Forms.TagHelpers { + /// + /// 多行文本框测试 + /// + public class TextAreaTagHelperTest { + /// + /// 输出工具 + /// + private readonly ITestOutputHelper _output; + /// + /// 多行文本框 + /// + private readonly TextAreaTagHelper _component; + + /// + /// 测试初始化 + /// + public TextAreaTagHelperTest( ITestOutputHelper output ) { + _output = output; + _component = new TextAreaTagHelper(); + Config.IsValidate = false; + } + + /// + /// 获取结果 + /// + private string GetResult( TagHelperAttributeList contextAttributes = null, TagHelperAttributeList outputAttributes = null, TagHelperContent content = null ) { + return Helper.GetResult( _output, _component, contextAttributes, outputAttributes, content ); + } + + /// + /// 测试默认输出 + /// + [Fact] + public void TestDefault() { + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult() ); + } + + /// + /// 测试设置名称 + /// + [Fact] + public void TestName() { + var attributes = new TagHelperAttributeList { { UiConst.Name, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试禁用 + /// + [Fact] + public void TestDisabled() { + var attributes = new TagHelperAttributeList { { UiConst.Disabled, true } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试只读 + /// + [Fact] + public void TestReadOnly() { + var attributes = new TagHelperAttributeList { { UiConst.ReadOnly, true } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置占位提示 + /// + [Fact] + public void TestPlaceholder() { + var attributes = new TagHelperAttributeList { { UiConst.Placeholder, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置占位提示浮动位置 + /// + [Fact] + public void TestFloatPlaceholder() { + var attributes = new TagHelperAttributeList { { MaterialConst.FloatPlaceholder, FloatType.Never } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置起始提示 + /// + [Fact] + public void TestStartHint() { + var attributes = new TagHelperAttributeList { { MaterialConst.StartHint, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置结束提示 + /// + [Fact] + public void TestEndHint() { + var attributes = new TagHelperAttributeList { { MaterialConst.EndHint, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置前缀 + /// + [Fact] + public void TestPrefix() { + var attributes = new TagHelperAttributeList { { UiConst.Prefix, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置后缀文本 + /// + [Fact] + public void TestSuffixText() { + var attributes = new TagHelperAttributeList { { MaterialConst.SuffixText, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置后缀FontAwesome图标 + /// + [Fact] + public void TestSuffixFontAwesomeIcon() { + var attributes = new TagHelperAttributeList { { MaterialConst.SuffixFontAwesomeIcon, FontAwesomeIcon.Apple } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置后缀Material图标 + /// + [Fact] + public void TestSuffixMaterialIcon() { + var attributes = new TagHelperAttributeList { { MaterialConst.SuffixMaterialIcon, MaterialIcon.Android } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试设置后缀图标单击事件 + /// + [Fact] + public void TestOnSuffixIconClick() { + var attributes = new TagHelperAttributeList { { MaterialConst.OnSuffixIconClick, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试是否显示清除按钮 + /// + [Fact] + public void TestShowClearButton() { + var attributes = new TagHelperAttributeList { { MaterialConst.ShowClearButton, false } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + + /// + /// 测试模型绑定 + /// + [Fact] + public void TestModel() { + var attributes = new TagHelperAttributeList { { UiConst.Model, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试必填项 + /// + [Fact] + public void TestRequired() { + var attributes = new TagHelperAttributeList { { UiConst.Required, true } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试必填项错误消息 + /// + [Fact] + public void TestRequiredMessage() { + var attributes = new TagHelperAttributeList { { UiConst.RequiredMessage, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试最小长度 + /// + [Fact] + public void TestMinLength() { + var attributes = new TagHelperAttributeList { { UiConst.MinLength, 3 } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试最小长度,添加指定错误消息 + /// + [Fact] + public void TestMinLength_Message() { + var attributes = new TagHelperAttributeList { { UiConst.MinLengthMessage, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试最大长度 + /// + [Fact] + public void TestMaxLength() { + var attributes = new TagHelperAttributeList { { UiConst.MaxLength, 3 } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试变更事件 + /// + [Fact] + public void TestOnChange() { + var attributes = new TagHelperAttributeList { { UiConst.OnChange, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试获得焦点事件 + /// + [Fact] + public void TestOnFocus() { + var attributes = new TagHelperAttributeList { { UiConst.OnFocus, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试失去焦点事件 + /// + [Fact] + public void TestOnBlur() { + var attributes = new TagHelperAttributeList { { UiConst.OnBlur, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试键盘按下事件 + /// + [Fact] + public void TestOnKeydown() { + var attributes = new TagHelperAttributeList { { UiConst.OnKeydown, "a" } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试最小行数 + /// + [Fact] + public void TestMinRows() { + var attributes = new TagHelperAttributeList { { MaterialConst.MinRows, 3 } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + + /// + /// 测试最大行数 + /// + [Fact] + public void TestMaxRows() { + var attributes = new TagHelperAttributeList { { MaterialConst.MaxRows, 3 } }; + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( attributes ) ); + } + } +} \ No newline at end of file diff --git a/test/Util.Ui.Tests/Material/Forms/TextBoxTest.cs b/test/Util.Ui.Tests/Material/Forms/TextBoxTest.cs index d4a9316e..996093ae 100644 --- a/test/Util.Ui.Tests/Material/Forms/TextBoxTest.cs +++ b/test/Util.Ui.Tests/Material/Forms/TextBoxTest.cs @@ -337,5 +337,25 @@ namespace Util.Ui.Tests.Material.Forms { result.Append( "" ); Assert.Equal( result.ToString(), GetResult( _component.MaxLength( 3 ) ) ); } + + /// + /// 测试多行文本框 + /// + [Fact] + public void TestToTextArea_1() { + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( _component.ToTextArea( 3) ) ); + } + + /// + /// 测试多行文本框 + /// + [Fact] + public void TestToTextArea_2() { + var result = new String(); + result.Append( "" ); + Assert.Equal( result.ToString(), GetResult( _component.ToTextArea( 3,5 ) ) ); + } } }