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. 更新通常很容易.
如何迁移?
在你的根文件夹中打开一个终端,然后运行以下命令:
yarn ng update @angular/cli @angular/core --force
这会做如下修改:
- 更新你的package.json并安装新的软件包
- 修改tsconfig.json文件创建一个”Solution Style”配置
- 重命名
browserlist
为.browserlistrc
另一方面,如果你单独使用 yarn ng update
命令检查首先要更新哪些包会更好. Angular会给你一个要更新的包列表.
当Angular报告上面的包后,运行命令:
yarn ng update @angular/cli @angular/core ng-zorro-antd --force
如果Angular提示你的仓库有中未提交的更改,可以提交/存储它,也可以在命令中添加
--allow-dirty
参数.
配置模块
在ABP v2.x中,每个延迟加载的模块都有一个可通过单独的程序包使用的配置模块,模块配置如下:
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…
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中,我们为每个配置模块引入了辅助入口点,并且提供了一种在没有包装的情况下配置延迟加载的模块的新方法. 现在模块配置如下所示:
import { AccountConfigModule } from '@abp/ng.account/config';
@NgModule({
imports: [
// other imports
AccountConfigModule.forRoot(),
],
// providers, declarations, and bootstrap
})
export class AppModule {}
… 在app-routing.module.ts…
const routes: Routes = [
// other route configuration
{
path: 'account',
loadChildren: () => import('@abp/ng.account')
.then(m => m.AccountModule.forLazy({ redirectUrl: '/' })),
},
];
这项更改帮助我们减少了捆绑包的大小并大大缩短了构建时间. 我们相信你会注意到你的应用程序有所不同.
一个更好的例子
AppModule:
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:
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中,通过以下两种方式之一将路由添加到菜单:
从v3.0开始,我们更改了添加和修改路由的方式. 我们不再将路由存储在 ConfigState
中(破坏性更改). 而是有一个名为 RoutesService
的新服务,该服务用于添加,修补或删除菜单项. 详情请查看文档.
如何迁移?
- 检查你是否曾经使用
ConfigState
或ConfigStateService
添加路由. 请用RoutesService
的add
方法替换它们. - 检查你是否曾经修补的路由. 将其替换为
RoutesService
的patch
方法. - 仔细检查你是否使用绝对路径,并在
add
或patch
方法调用中为子菜单项提供parentName
而不是children
属性.
NavItemsService
在ABP v2.x中,通过LayoutStateService添加导航元素.
从v3.0开始,我们改变了添加和修改导航项的方式,以前的方法不再可用(破坏性更改). 详情请查看文档.
如何迁移?
- 用
NavItemsService
的addItems
方法替换所有dispatchAddNavigationElement
调用.
ngx-datatable
在v3之前,我们一直使用自定义组件 abp-table
作为默认表. 但是数据表是复杂的组件,要实现功能齐全的数据表需要大量的精力,我们计划将其引入其他功能.
从ABP v3开始,我们已切换到经过严格测试,执行良好的数据表格:ngx-datatable. 所有的ABP模块都已经实现了ngx-datatable. ThemeSharedModule
已经导出了 NgxDatatableModule
. 因此如果你在终端运行 yarn add @swimlane/ngx-datatable
来安装这个包,它将在你的应用的所有模块中可用.
为了正确设置样式,你需要在angular.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
.
这些指令的用法很简单:
@Component({
providers: [ListService],
})
export class SomeComponent {
data$ = this.list.hookToQuery(
query => this.dataService.get(query)
);
constructor(
public readonly list: ListService,
public readonly dataService: SomeDataService,
) {}
}
…在组件模板…
<ngx-datatable
[rows]="(data$ | async)?.items || []"
[count]="(data$ | async)?.totalCount || 0"
[list]="list"
default
>
<!-- column templates here -->
</ngx-datatable>
通过 NgxDatatableListDirective
绑定注入的 ListService
实例后,你不再需要担心分页或排序. 同样 NgxDatatableDefaultDirective
去除了几个属性绑定,以使ngx-datatable适合我们的样式.
一个更好的例子
<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一段时间,请确保根据此处描述的破坏性更改更新分页.
**重要说明:**abp-table没有被删除,但已被弃用并在以后的版本中删除. 请考虑切换到ngx-datatable。
过时的接口
某些接口早已被标记为已弃用,现在已将其删除.
如何迁移?
请检查你是否仍在使用Issue中列出的任何内容.