@ -0,0 +1,60 @@ |
|||
# 切换到 EF Core Oracle Devart 提供程序 |
|||
|
|||
本文介绍如何将预配置为SqlServer提供程序的 **[应用程序启动模板](Startup-Templates/Application.md)** 切换到 **Oracle** 数据库提供程序 |
|||
|
|||
> 本文档使用[Devart](https://www.devart.com/dotconnect/oracle/)公司的付费库,有关其他选项,请参见[文档](Entity-Framework-Core-Oracle.md). |
|||
|
|||
## 替换Volo.Abp.EntityFrameworkCore.SqlServer包 |
|||
|
|||
解决方案中的 `.EntityFrameworkCore` 项目依赖于 [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet包. 删除这个包并且添加相同版本的[Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) 包. |
|||
|
|||
## 替换模块依赖项 |
|||
|
|||
在 `.EntityFrameworkCore` 项目中找到 **YourProjectName*EntityFrameworkCoreModule** 类, 删除 `DependsOn` attribute 上的`typeof(AbpEntityFrameworkCoreSqlServerModule)`, 添加 `typeof(AbpEntityFrameworkCoreOracleDevartModule)` (并且替换 `using Volo.Abp.EntityFrameworkCore.SqlServer;` 为 `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`). |
|||
|
|||
## UseOracle() |
|||
|
|||
查找你的解决方案中 `UseSqlServer()`调用,替换为 `UseOracle()`. 检查下列文件: |
|||
|
|||
* `.EntityFrameworkCore` 项目中的*YourProjectName*EntityFrameworkCoreModule.cs. |
|||
* `.EntityFrameworkCore.DbMigrations` 项目中的*YourProjectName*MigrationsDbContextFactory.cs. |
|||
|
|||
找到 *YourProjectName*MigrationsDbContextFactory.cs 的 `CreateDbContext()` 方法,将以下代码块 |
|||
|
|||
```csharp |
|||
var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>() |
|||
.UseSqlServer(configuration.GetConnectionString("Default")); |
|||
``` |
|||
|
|||
替换为: |
|||
|
|||
```csharp |
|||
var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>) |
|||
new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle |
|||
( |
|||
configuration.GetConnectionString("Default") |
|||
); |
|||
``` |
|||
|
|||
> 根据你的解决方案的结构,你可能发现更多需要改变代码的文件. |
|||
|
|||
## 更改连接字符串 |
|||
|
|||
Oracle连接字符串与SQL Server连接字符串不同. 所以检查你的解决方案中所有的 `appsettings.json` 文件,更改其中的连接字符串. 有关Oracle连接字符串选项的详细内容请参见[connectionstrings.com](https://www.connectionstrings.com/oracle/). |
|||
|
|||
通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构. |
|||
|
|||
## 重新生成迁移 |
|||
|
|||
启动模板使用[Entity Framework Core的Code First迁移](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). EF Core迁移取决于所选的DBMS提供程序. 因此更改DBMS提供程序会导致迁移失败. |
|||
|
|||
* 删除 `.EntityFrameworkCore.DbMigrations` 项目下的Migrations文件夹,并重新生成解决方案. |
|||
* 在包管理控制台中运行 `Add-Migration "Initial"`(在解决方案资源管理器选择 `.DbMigrator` (或 `.Web`) 做为启动项目并且选择 `.EntityFrameworkCore.DbMigrations` 做为默认项目). |
|||
|
|||
这将创建一个配置所有数据库对象(表)的数据库迁移. |
|||
|
|||
运行 `.DbMigrator` 项目创建数据库和初始种子数据. |
|||
|
|||
## 运行应用程序 |
|||
|
|||
它已准备就绪, 只需要运行该应用程序与享受编码. |
|||
@ -0,0 +1,67 @@ |
|||
# 切换到EF Core Oracle提供程序 |
|||
|
|||
本文介绍如何将预配置为SqlServer提供程序的 **[应用程序启动模板](Startup-Templates/Application.md)** 切换到 **Oracle** 数据库提供程序 |
|||
|
|||
> 本文档使用[Devart](https://www.devart.com/dotconnect/oracle/)公司的付费库,因为它是oracle唯一支持EF Core 3.x的库 |
|||
|
|||
## 替换Volo.Abp.EntityFrameworkCore.SqlServer包 |
|||
|
|||
解决方案中的 `.EntityFrameworkCore` 项目依赖于 [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet包. 删除这个包并且添加相同版本的 [Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) 包. |
|||
|
|||
## 替换模块依赖项 |
|||
|
|||
在 `.EntityFrameworkCore` 项目中找到 **YourProjectName*EntityFrameworkCoreModule** 类, 删除 `DependsOn` attribute 上的`typeof(AbpEntityFrameworkCoreSqlServerModule)`, 添加 `typeof(AbpEntityFrameworkCoreOracleDevartModule)` (并且替换 `using Volo.Abp.EntityFrameworkCore.SqlServer;` 为 `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`). |
|||
|
|||
## UseOracle() |
|||
|
|||
Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files: |
|||
|
|||
* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. |
|||
* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. |
|||
|
|||
In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block |
|||
|
|||
查找你的解决方案中 `UseSqlServer()`调用,替换为 `UseOracle()`. 检查下列文件: |
|||
|
|||
* `.EntityFrameworkCore` 项目中的*YourProjectName*EntityFrameworkCoreModule.cs. |
|||
* `.EntityFrameworkCore.DbMigrations` 项目中的*YourProjectName*MigrationsDbContextFactory.cs. |
|||
|
|||
使用以下代码替换*YourProjectName*MigrationsDbContextFactory.cs中的 `CreateDbContext()` 方法: |
|||
|
|||
```csharp |
|||
var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>() |
|||
.UseSqlServer(configuration.GetConnectionString("Default")); |
|||
``` |
|||
|
|||
与这个 |
|||
|
|||
```csharp |
|||
var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>) |
|||
new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle |
|||
( |
|||
configuration.GetConnectionString("Default") |
|||
); |
|||
``` |
|||
|
|||
> 根据你的解决方案的结构,你可能发现更多需要改变代码的文件. |
|||
|
|||
## 更改连接字符串 |
|||
|
|||
Oracle连接字符串与SQL Server连接字符串不同. 所以检查你的解决方案中所有的 `appsettings.json` 文件,更改其中的连接字符串. 有关oracle连接字符串选项的详细内容请参见[connectionstrings.com](https://www.connectionstrings.com/oracle/). |
|||
|
|||
通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构. |
|||
|
|||
## 重新生成迁移 |
|||
|
|||
启动模板使用[Entity Framework Core的Code First迁移](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). EF Core迁移取决于所选的DBMS提供程序. 因此更改DBMS提供程序会导致迁移失败. |
|||
|
|||
* 删除 `.EntityFrameworkCore.DbMigrations` 项目下的Migrations文件夹,并重新生成解决方案. |
|||
* 在包管理控制台中运行 `Add-Migration "Initial"`(在解决方案资源管理器选择 `.DbMigrator` (或 `.Web`) 做为启动项目并且选择 `.EntityFrameworkCore.DbMigrations` 做为默认项目). |
|||
|
|||
这将创建一个配置所有数据库对象(表)的数据库迁移. |
|||
|
|||
运行 `.DbMigrator` 项目创建数据库和初始种子数据. |
|||
|
|||
## 运行应用程序 |
|||
|
|||
它已准备就绪, 只需要运行该应用程序与享受编码. |
|||
@ -1,67 +1,10 @@ |
|||
# 切换到EF Core Oracle提供程序 |
|||
# 切换到EF Core Oracle 提供程序 |
|||
|
|||
本文介绍如何将预配置为SqlServer提供程序的 **[应用程序启动模板](Startup-Templates/Application.md)** 切换到 **Oracle** 数据库提供程序 |
|||
|
|||
> 本文档使用[Devart](https://www.devart.com/dotconnect/oracle/)公司的付费库,因为它是oracle唯一支持EF Core 3.x的库 |
|||
ABP框架提供了两种不同的Oracle包集成. 你可以选择以下其中一个: |
|||
|
|||
## 替换Volo.Abp.EntityFrameworkCore.SqlServer包 |
|||
* **[Volo.Abp.EntityFrameworkCore.Oracle](Entity-Framework-Core-Oracle-Official.md)** 使用官方 & 免费的oracle驱动 ( **当前处于 beta**). |
|||
* **[Volo.Abp.EntityFrameworkCore.Oracle.Devart](Entity-Framework-Core-Oracle-Devart.md)** 使用[Devart](https://www.devart.com/)公司提供的商业(付费)驱动. |
|||
|
|||
解决方案中的 `.EntityFrameworkCore` 项目依赖于 [Volo.Abp.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.SqlServer) NuGet包. 删除这个包并且添加相同版本的 [Volo.Abp.EntityFrameworkCore.Oracle.Devart](https://www.nuget.org/packages/Volo.Abp.EntityFrameworkCore.Oracle.Devart) 包. |
|||
|
|||
## 替换模块依赖项 |
|||
|
|||
在 `.EntityFrameworkCore` 项目中找到 **YourProjectName*EntityFrameworkCoreModule** 类, 删除 `DependsOn` attribute 上的`typeof(AbpEntityFrameworkCoreSqlServerModule)`, 添加 `typeof(AbpEntityFrameworkCoreOracleDevartModule)` (或者替换 `using Volo.Abp.EntityFrameworkCore.SqlServer;` 为 `using Volo.Abp.EntityFrameworkCore.Oracle.Devart;`). |
|||
|
|||
## UseOracle() |
|||
|
|||
Find `UseSqlServer()` calls in your solution, replace with `UseOracle()`. Check the following files: |
|||
|
|||
* *YourProjectName*EntityFrameworkCoreModule.cs inside the `.EntityFrameworkCore` project. |
|||
* *YourProjectName*MigrationsDbContextFactory.cs inside the `.EntityFrameworkCore.DbMigrations` project. |
|||
|
|||
In the `CreateDbContext()` method of the *YourProjectName*MigrationsDbContextFactory.cs, replace the following code block |
|||
|
|||
查找你的解决方案中 `UseSqlServer()`调用,替换为 `UseOracle()`. 检查下列文件: |
|||
|
|||
* `.EntityFrameworkCore` 项目中的*YourProjectName*EntityFrameworkCoreModule.cs. |
|||
* `.EntityFrameworkCore` 项目中的*YourProjectName*MigrationsDbContextFactory.cs. |
|||
|
|||
使用以下代码替换*YourProjectName*MigrationsDbContextFactory.cs中的 `CreateDbContext()` 方法: |
|||
|
|||
```csharp |
|||
var builder = new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>() |
|||
.UseSqlServer(configuration.GetConnectionString("Default")); |
|||
``` |
|||
|
|||
与这个 |
|||
|
|||
```csharp |
|||
var builder = (DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>) |
|||
new DbContextOptionsBuilder<YourProjectNameMigrationsDbContext>().UseOracle |
|||
( |
|||
configuration.GetConnectionString("Default") |
|||
); |
|||
``` |
|||
|
|||
> 根据你的解决方案的结构,你可能发现更多需要改变代码的文件. |
|||
|
|||
## 更改连接字符串 |
|||
|
|||
Oracle连接字符串与SQL Server连接字符串不同. 所以检查你的解决方案中所有的 `appsettings.json` 文件,更改其中的连接字符串. 有关oracle连接字符串选项的详细内容请参见[connectionstrings.com](https://www.connectionstrings.com/oracle/). |
|||
|
|||
通常需要更改 `.DbMigrator` 和 `.Web` 项目里面的 `appsettings.json` ,但它取决于你的解决方案结构. |
|||
|
|||
## 重新生成迁移 |
|||
|
|||
启动模板使用[Entity Framework Core的Code First迁移](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/). EF Core迁移取决于所选的DBMS提供程序. 因此更改DBMS提供程序会导致迁移失败. |
|||
|
|||
* 删除 `.EntityFrameworkCore.DbMigrations` 项目下的Migrations文件夹,并重新生成解决方案. |
|||
* 在包管理控制台中运行 `Add-Migration "Initial"`(在解决方案资源管理器选择 `.DbMigrator` (或 `.Web`) 做为启动项目并且选择 `.EntityFrameworkCore.DbMigrations` 做为默认项目). |
|||
|
|||
这将创建一个配置所有数据库对象(表)的数据库迁移. |
|||
|
|||
运行 `.DbMigrator` 项目创建数据库和初始种子数据. |
|||
|
|||
## 运行应用程序 |
|||
|
|||
它已准备就绪, 只需要运行该应用程序与享受编码. |
|||
> 你可以选择一个你想要的包,如果你不知道它们之间的区别,请在网站上进行搜索. ABP框架仅提供集成,不提供第三库类库的支持. |
|||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 56 KiB |
@ -0,0 +1,363 @@ |
|||
# Angular UI v2.9 迁移到 v3.0 指南 |
|||
|
|||
## 在v3.0改变了什么? |
|||
|
|||
### Angular 10 |
|||
|
|||
新的ABP Angular UI基于Angular 10和TypeScript 3.9,我们已经放弃了对Angular 8的支持. 不过ABP模块将继续与Angular 9兼容使用. 因此如果你的项目是Angular 9,则无需更新为 Angular10. 更新通常很容易. |
|||
|
|||
#### 如何迁移? |
|||
|
|||
在你的根文件夹中打开一个终端,然后运行以下命令: |
|||
|
|||
```sh |
|||
yarn ng update @angular/cli @angular/core --force |
|||
``` |
|||
|
|||
这会做如下修改: |
|||
|
|||
- 更新你的package.json并安装新的软件包 |
|||
- 修改tsconfig.json文件创建一个"Solution Style"配置 |
|||
- 重命名 `browserlist` 为 `.browserlistrc` |
|||
|
|||
另一方面,如果你单独使用 `yarn ng update` 命令检查首先要更新哪些包会更好. Angular会给你一个要更新的包列表. |
|||
|
|||
 |
|||
|
|||
当Angular报告上面的包后,运行命令: |
|||
|
|||
```sh |
|||
yarn ng update @angular/cli @angular/core ng-zorro-antd --force |
|||
``` |
|||
|
|||
> 如果Angular提示你的仓库有中未提交的更改,可以提交/存储它,也可以在命令中添加 `--allow-dirty` 参数. |
|||
|
|||
### 配置模块 |
|||
|
|||
在ABP v2.x中,每个延迟加载的模块都有一个可通过单独的程序包使用的配置模块,模块配置如下: |
|||
|
|||
```js |
|||
import { AccountConfigModule } from '@abp/ng.account.config'; |
|||
|
|||
@NgModule({ |
|||
imports: [ |
|||
// other imports |
|||
AccountConfigModule.forRoot({ redirectUrl: '/' }), |
|||
], |
|||
// providers, declarations, and bootstrap |
|||
}) |
|||
export class AppModule {} |
|||
``` |
|||
|
|||
...在app-routing.module.ts... |
|||
|
|||
```js |
|||
const routes: Routes = [ |
|||
// other route configuration |
|||
{ |
|||
path: 'account', |
|||
loadChildren: () => import( |
|||
'./lazy-libs/account-wrapper.module' |
|||
).then(m => m.AccountWrapperModule), |
|||
}, |
|||
]; |
|||
``` |
|||
|
|||
虽然有效,但有一些缺点: |
|||
|
|||
- 每个模块都有两个独立的程序包,但实际上这些程序包是相互依赖的. |
|||
- 配置延迟加载的模块需要包装模块. |
|||
- ABP Commercial具有可扩展性系统,在根模块上配置可扩展模块会增加 bundle 的大小. |
|||
|
|||
在ABP v3.0中,我们为每个配置模块引入了辅助入口点,并且提供了一种在没有包装的情况下配置延迟加载的模块的新方法. 现在模块配置如下所示: |
|||
|
|||
```js |
|||
import { AccountConfigModule } from '@abp/ng.account/config'; |
|||
|
|||
@NgModule({ |
|||
imports: [ |
|||
// other imports |
|||
AccountConfigModule.forRoot(), |
|||
], |
|||
// providers, declarations, and bootstrap |
|||
}) |
|||
export class AppModule {} |
|||
``` |
|||
|
|||
... 在app-routing.module.ts... |
|||
|
|||
```js |
|||
const routes: Routes = [ |
|||
// other route configuration |
|||
{ |
|||
path: 'account', |
|||
loadChildren: () => import('@abp/ng.account') |
|||
.then(m => m.AccountModule.forLazy({ redirectUrl: '/' })), |
|||
}, |
|||
]; |
|||
``` |
|||
|
|||
这项更改帮助我们减少了捆绑包的大小并大大缩短了构建时间. 我们相信你会注意到你的应用程序有所不同. |
|||
|
|||
#### 一个更好的例子 |
|||
|
|||
AppModule: |
|||
|
|||
```js |
|||
import { AccountConfigModule } from '@abp/ng.account/config'; |
|||
import { CoreModule } from '@abp/ng.core'; |
|||
import { IdentityConfigModule } from '@abp/ng.identity/config'; |
|||
import { SettingManagementConfigModule } from '@abp/ng.setting-management/config'; |
|||
import { TenantManagementConfigModule } from '@abp/ng.tenant-management/config'; |
|||
import { ThemeBasicModule } from '@abp/ng.theme.basic'; |
|||
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
|||
import { NgModule } from '@angular/core'; |
|||
import { BrowserModule } from '@angular/platform-browser'; |
|||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; |
|||
import { NgxsModule } from '@ngxs/store'; |
|||
import { environment } from '../environments/environment'; |
|||
import { AppRoutingModule } from './app-routing.module'; |
|||
|
|||
@NgModule({ |
|||
imports: [ |
|||
BrowserModule, |
|||
BrowserAnimationsModule, |
|||
AppRoutingModule, |
|||
CoreModule.forRoot({ |
|||
environment, |
|||
sendNullsAsQueryParam: false, |
|||
skipGetAppConfiguration: false, |
|||
}), |
|||
ThemeSharedModule.forRoot(), |
|||
AccountConfigModule.forRoot(), |
|||
IdentityConfigModule.forRoot(), |
|||
TenantManagementConfigModule.forRoot(), |
|||
SettingManagementConfigModule.forRoot(), |
|||
ThemeBasicModule.forRoot(), |
|||
NgxsModule.forRoot(), |
|||
], |
|||
// providers, declarations, and bootstrap |
|||
}) |
|||
export class AppModule {} |
|||
``` |
|||
|
|||
AppRoutingModule: |
|||
|
|||
```js |
|||
import { DynamicLayoutComponent } from '@abp/ng.core'; |
|||
import { NgModule } from '@angular/core'; |
|||
import { RouterModule, Routes } from '@angular/router'; |
|||
|
|||
const routes: Routes = [ |
|||
{ |
|||
path: '', |
|||
component: DynamicLayoutComponent, |
|||
children: [ |
|||
{ |
|||
path: '', |
|||
pathMatch: 'full', |
|||
loadChildren: () => import('./home/home.module') |
|||
.then(m => m.HomeModule), |
|||
}, |
|||
{ |
|||
path: 'account', |
|||
loadChildren: () => import('@abp/ng.account') |
|||
.then(m => m.AccountModule.forLazy({ redirectUrl: '/' })), |
|||
}, |
|||
{ |
|||
path: 'identity', |
|||
loadChildren: () => import('@abp/ng.identity') |
|||
.then(m => m.IdentityModule.forLazy()), |
|||
}, |
|||
{ |
|||
path: 'tenant-management', |
|||
loadChildren: () => import('@abp/ng.tenant-management') |
|||
.then(m => m.TenantManagementModule.forLazy()), |
|||
}, |
|||
{ |
|||
path: 'setting-management', |
|||
loadChildren: () => import('@abp/ng.setting-management') |
|||
.then(m => m.SettingManagementModule.forLazy()), |
|||
}, |
|||
], |
|||
}, |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule.forRoot(routes)], |
|||
exports: [RouterModule], |
|||
}) |
|||
export class AppRoutingModule {} |
|||
``` |
|||
|
|||
> 你可能已经注意到我们在top级别路由组件上使用了 `DynamicLayoutComponent`. 我们这样做是为了避免不必要的渲染和闪烁. 这不是强制的,但是我们建议在你的应用程序路由中做同样的事情. |
|||
|
|||
#### 如何迁移? |
|||
|
|||
- 使用 `yarn remove` 删除你的项目的配置包. |
|||
- 从辅助入口点(例如`@abp/ng.identity/config`)导入配置模块. |
|||
- 调用所有新配置模块的静态 `forRoot`方法,即使配置没有被传递. |
|||
- 调用 `ThemeBasicModule` 的静态 `forRoot` 方法(或商业上的 `ThemeLeptonModule`),并从导入中删除 `SharedModule`(除非已在其中添加了根模块所需的任何内容). |
|||
- 在app路由模块中直接导入延迟ABP模块 (如 `() => import('@abp/ng.identity').then(...)`). |
|||
- 在所有延迟模块 `then` 中调用的静态 `forLazy` 方法,即使配置没有被传递. |
|||
- [可选]使用 `DynamicLayoutComponent` 添加空的父路由,获得更好的性能和UX. |
|||
|
|||
### RoutesService |
|||
|
|||
在ABP v2.x中,通过以下两种方式之一将路由添加到菜单: |
|||
|
|||
- [通过 `AppRoutingModule` 的 `routes` 属性](https://docs.abp.io/en/abp/2.9.0/UI/Angular/Modifying-the-Menu#via-routes-property-in-approutingmodule) |
|||
- [通过 ConfigState](https://docs.abp.io/en/abp/2.9.0/UI/Angular/Modifying-the-Menu#via-configstate) |
|||
|
|||
从v3.0开始,我们更改了添加和修改路由的方式. 我们不再将路由存储在 `ConfigState`中(破坏性更改). 而是有一个名为 `RoutesService` 的新服务,该服务用于添加,修补或删除菜单项. 详情请查看[文档](Modifying-the-Menu.md). |
|||
|
|||
#### 如何迁移? |
|||
|
|||
- 检查你是否曾经使用 `ConfigState` 或 `ConfigStateService` 添加路由. 请用 `RoutesService` 的 `add` 方法替换它们. |
|||
- 检查你是否曾经修补的路由. 将其替换为 `RoutesService` 的 `patch` 方法. |
|||
- 仔细检查你是否使用绝对路径,并在 `add` 或 `patch` 方法调用中为子菜单项提供 `parentName` 而不是 `children` 属性. |
|||
|
|||
### NavItemsService |
|||
|
|||
在ABP v2.x中,[通过LayoutStateService](https://docs.abp.io/en/abp/2.9.0/UI/Angular/Modifying-the-Menu#how-to-add-an-element-to-right-part-of-the-menu)添加导航元素. |
|||
|
|||
从v3.0开始,我们改变了添加和修改导航项的方式,以前的方法不再可用(破坏性更改). 详情请查看[文档](Modifying-the-Menu.md). |
|||
|
|||
#### 如何迁移? |
|||
|
|||
- 用 `NavItemsService` 的 `addItems` 方法替换所有 `dispatchAddNavigationElement` 调用. |
|||
|
|||
### ngx-datatable |
|||
|
|||
在v3之前,我们一直使用自定义组件 `abp-table` 作为默认表. 但是数据表是复杂的组件,要实现功能齐全的数据表需要大量的精力,我们计划将其引入其他功能. |
|||
|
|||
从ABP v3开始,我们已切换到经过严格测试,执行良好的数据表格:[ngx-datatable](https://github.com/swimlane/ngx-datatable). 所有的ABP模块都已经实现了ngx-datatable. `ThemeSharedModule` 已经导出了 `NgxDatatableModule`. 因此如果你在终端运行 `yarn add @swimlane/ngx-datatable` 来安装这个包,它将在你的应用的所有模块中可用. |
|||
|
|||
为了正确设置样式,你需要在angular.json文件的样式部分中添加以下内容: |
|||
|
|||
```json |
|||
{ |
|||
"input": "node_modules/@swimlane/ngx-datatable/index.css", |
|||
"inject": true, |
|||
"bundleName": "ngx-datatable-index" |
|||
}, |
|||
{ |
|||
"input": "node_modules/@swimlane/ngx-datatable/assets/icons.css", |
|||
"inject": true, |
|||
"bundleName": "ngx-datatable-icons" |
|||
}, |
|||
{ |
|||
"input": "node_modules/@swimlane/ngx-datatable/themes/material.css", |
|||
"inject": true, |
|||
"bundleName": "ngx-datatable-material" |
|||
} |
|||
``` |
|||
|
|||
由于尚未删除 `abp-table`, 因此以前由ABP v2.x构建的模块不会突然丢失所有. 但是它们的外观与内置ABP v3模块有所不同, 因此你可能希望将这些模块中的表转换为ngx-datatable. 为了减少将abp-table转换为ngx-datatable所需的工作量,我们修改了 `ListService` 以使其与 `ngx-datatable` 一起很好地工作,并引入了两个新指令: `NgxDatatableListDirective` 和 `NgxDatatableDefaultDirective`. |
|||
|
|||
这些指令的用法很简单: |
|||
|
|||
```js |
|||
@Component({ |
|||
providers: [ListService], |
|||
}) |
|||
export class SomeComponent { |
|||
data$ = this.list.hookToQuery( |
|||
query => this.dataService.get(query) |
|||
); |
|||
|
|||
constructor( |
|||
public readonly list: ListService, |
|||
public readonly dataService: SomeDataService, |
|||
) {} |
|||
} |
|||
``` |
|||
|
|||
...在组件模板... |
|||
|
|||
```html |
|||
<ngx-datatable |
|||
[rows]="(data$ | async)?.items || []" |
|||
[count]="(data$ | async)?.totalCount || 0" |
|||
[list]="list" |
|||
default |
|||
> |
|||
<!-- column templates here --> |
|||
</ngx-datatable> |
|||
``` |
|||
|
|||
通过 `NgxDatatableListDirective` 绑定注入的 `ListService` 实例后,你不再需要担心分页或排序. 同样 `NgxDatatableDefaultDirective` 去除了几个属性绑定,以使ngx-datatable适合我们的样式. |
|||
|
|||
#### 一个更好的例子 |
|||
|
|||
```html |
|||
<ngx-datatable |
|||
[rows]="items" |
|||
[count]="count" |
|||
[list]="list" |
|||
default |
|||
> |
|||
<!-- the grid actions column --> |
|||
<ngx-datatable-column |
|||
name="" |
|||
[maxWidth]="150" |
|||
[width]="150" |
|||
[sortable]="false" |
|||
> |
|||
<ng-template |
|||
ngx-datatable-cell-template |
|||
let-row="row" |
|||
let-i="rowIndex" |
|||
> |
|||
<abp-grid-actions |
|||
[index]="i" |
|||
[record]="row" |
|||
text="AbpUi::Actions" |
|||
></abp-grid-actions> |
|||
</ng-template> |
|||
</ngx-datatable-column> |
|||
|
|||
<!-- a basic column --> |
|||
<ngx-datatable-column |
|||
prop="someProp" |
|||
[name]="'::SomeProp' | abpLocalization" |
|||
[width]="200" |
|||
></ngx-datatable-column> |
|||
|
|||
<!-- a column with a custom template --> |
|||
<ngx-datatable-column |
|||
prop="someOtherProp" |
|||
[name]="'::SomeOtherProp' | abpLocalization" |
|||
[width]="250" |
|||
> |
|||
<ng-template |
|||
ngx-datatable-cell-template |
|||
let-row="row" |
|||
let-i="index" |
|||
> |
|||
<div abpEllipsis>{%{{{ row.someOtherProp }}}%}</div> |
|||
</ng-template> |
|||
</ngx-datatable-column> |
|||
</ngx-datatable> |
|||
``` |
|||
|
|||
#### 如何迁移? |
|||
|
|||
- 安装 `@swimlane/ngx-datatable` 包. |
|||
- 添加ngx-datatable样式到angular.json文件. |
|||
- 如果可以的话,根据上面的例子更新你的模. |
|||
- 如果你稍后需要这样做,并且打算保留abp-table一段时间,请确保根据此处描述的[破坏性更改](List-Service.md)更新分页. |
|||
|
|||
**重要说明:**abp-table没有被删除,但已被弃用并在以后的版本中删除. 请考虑切换到ngx-datatable。 |
|||
|
|||
### 过时的接口 |
|||
|
|||
某些接口早已被标记为已弃用,现在已将其删除. |
|||
|
|||
#### 如何迁移? |
|||
|
|||
请检查你是否仍在使用[Issue中列出的任何内容](https://github.com/abpframework/abp/issues/4281). |
|||
|
|||
## 下一步是什么? |
|||
|
|||
* [服务代理](Service-Proxies.md) |
|||
|
After Width: | Height: | Size: 152 KiB |
@ -1,3 +1,277 @@ |
|||
## Dynamic Forms |
|||
# 动态表单 |
|||
|
|||
目前还没有文档. 你现在可以看到[组件演示](http://bootstrap-taghelpers.abp.io/Components/DynamicForms). |
|||
`提示:` 在开始阅读本文档之前,请确保你已经看过并理解了[abp表单元素](Form-elements.md)文档. |
|||
|
|||
## 介绍 |
|||
|
|||
`abp-dynamic-form` 为给定c#模型创建bootstrap表单. |
|||
|
|||
基本用法: |
|||
|
|||
````xml |
|||
<abp-dynamic-form abp-model="@Model.MyDetailedModel"/> |
|||
```` |
|||
|
|||
Model: |
|||
|
|||
````csharp |
|||
public class DynamicFormsModel : PageModel |
|||
{ |
|||
[BindProperty] |
|||
public DetailedModel MyDetailedModel { get; set; } |
|||
|
|||
public List<SelectListItem> CountryList { get; set; } = new List<SelectListItem> |
|||
{ |
|||
new SelectListItem { Value = "CA", Text = "Canada"}, |
|||
new SelectListItem { Value = "US", Text = "USA"}, |
|||
new SelectListItem { Value = "UK", Text = "United Kingdom"}, |
|||
new SelectListItem { Value = "RU", Text = "Russia"} |
|||
}; |
|||
|
|||
public void OnGet() |
|||
{ |
|||
MyDetailedModel = new DetailedModel |
|||
{ |
|||
Name = "", |
|||
Description = "Lorem ipsum dolor sit amet.", |
|||
IsActive = true, |
|||
Age = 65, |
|||
Day = DateTime.Now, |
|||
MyCarType = CarType.Coupe, |
|||
YourCarType = CarType.Sedan, |
|||
Country = "RU", |
|||
NeighborCountries = new List<string>() { "UK", "CA" } |
|||
}; |
|||
} |
|||
|
|||
public class DetailedModel |
|||
{ |
|||
[Required] |
|||
[Placeholder("Enter your name...")] |
|||
[Display(Name = "Name")] |
|||
public string Name { get; set; } |
|||
|
|||
[TextArea(Rows = 4)] |
|||
[Display(Name = "Description")] |
|||
[InputInfoText("Describe Yourself")] |
|||
public string Description { get; set; } |
|||
|
|||
[Required] |
|||
[DataType(DataType.Password)] |
|||
[Display(Name = "Password")] |
|||
public string Password { get; set; } |
|||
|
|||
[Display(Name = "Is Active")] |
|||
public bool IsActive { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "Age")] |
|||
public int Age { get; set; } |
|||
|
|||
[Required] |
|||
[Display(Name = "My Car Type")] |
|||
public CarType MyCarType { get; set; } |
|||
|
|||
[Required] |
|||
[AbpRadioButton(Inline = true)] |
|||
[Display(Name = "Your Car Type")] |
|||
public CarType YourCarType { get; set; } |
|||
|
|||
[DataType(DataType.Date)] |
|||
[Display(Name = "Day")] |
|||
public DateTime Day { get; set; } |
|||
|
|||
[SelectItems(nameof(CountryList))] |
|||
[Display(Name = "Country")] |
|||
public string Country { get; set; } |
|||
|
|||
[SelectItems(nameof(CountryList))] |
|||
[Display(Name = "Neighbor Countries")] |
|||
public List<string> NeighborCountries { get; set; } |
|||
} |
|||
|
|||
public enum CarType |
|||
{ |
|||
Sedan, |
|||
Hatchback, |
|||
StationWagon, |
|||
Coupe |
|||
} |
|||
} |
|||
```` |
|||
|
|||
## Demo |
|||
|
|||
参阅 [动态表单demo页面](https://bootstrap-taghelpers.abp.io/Components/Dropdowns)查看示例. |
|||
|
|||
## Attributes |
|||
|
|||
### abp-model |
|||
|
|||
为动态表单设置c#模型,模型的属性以表单形式转化为输入. |
|||
|
|||
### submit-button |
|||
|
|||
可以为 `True` 或 `False`. |
|||
|
|||
如果为 `True`,则会在表单底部生成一个提交按钮. |
|||
|
|||
默认值是 `False`. |
|||
|
|||
### required-symbols |
|||
|
|||
可以为 `True` 或 `False`. |
|||
|
|||
如果为 `True`,则必需的输入将带有一个符号(*),表示它们是必需的. |
|||
|
|||
默认值是 `True`. |
|||
|
|||
## 表单内容布局 |
|||
|
|||
默认情况下,“`abp-dynamic-form` 会清除内部html并将inputs放入自身. 如果要向动态表单添加其他内容或将inputs放置到某些特定区域,可以使用`<abp-form-content />`标签. 这个标签将被表单内容替换, 而 `abp-dynamic-form` 标签的内部html的其余部分将保持不变. |
|||
|
|||
用法: |
|||
|
|||
````xml |
|||
<abp-dynamic-form abp-model="@Model.MyExampleModel"> |
|||
<div> |
|||
Some content.... |
|||
</div> |
|||
<div class="input-area"> |
|||
<abp-form-content /> |
|||
</div> |
|||
<div> |
|||
Some more content.... |
|||
</div> |
|||
</abp-dynamic-form> |
|||
```` |
|||
|
|||
## 输入排序 |
|||
|
|||
`abp-dynamic-form` 通过 `DisplayOrder` attribute对属性进行排序,然后按模型类中的属性顺序进行排序. |
|||
|
|||
默认每个属性的 `DisplayOrder` attribute值是10000. |
|||
|
|||
参见以下示例: |
|||
|
|||
````csharp |
|||
public class OrderExampleModel |
|||
{ |
|||
[DisplayOrder(10004)] |
|||
public string Name{ get; set; } |
|||
|
|||
[DisplayOrder(10005)] |
|||
public string Surname{ get; set; } |
|||
|
|||
//Default 10000 |
|||
public string EmailAddress { get; set; } |
|||
|
|||
[DisplayOrder(10003)] |
|||
public string PhoneNumber { get; set; } |
|||
|
|||
[DisplayOrder(9999)] |
|||
public string City { get; set; } |
|||
} |
|||
```` |
|||
|
|||
在这个示例中,inputs字段顺序为: `City` > `EmailAddress` > `PhoneNumber` > `Name` > `Surname`. |
|||
|
|||
## 忽略属性 |
|||
|
|||
默认情况下, `abp-dynamic-form` 会为模型类中的每个属性生成输入. 如果要忽略属性请使用 `DynamicFormIgnore` attribute. |
|||
|
|||
参见以下示例: |
|||
|
|||
````csharp |
|||
public class MyModel |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
[DynamicFormIgnore] |
|||
public string Surname { get; set; } |
|||
} |
|||
```` |
|||
|
|||
在这个示例中,不会为 `Surname` 属性生成输入. |
|||
|
|||
## 指示文本框,单选按钮组和组合框 |
|||
|
|||
如果你已经阅读了[表单元素文档](Form-elements.md),你会注意到在c#模型上 `abp-radio` 和 `abp-select` 标签非常相. 我们必须使用 `[AbpRadioButton()]` attribute来告诉 `abp-dynamic-form` 你的哪些属性是单选按钮组,哪些属性是组合框. |
|||
|
|||
参见以下示例: |
|||
|
|||
````xml |
|||
<abp-dynamic-form abp-model="@Model.MyDetailedModel"/> |
|||
```` |
|||
|
|||
Model: |
|||
|
|||
````csharp |
|||
public class DynamicFormsModel : PageModel |
|||
{ |
|||
[BindProperty] |
|||
public DetailedModel MyDetailedModel { get; set; } |
|||
|
|||
public List<SelectListItem> CountryList { get; set; } = new List<SelectListItem> |
|||
{ |
|||
new SelectListItem { Value = "CA", Text = "Canada"}, |
|||
new SelectListItem { Value = "US", Text = "USA"}, |
|||
new SelectListItem { Value = "UK", Text = "United Kingdom"}, |
|||
new SelectListItem { Value = "RU", Text = "Russia"} |
|||
}; |
|||
|
|||
public void OnGet() |
|||
{ |
|||
MyDetailedModel = new DetailedModel |
|||
{ |
|||
ComboCarType = CarType.Coupe, |
|||
RadioCarType = CarType.Sedan, |
|||
ComboCountry = "RU", |
|||
RadioCountry = "UK" |
|||
}; |
|||
} |
|||
|
|||
public class DetailedModel |
|||
{ |
|||
public CarType ComboCarType { get; set; } |
|||
|
|||
[AbpRadioButton(Inline = true)] |
|||
public CarType RadioCarType { get; set; } |
|||
|
|||
[SelectItems(nameof(CountryList))] |
|||
public string ComboCountry { get; set; } |
|||
|
|||
[AbpRadioButton()] |
|||
[SelectItems(nameof(CountryList))] |
|||
public string RadioCountry { get; set; } |
|||
} |
|||
|
|||
public enum CarType |
|||
{ |
|||
Sedan, |
|||
Hatchback, |
|||
StationWagon, |
|||
Coupe |
|||
} |
|||
} |
|||
```` |
|||
|
|||
正如你上面的例子中看到: |
|||
|
|||
* 如果在**Enum**属性上使用 `[AbpRadioButton()]`,它将是一个单选按钮组. 否则它是组合框. |
|||
* 如果在属性上使用 `[SelectItems()]` 和 `[AbpRadioButton()]`,那么它将是一个单选按钮组. |
|||
* 如果只在属性上使用 `[SelectItems()]`,它将是一个组合框. |
|||
* 如果一个属性没有使用这些属性,它将是一个文本框. |
|||
|
|||
## 本地化 |
|||
|
|||
`abp-dynamic-form` 会处理本地化. |
|||
|
|||
默认情况下, 它将尝试查找 "DisplayName:{PropertyName}" 或 "{PropertyName}" 定位本地化键,并将定位值设置为label. |
|||
|
|||
你可以使用Asp.Net Core的 `[Display()]` attribute自行设置. 可以在此属性中使用本地化密钥. 请参阅以下示例: |
|||
|
|||
````csharp |
|||
[Display(Name = "Name")] |
|||
public string Name { get; set; } |
|||
```` |
|||