diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json index 46cd1cb470..c6010b777b 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json @@ -148,6 +148,10 @@ "SuccessfullySent": "Successfully Sent", "SuccessfullyDeleted": "Successfully Deleted", "DiscountRequestDeletionWarningMessage": "Discount request will be deleted" , - "BusinessType": "Business Type" + "BusinessType": "Business Type", + "TotalQuestionCount": "Total question count", + "RemainingQuestionCount": "Remaining question count", + "TotalQuestionMustBeGreaterWarningMessage": "TotalQuestionCount must be greater than RemainingQuestionCount !", + "QuestionCountsMustBeGreaterThanZero": "TotalQuestionCount and RemainingQuestionCount must be zero or greater than zero !" } } \ No newline at end of file diff --git a/docs/en/CLI.md b/docs/en/CLI.md index 75e3e027b4..d9b6832dd5 100644 --- a/docs/en/CLI.md +++ b/docs/en/CLI.md @@ -40,7 +40,7 @@ abp new Acme.BookStore #### Options * `--template` or `-t`: Specifies the template name. Default template name is `app`, which generates a web application. Available templates: - * `app` (default): [Application template](Startup-Templates/Application.md). Additional options: + * **`app`** (default): [Application template](Startup-Templates/Application.md). Additional options: * `--ui` or `-u`: Specifies the UI framework. Default framework is `mvc`. Available frameworks: * `mvc`: ASP.NET Core MVC. There are some additional options for this template: * `--tiered`: Creates a tiered solution where Web and Http API layers are physically separated. If not specified, it creates a layered solution which is less complex and suitable for most scenarios. @@ -51,10 +51,10 @@ abp new Acme.BookStore * `--mobile` or `-m`: Specifies the mobile application framework. Default framework is `react-native`. Available frameworks: * `none`: no mobile application. * `react-native`: React Native. - * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: - * `ef`: Entity Framework Core. - * `mongodb`: MongoDB. - * `module`: [Module template](Startup-Templates/Module.md). Additional options: + * `--database-provider` or `-d`: Specifies the database provider. Default provider is `ef`. Available providers: + * `ef`: Entity Framework Core. + * `mongodb`: MongoDB. + * **`module`**: [Module template](Startup-Templates/Module.md). Additional options: * `--no-ui`: Specifies to not include the UI. This makes possible to create service-only modules (a.k.a. microservices - without UI). * `--output-folder` or `-o`: Specifies the output folder. Default value is the current directory. * `--version` or `-v`: Specifies the ABP & template version. It can be a [release tag](https://github.com/abpframework/abp/releases) or a [branch name](https://github.com/abpframework/abp/branches). Uses the latest release if not specified. Most of the times, you will want to use the latest version. diff --git a/docs/en/Dapper.md b/docs/en/Dapper.md index d8b17838eb..26fbf97e66 100644 --- a/docs/en/Dapper.md +++ b/docs/en/Dapper.md @@ -1,22 +1,24 @@ # Dapper Integration -Because Dapper's idea is that the sql statement takes precedence, and mainly provides some extension methods for the `IDbConnection` interface. +Dapper is a light-weight and simple database provider. The major benefit of using Dapper is writing T-SQL queries. It provides some extension methods for `IDbConnection` interface. -Abp does not encapsulate too many functions for Dapper. Abp Dapper provides a `DapperRepository` base class based on Abp EntityFrameworkCore, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. - -These two properties can work well with [Unit-Of-Work](Unit-Of-Work.md). +ABP does not encapsulate many functions for Dapper. ABP Dapper library provides a `DapperRepository` base class based on ABP EntityFrameworkCore module, which provides the `IDbConnection` and `IDbTransaction` properties required by Dapper. `IDbConnection` and `IDbTransaction` works well with the [ABP Unit-Of-Work](Unit-Of-Work.md). ## Installation -Please install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). +Install and configure EF Core according to [EF Core's integrated documentation](Entity-Framework-Core.md). + +`Volo.Abp.Dapper` is the library for the Dapper integration. + +You can find it on NuGet Gallery: https://www.nuget.org/packages/Volo.Abp.Dapper -`Volo.Abp.Dapper` is the main nuget package for the Dapper integration. Install it to your project (for a layered application, to your data/infrastructure layer): +Install it to your project (for a layered application, to your data/infrastructure layer): ```shell Install-Package Volo.Abp.Dapper ``` -Then add `AbpDapperModule` module dependency (`DependsOn` attribute) to your [module](Module-Development-Basics.md): +Then add `AbpDapperModule` module dependency (with `DependsOn` attribute) to your [module](Module-Development-Basics.md): ````C# using Volo.Abp.Dapper; @@ -34,9 +36,10 @@ namespace MyCompany.MyProject ## Implement Dapper Repository -The following code implements the `Person` repository, which requires EF Core's `DbContext` (MyAppDbContext). You can inject `PersonDapperRepository` to call its methods. +The following code creates the `PersonRepository`, which requires EF Core's `DbContext` (MyAppDbContext). +You can inject `PersonDapperRepository` to your services for your database operations. -`DbConnection` and `DbTransaction` are from the `DapperRepository` base class. +`DbConnection` and `DbTransaction` comes from the `DapperRepository` base class. ```C# public class PersonDapperRepository : DapperRepository, ITransientDependency diff --git a/docs/en/Object-Extensions.md b/docs/en/Object-Extensions.md index 40a7ddd5ea..8c7cca4037 100644 --- a/docs/en/Object-Extensions.md +++ b/docs/en/Object-Extensions.md @@ -200,17 +200,17 @@ ObjectExtensionManager.Instance ````csharp ObjectExtensionManager.Instance - .AddOrUpdateProperty( - "MyIntProperty", + .AddOrUpdateProperty( + "MyDateTimeProperty", options => { - options.DefaultValueFactory = () => 42; + options.DefaultValueFactory = () => DateTime.Now; }); ```` `options.DefaultValueFactory` has a higher priority than the `options.DefaultValue` . -> Tip: Use `DefaultValueFactory` option only if the default value may change over the time. If it is a constant value, then use the `DefaultValue` option. +> Tip: Use `DefaultValueFactory` option only if the default value may change over the time (like `DateTime.Now` in this example). If it is a constant value, then use the `DefaultValue` option. #### CheckPairDefinitionOnMapping diff --git a/docs/en/SignalR-Integration.md b/docs/en/SignalR-Integration.md index 0a2ebdaa54..f318967888 100644 --- a/docs/en/SignalR-Integration.md +++ b/docs/en/SignalR-Integration.md @@ -1,6 +1,6 @@ # SignalR Integration -> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages that simplify the integration and usage. +> It is already possible to follow [the standard Microsoft tutorial](https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr) to add [SignalR](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) to your application. However, ABP provides a SignalR integration packages those simplify the integration and usage. ## Installation @@ -224,4 +224,14 @@ ABP implements SignalR's `IUserIdProvider` interface to provide the current user See the [SignalR Integration Demo](https://github.com/abpframework/abp-samples/tree/master/SignalRDemo) as a sample application. It has a simple Chat page to send messages between (authenticated) users. -![signalr-demo-chat](images/signalr-demo-chat.png) \ No newline at end of file +![signalr-demo-chat](images/signalr-demo-chat.png) + +## Remarks + +ABP Framework doesn't change the SignalR. It works in your ABP Framework based application just like any other ASP.NET Core application. + +Refer to the Microsoft's documentation to [host and scale](https://docs.microsoft.com/en-us/aspnet/core/signalr/scale) your application, integrate to [Azure](https://docs.microsoft.com/en-us/aspnet/core/signalr/publish-to-azure-web-app) or [Redis backplane](https://docs.microsoft.com/en-us/aspnet/core/signalr/redis-backplane)... etc. + +## See Also + +* [Microsoft SignalR documentation](https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction) \ No newline at end of file diff --git a/docs/en/Text-Templating.md b/docs/en/Text-Templating.md index 18a360c5c8..c71787f598 100644 --- a/docs/en/Text-Templating.md +++ b/docs/en/Text-Templating.md @@ -204,13 +204,14 @@ Inline localization uses the [localization system](Localization.md) to localize Assuming you need to send an email to a user to reset her/his password. Here, the template content: ```` -{%{{{L "ResetMyPassword"}}}%} +{%{{{L "ResetMyPassword" model.name}}}%} ```` `L` function is used to localize the given key based on the current user culture. You need to define the `ResetMyPassword` key inside your localization file: ````json -"ResetMyPassword": "Click here to reset your password" +"ResetMyPasswordTitle": "Reset my password", +"ResetMyPassword": "Hi {0}, Click here to reset your password" ```` You also need to declare the localization resource to be used with this template, inside your template definition provider class: @@ -234,6 +235,7 @@ var result = await _templateRenderer.RenderAsync( "PasswordReset", //the template name new PasswordResetModel { + Name = "john", Link = "https://abp.io/example-link?userId=123&token=ABC" } ); @@ -242,7 +244,7 @@ var result = await _templateRenderer.RenderAsync( You will see the localized result: ````csharp -Click here to reset your password +Hi john, Click here to reset your password ```` > If you define the [default localization resource](Localization.md) for your application, then no need to declare the resource type for the template definition. diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md index df23fd649c..8f664a80a5 100644 --- a/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Form-elements.md @@ -88,6 +88,8 @@ You can set some of the attributes on your c# property, or directly on html tag. * `label`: Sets the label for input. * `display-required-symbol`: Adds the required symbol (*) to label if input is required. Default `True`. +`asp-format`, `name` and `value` attributes of [Asp.Net Core Input Tag Helper](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-input-tag-helper) are also valid for `abp-input` tag helper. + ### Label & Localization You can set label of your input in different ways: @@ -258,4 +260,4 @@ You can set some of the attributes on your c# property, or directly on html tag. #### Tag Attributes - `asp-items`: Sets the select data. This Should be a list of SelectListItem. -- `Inline`: If true, radio buttons will be in single line, next to each other. If false, they will be under each other. \ No newline at end of file +- `Inline`: If true, radio buttons will be in single line, next to each other. If false, they will be under each other. diff --git a/docs/zh-Hans/Dapper.md b/docs/zh-Hans/Dapper.md index 60289abd00..1f6f1aee61 100644 --- a/docs/zh-Hans/Dapper.md +++ b/docs/zh-Hans/Dapper.md @@ -1,16 +1,16 @@ # Dapper 集成 -由于Dapper的思想是sql语句优先, 且主要为`IDbConnection`接口提供了一些扩展方法. +Dapper 是轻量化的数据库提供者,使用Dapper主要的好处是编写T-SQL查询,它为 `IDbConnection` 接口提供了一些扩展方法. -Abp并没有为Dapper封装太多功能. Abp Dapper在Abp EntityFrameworkCore的基础上提供了`DapperRepository`基类, 在其中提供了Dapper需要的`IDbConnection`和`IDbTransaction`属性. - -这两个属性可以和[工作单元](Unit-Of-Work.md)很好的配合. +ABP没有为Dapper封装许多功能, ABP Dapper库在ABP EntityFrameworkCore的基础上提供了 `DapperRepository` 基类,在其中提供了Dapper需要的`IDbConnection`和`IDbTransaction`属性,这两个属性可以和[工作单元](Unit-Of-Work.md)很好的配合. ## 安装 请先根据[EF Core的集成文档](Entity-Framework-Core.md)安装并配置好EF Core. -`Volo.Abp.Dapper`是Dapper集成的主要nuget包. 将其安装到你的项目中(在分层应用程序中适用于 数据访问/基础设施层): +`Volo.Abp.Dapper`是Dapper集成的主要[nuget包](https://www.nuget.org/packages/Volo.Abp.Dapper). + +将其安装到你的项目中(在分层应用程序中适用于 数据访问/基础设施层): ```shell Install-Package Volo.Abp.Dapper diff --git a/docs/zh-Hans/Object-Extensions.md b/docs/zh-Hans/Object-Extensions.md index f925a8730a..22f4ae9322 100644 --- a/docs/zh-Hans/Object-Extensions.md +++ b/docs/zh-Hans/Object-Extensions.md @@ -176,6 +176,44 @@ ObjectExtensionManager.Instance `options` 有一个名为 `Configuration` 的字典,该字典存储对象扩展定义甚至可以扩展. EF Core使用它来将其他属性映射到数据库中的表字段. 请参阅[扩展实体文档](Customizing-Application-Modules-Extending-Entities.md). +#### 默认值 + +自动为新属性设置默认值,默认值是属性类型的自然默认值,例如: `string`: `null` , `bool`: `false` 或 `int`: `0`. + +有两种方法可以覆盖默认值: + +##### DefaultValue 选项 + +`DefaultValue` 选项可以设置任何值: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "MyIntProperty", + options => + { + options.DefaultValue = 42; + }); +```` + +##### DefaultValueFactory 选项 + +`DefaultValueFactory` 可以设置返回默认值的函数: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "MyDateTimeProperty", + options => + { + options.DefaultValueFactory = () => DateTime.Now; + }); +```` + +`options.DefaultValueFactory` 比 `options.DefaultValue` 优先级要高. + +> 提示: 只有在默认值可能发生变化时(如示例中的`DateTime.Now;`) 才使用 `DefaultValueFactory`,如果是一个常量请使用 `DefaultValue` 选项. + #### CheckPairDefinitionOnMapping 控制在映射两个可扩展对象时如何检查属性定义. 请参阅*对象到对象映射*部分,了解 `CheckPairDefinitionOnMapping` 选项. diff --git a/docs/zh-Hans/Samples/Index.md b/docs/zh-Hans/Samples/Index.md index 97569f2ae2..12fcf0ac97 100644 --- a/docs/zh-Hans/Samples/Index.md +++ b/docs/zh-Hans/Samples/Index.md @@ -45,6 +45,9 @@ * [源码](https://github.com/abpframework/abp-samples/tree/master/RabbitMqEventBus) * [分布式事件总线文档](../Distributed-Event-Bus.md) * [RabbitMQ 分布式事件总线集成文档](../Distributed-Event-Bus-RabbitMQ-Integration.md) +* **文本模板 Demo**: 文本模板系统的不同用例. + * [源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo) + * [文本模板文档](../Text-Templating.md) * **自定义认证**: 如何为ASP.NET Core MVC / Razor Pages应用程序自定义身份验证的解决方案. * [源码](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization) * 相关 "[How To](../How-To/Index.md)" 文档: diff --git a/docs/zh-Hans/Text-Templating.md b/docs/zh-Hans/Text-Templating.md new file mode 100644 index 0000000000..3d3941c607 --- /dev/null +++ b/docs/zh-Hans/Text-Templating.md @@ -0,0 +1,455 @@ +# 文本模板 + +## 介绍 + +ABP框架提供了一个简单有效的文本模板系统,文本模板用于动态渲染基于模板和模型(数据对象)内容: + +***TEMPLATE + MODEL ==render==> RENDERED CONTENT*** + +它非常类似于 ASP.NET Core Razor View (或 Page): + +*RAZOR VIEW (or PAGE) + MODEL ==render==> HTML CONTENT* + +你可以将渲染的输出用于任何目的,例如发送电子邮件或准备一些报告. + +### 示例 + +Here, a simple template: + +```` +Hello {%{{{model.name}}}%} :) +```` + +你可以定义一个含有 `Name` 属性的类来渲染这个模板: + +````csharp +public class HelloModel +{ + public string Name { get; set; } +} +```` + +如果你使用 `Name` 为 `John` 的 `HelloModel` 渲染模板,输出为: + +```` +Hello John :) +```` + +模板渲染引擎非常强大; + +* 它基于 [Scriban](https://github.com/lunet-io/scriban) 库, 所以它支持 **条件逻辑**, **循环** 等. +* 模板内容 **可以本地化**. +* 你可以定义 **布局模板** 在渲染其他模板中用做布局. +* 对于高级场景,你可以传递任何对象到模板上下文. + +### 源码 + +这里是本文开发和引用的[示例应用程序源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). + +## 安装 + +推荐使用 [ABP CLI](CLI.md) 安装包. + +### 使用 ABP CLI + +在项目目录(.csproj file)打开命令行窗口运行以下命令: + +````bash +abp add-package Volo.Abp.TextTemplating +```` + +### 手动安装 + +如果你想要手动安装; + +1. 添加 [Volo.Abp.TextTemplating](https://www.nuget.org/packages/Volo.Abp.TextTemplating) NuGet包到你的项目: + +```` +Install-Package Volo.Abp.TextTemplating +```` + +2. 添加 `AbpTextTemplatingModule` 到你的模块依赖列表: + +````csharp +[DependsOn( + //...other dependencies + typeof(AbpTextTemplatingModule) //Add the new module dependency + )] +public class YourModule : AbpModule +{ +} +```` + +## 定义模板 + +在渲染模板之前,需要定义它. 创建一个继承自 `TemplateDefinitionProvider` 的类: + +````csharp +public class DemoTemplateDefinitionProvider : TemplateDefinitionProvider +{ + public override void Define(ITemplateDefinitionContext context) + { + context.Add( + new TemplateDefinition("Hello") //template name: "Hello" + .WithVirtualFilePath( + "/Demos/Hello/Hello.tpl", //template content path + isInlineLocalized: true + ) + ); + } +} +```` + +* `context` 对象用于添加新模板或获取依赖模块定义的模板. 使用 `context.Add(...)` 定义新模板. +* `TemplateDefinition` 是代表模板的类,每个模板必须有唯一的名称(在渲染模板时使用). +* `/Demos/Hello/Hello.tpl` 是模板文件的路径. +* `isInlineLocalized` 声明针对所有语言使用一个模板(`true` 还是针对每种语言使用不同的模板(`false`). 更多内容参阅下面的本地化部分. + +### 模板内容 + +`WithVirtualFilePath` 表示我们使用[虚拟文件系统](Virtual-File-System.md)存储模板内容. 在项目内创建一个 `Hello.tpl` 文件,并在属性窗口中将其标记为"**嵌入式资源**": + +![hello-template](images/hello-template.png) + +示例 `Hello.tpl` 内容如下所示: + +```` +Hello {%{{{model.name}}}%} :) +```` + +[虚拟文件系统](Virtual-File-System.md) 需要在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法添加你的文件: + +````csharp +Configure(options => +{ + options.FileSets.AddEmbedded("TextTemplateDemo"); +}); +```` + +* `TextTemplateDemoModule`是模块类. +* `TextTemplateDemo` 是你的项目的根命名空间. + +## 渲染模板 + +`ITemplateRenderer` 服务用于渲染模板内容. + +### 示例: 渲染一个简单的模板 + +````csharp +public class HelloDemo : ITransientDependency +{ + private readonly ITemplateRenderer _templateRenderer; + + public HelloDemo(ITemplateRenderer templateRenderer) + { + _templateRenderer = templateRenderer; + } + + public async Task RunAsync() + { + var result = await _templateRenderer.RenderAsync( + "Hello", //the template name + new HelloModel + { + Name = "John" + } + ); + + Console.WriteLine(result); + } +} +```` + +* `HelloDemo` 是一个简单的类,在构造函数注入了 `ITemplateRenderer` 并在 `RunAsync` 方法中使用它. +* `RenderAsync` 有两个基本参数: + * `templateName`: 要渲染的模板名称 (本示例中是 `Hello`). + * `model`: 在模板内部用做 `model` 的对象 (本示例中是 `HelloModel` 对象). + +示例会返回以下结果: + +````csharp +Hello John :) +```` + +### 匿名模型 + +虽然建议为模板创建模型类,但在简单情况下使用匿名对象也是可行的: + +````csharp +var result = await _templateRenderer.RenderAsync( + "Hello", + new + { + Name = "John" + } +); +```` + +示例中我们并没有创建模型类,但是创建了一个匿名对象模型. + +### 大驼峰 与 小驼峰 + +PascalCase 属性名(如 `UserName`) 在模板中用做小驼峰(如 `userName`). + +## 本地化 + +可以基于当前文化对模板内容进行本地化. 以下部分描述了两种类型的本地化选项. + +### 内联本地化 + +内联本地化使用[本地化系统](Localization.md)本地化模板内的文本. + +#### 示例: 重置密码链接 + +假设你需要向用户发送电子邮件重置密码. 模板内容: + +```` +{%{{{L "ResetMyPassword"}}}%} +```` + +`L` 函数用于根据当前用户的文化来定位给定的Key,你需要在本地化文件中定义 `ResetMyPassword` 键: + +````json +"ResetMyPassword": "Click here to reset your password" +```` + +你还需要在模板定义提供程序类中声明要与此模板一起使用的本地化资源: + +````csharp +context.Add( + new TemplateDefinition( + "PasswordReset", //Template name + typeof(DemoResource) //LOCALIZATION RESOURCE + ).WithVirtualFilePath( + "/Demos/PasswordReset/PasswordReset.tpl", //template content path + isInlineLocalized: true + ) +); +```` + +当你这样渲染模板时: + +````csharp +var result = await _templateRenderer.RenderAsync( + "PasswordReset", //the template name + new PasswordResetModel + { + Link = "https://abp.io/example-link?userId=123&token=ABC" + } +); +```` + +你可以看到以下本地化结果: + +````csharp +Click here to reset your password +```` + +> 如果你为应用程序定义了 [默认本地化资源](Localization.md), 则无需声明模板定义的资源类型. + +### 多个内容本地化 + +你可能希望为每种语言创建不同的模板文件,而不是使用本地化系统本地化单个模板. 如果模板对于特定的文化(而不是简单的文本本地化)应该是完全不同的,则可能需要使用它. + +#### 示例: 欢迎电子邮件模板 + +假设你要发送电子邮件欢迎用户,但要定义基于用户的文化完全不同的模板. + +首先创建一个文件夹,将模板放在里面,像 `en.tpl`, `tr.tpl` 每一个你支持的文化: + +![multiple-file-template](images/multiple-file-template.png) + +然后在模板定义提供程序类中添加模板定义: + +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en" + ) + .WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", //template content folder + isInlineLocalized: false + ) +); +```` + +* 设置 **默认文化名称**, 当没有所需的文化模板,回退到缺省文化. +* 指定 **模板文件夹** 而不是单个模板文件. +* 设置 `isInlineLocalized` 为 `false`. + +就这些,你可以渲染当前文化的模板: + +````csharp +var result = await _templateRenderer.RenderAsync("WelcomeEmail"); +```` + +> 为了简单我们跳过了模型,但是你可以使用前面所述的模型. + +### 指定文化 + +`ITemplateRenderer` 服务如果没有指定则使用当前文化 (`CultureInfo.CurrentUICulture`). 如果你需要你可以使用 `cultureName` 参数指定文化. + +````csharp +var result = await _templateRenderer.RenderAsync( + "WelcomeEmail", + cultureName: "en" +); +```` + +## 布局模板 + +布局模板用于在其他模板之间创建共享布局. 它类似于ASP.NET Core MVC / Razor Pages中的布局系统. + +### 示例: 邮件HTML布局模板 + +例如,你想为所有电子邮件模板创建一个布局. + +首先像之前一样创建一个模板文件: + +````xml + + + + + + + {%{{{content}}}%} + + +```` + +* 布局模板必须具有 **{%{{{content}}}%}** 部分作为渲染的子内容的占位符. + +在模板定义提供程序中注册模板: + +````csharp +context.Add( + new TemplateDefinition( + "EmailLayout", + isLayout: true //SET isLayout! + ).WithVirtualFilePath( + "/Demos/EmailLayout/EmailLayout.tpl", + isInlineLocalized: true + ) +); +```` + +现在你可以将此模板用作任何其他模板的布局: + +````csharp +context.Add( + new TemplateDefinition( + name: "WelcomeEmail", + defaultCultureName: "en", + layout: "EmailLayout" //Set the LAYOUT + ).WithVirtualFilePath( + "/Demos/WelcomeEmail/Templates", + isInlineLocalized: false + ) +); +```` + +## 全局上下文 + +ABP传递 `model`,可用于访问模板内的模型. 如果需要,可以传递更多的全局变量. + +示例模板内容: + +```` +A global object value: {%{{{myGlobalObject}}}%} +```` + +模板假定它渲染上下文中的 `myGlobalObject` 对象. 你可以如下所示提供它: + +````csharp +var result = await _templateRenderer.RenderAsync( + "GlobalContextUsage", + globalContext: new Dictionary + { + {"myGlobalObject", "TEST VALUE"} + } +); +```` + +渲染的结果将是: + +```` +A global object value: TEST VALUE +```` + +## 高级功能 + +本节介绍文本模板系统的一些内部知识和高级用法. + +### 模板内容Provider + +`TemplateRenderer` 用于渲染模板,这是大多数情况下所需的模板. 但是你可以使用 `ITemplateContentProvider` 获取原始(未渲染的)模板内容. + +> `ITemplateRenderer` 内部使用 `ITemplateContentProvider` 获取原始模板内容. + +示例: + +````csharp +public class TemplateContentDemo : ITransientDependency +{ + private readonly ITemplateContentProvider _templateContentProvider; + + public TemplateContentDemo(ITemplateContentProvider templateContentProvider) + { + _templateContentProvider = templateContentProvider; + } + + public async Task RunAsync() + { + var result = await _templateContentProvider + .GetContentOrNullAsync("Hello"); + + Console.WriteLine(result); + } +} +```` + +结果是原始模板内容: + +```` +Hello {%{{{model.name}}}%} :) +```` + +* `GetContentOrNullAsync` 如果没有为请求的模板定义任何内容,则返回 `null`. +* 它可以获取 `cultureName` 参数,如果模板针对不同的文化具有不同的文件,则可以使用该参数(请参见上面的"多内容本地化"部分). + +### 模板内容贡献者 + +`ITemplateContentProvider` 服务使用 `ITemplateContentContributor` 实现来查找模板内容. 有一个预实现的内容贡献者 `VirtualFileTemplateContentContributor`,它从上面描述的虚拟文件系统中获取模板内容. + +你可以实现 `ITemplateContentContributor` 从另一个源读取原始模板内容. + +示例: + +````csharp +public class MyTemplateContentProvider + : ITemplateContentContributor, ITransientDependency +{ + public async Task GetOrNullAsync(TemplateContentContributorContext context) + { + var templateName = context.TemplateDefinition.Name; + + //TODO: Try to find content from another source + return null; + } +} + +```` + +如果源无法找到内容, 则返回 `null`, `ITemplateContentProvider` 将回退到下一个贡献者. + +### Template Definition Manager + +`ITemplateDefinitionManager` 服务可用于获取模板定义(由模板定义提供程序创建). + +## 另请参阅 + +* 本文开发和引用的[应用程序示例源码](https://github.com/abpframework/abp-samples/tree/master/TextTemplateDemo). +* [本地化系统](Localization.md). +* [虚拟文件系统](Virtual-File-System.md). \ No newline at end of file diff --git a/docs/zh-Hans/UI/Angular/Confirmation-Service.md b/docs/zh-Hans/UI/Angular/Confirmation-Service.md index a1a181837c..7f47c7d8a1 100644 --- a/docs/zh-Hans/UI/Angular/Confirmation-Service.md +++ b/docs/zh-Hans/UI/Angular/Confirmation-Service.md @@ -49,7 +49,7 @@ this.confirmation - `message` 和 `title` 参数接收字符串,本地化Key或本地化对象. 参阅[本地化文档](./Localization.md) - `Confirmation.Status` 是一个枚举,具有三个属性; - `Confirmation.Status.confirm` 是一个关闭事件值,当通过确认按钮关闭弹出窗口时触发此事件. - - `Confirmation.Status.reject` 是一个关闭事件值,当通过“取消”按钮关闭弹出窗口时触发此事件. + - `Confirmation.Status.reject` 是一个关闭事件值,当通过取消按钮关闭弹出窗口时触发此事件. - `Confirmation.Status.dismiss` 是一个关闭事件值,当通过按Escape键关闭弹出窗口时触发此事件. 如果你对确认状态不感兴趣,则不必订阅返回的observable: @@ -66,6 +66,7 @@ this.confirmation.error('You are not authorized.', 'Error'); const options: Partial = { hideCancelBtn: false, hideYesBtn: false, + dismissible: false, cancelText: 'Close', yesText: 'Confirm', messageLocalizationParams: ['Demo'], @@ -81,8 +82,9 @@ this.confirmation.warn( - `hideCancelBtn` 选项为 `true` 时隐藏取消按钮. 默认值为 `false`. - `hideYesBtn` 选项为 `true` 时隐藏确认按钮. 默认值为 `false`. -- `cancelText` 是取消按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Cancel`. -- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值是 `AbpUi::Yes`. +- `dismissible`选项允许通过按Escape键或单击背景来取消确认弹出窗口. 默认值为 `true`. +- `cancelText` 是取消按钮的文本,可以传递本地化键或本地化对象. 默认值为 `AbpUi::Cancel`. +- `yesText` 是确定按钮的文本,可以传递本地化键或本地化对象. 默认值为 `AbpUi::Yes`. - `messageLocalizationParams`是用于消息本地化的插值参数. - `titleLocalizationParams` 是标题本地化的插值参数. diff --git a/docs/zh-Hans/UI/Angular/Track-By-Service.md b/docs/zh-Hans/UI/Angular/Track-By-Service.md index 7b664141ff..53c9dcbf1c 100644 --- a/docs/zh-Hans/UI/Angular/Track-By-Service.md +++ b/docs/zh-Hans/UI/Angular/Track-By-Service.md @@ -96,3 +96,7 @@ class DemoComponent { trackByTenantAccountId = trackByDeep('tenant', 'account', 'id'); } ``` + +## 下一步是什么? + +- [ListService](./List-Service.md) \ No newline at end of file diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index d61aa63ce6..a3becc073b 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -181,6 +181,10 @@ "path": "Object-To-Object-Mapping.md" }, + { + "text": "文本模板", + "path": "Text-Templating.md" + }, { "text": "JSON序列化" }, diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs index 83093e0370..d15870e7c1 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/EntityExtensionDto.cs @@ -9,6 +9,5 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending public Dictionary Properties { get; set; } public Dictionary Configuration { get; set; } - } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs new file mode 100644 index 0000000000..4f2dc526eb --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +{ + [Serializable] + public class ExtensionEnumDto + { + public List Fields { get; set; } + + public string LocalizationResource { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs new file mode 100644 index 0000000000..ddc7c349d4 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ExtensionEnumFieldDto.cs @@ -0,0 +1,12 @@ +using System; + +namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending +{ + [Serializable] + public class ExtensionEnumFieldDto + { + public string Name { get; set; } + + public object Value { get; set; } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs index fd9665eab1..0338bbd4dc 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.Contracts/Volo/Abp/AspNetCore/Mvc/ApplicationConfigurations/ObjectExtending/ObjectExtensionsDto.cs @@ -7,5 +7,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ApplicationConfigurations.ObjectExtending public class ObjectExtensionsDto { public Dictionary Modules { get; set; } + + public Dictionary Enums { get; set; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs index d925fd11e8..761b72ec99 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperExtensions.cs @@ -2,20 +2,31 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.TagHelpers; using System.Text.Encodings.Web; -using Volo.Abp.Threading; namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions { public static class TagHelperExtensions { - public static async Task ProcessAndGetOutputAsync(this TagHelper tagHelper, TagHelperAttributeList attributeList, TagHelperContext context, string tagName = "div", TagMode tagMode = TagMode.SelfClosing) + public static async Task ProcessAndGetOutputAsync( + this TagHelper tagHelper, + TagHelperAttributeList attributeList, + TagHelperContext context, + string tagName = "div", + TagMode tagMode = TagMode.SelfClosing) { - var innerOutput = new TagHelperOutput(tagName, attributeList, (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) + var innerOutput = new TagHelperOutput( + tagName, + attributeList, + (useCachedResult, encoder) => Task.Run(() => new DefaultTagHelperContent())) { TagMode = tagMode }; - - var innerContext = new TagHelperContext(attributeList, context.Items, Guid.NewGuid().ToString()); + + var innerContext = new TagHelperContext( + attributeList, + context.Items, + Guid.NewGuid().ToString() + ); tagHelper.Init(context); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs index f264d21cd5..16a700f4bc 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelper.cs @@ -33,6 +33,13 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form [ViewContext] public ViewContext ViewContext { get; set; } + [HtmlAttributeName("asp-format")] + public string Format { get; set; } + + public string Name { get; set; } + + public string Value { get; set; } + public AbpInputTagHelper(AbpInputTagHelperService tagHelperService) : base(tagHelperService) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs index a2bb92a14d..4ddf44d2d6 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs @@ -91,7 +91,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form inputHtml + label : label + inputHtml; - return innerContent + infoHtml + validation; + return innerContent + infoHtml + validation; } protected virtual string SurroundInnerHtmlAndGet(TagHelperContext context, TagHelperOutput output, string innerHtml, bool isCheckbox) @@ -103,30 +103,56 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form protected virtual TagHelper GetInputTagHelper(TagHelperContext context, TagHelperOutput output) { - var textAreaAttribute = TagHelper.AspFor.ModelExplorer.GetAttribute