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 ) ) );
+ }
}
}