组件替换
您可以用自定义组件替换某些ABP组件。
您可以替换但无法自定义默认ABP组件的原因是:禁用或更改该组件的某些部分可能会导致问题。因此,我们将这些组件称为可替换组件。
如何替换组件
创建一个您想要使用的新组件来代替某个ABP组件。
然后,打开 app.component.ts 文件,执行 ReplaceableComponentsService 的 add 方法,将您的组件替换为ABP组件,如下所示:
import { ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { eIdentityComponents } from '@abp/ng.identity'; // 导入 eIdentityComponents 枚举
import { Component, inject } from '@angular/core';
//...
@Component(/* 组件元数据 */)
export class AppComponent {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: YourNewRoleComponent,
key: eIdentityComponents.Roles,
});
}
}
如何替换布局
每个ABP主题包都有3个名为 ApplicationLayoutComponent、AccountLayoutComponent、EmptyLayoutComponent 的布局。这些布局可以用相同的方式替换。
布局组件模板应包含
<router-outlet></router-outlet>元素。
以下示例描述了如何替换 ApplicationLayoutComponent:
在 angular 文件夹中运行以下命令以生成一个布局:
yarn ng generate component my-application-layout
在您希望页面加载的位置,将以下代码添加到布局模板 (my-application-layout.component.html) 中。
<router-outlet></router-outlet>
打开 src/app 文件夹中的 app.component.ts 文件,并按如下所示进行修改:
import { ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入用于组件键的 eThemeBasicComponents 枚举
import { MyApplicationLayoutComponent } from './my-application-layout/my-application-layout.component'; // 导入 MyApplicationLayoutComponent
import { Component, inject } from '@angular/core';
@Component(/* 组件元数据 */)
export class AppComponent {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: MyApplicationLayoutComponent,
key: eThemeBasicComponents.ApplicationLayout,
});
}
}
如果您希望在运行时替换布局组件(例如:通过按按钮更改布局),请将
ReplaceableComponentsService的add方法的第二个参数传递为true。DynamicLayoutComponent 使用 router-outlet 加载内容。当add方法的第二个参数为true时,路由将被刷新,因此请谨慎使用。您的组件状态将消失,并且任何初始化逻辑(包括 HTTP 请求)都将重复执行。
布局组件
如何添加新的布局组件
要添加新的布局组件,您需要遵循以下步骤:
步骤 1:创建一个新的 Angular 组件
此组件应具有用于动态内容加载的 router-outlet。您可以使用 Angular CLI 创建一个新组件。在终端中运行以下命令:
ng generate component new-layout
此命令将创建一个名为 new-layout 的新组件。现在,打开 new-layout.component.html 文件并向其中添加一个 router-outlet:
<router-outlet></router-outlet>
这个 router-outlet 将充当一个占位符,Angular 会根据当前路由器状态动态填充内容。
步骤 2:为布局组件定义一个变量
虽然此步骤是可选的,但如果您打算多次使用布局组件的值,它可能会很有用。您可以像这样为布局组件定义一个变量:
export const eCustomLayout = {
key: "CustomLayout",
component: "CustomLayoutComponent",
};
在此变量中,key 是布局组件的唯一标识符,component 是布局组件的名称。
当您需要引用布局组件时,可以使用此变量。
步骤 3:将布局组件添加到 ABP 可替换系统中
接下来,您需要将新的布局组件添加到 ReplaceableComponentsService 中。此服务允许您动态地将一个组件替换为另一个组件。
您可以通过为 provideAppInitializer 定义一个使用工厂函数的提供者来实现。在此函数中,您注入 ReplaceableComponentsService 并使用其 add 方法来添加新的布局组件。
操作方法如下:
export const CUSTOM_LAYOUT_PROVIDERS = [
provideAppInitializer(()=>{
configureLayoutFn();
}),
];
function configureLayoutFn() {
const service = inject(ReplaceableComponentsService);
service.add({
key: eCustomLayout.component,
component: CustomLayoutComponent,
});
}
在此代码中,configureLayoutFn 是一个将新布局组件添加到 ReplaceableComponentsService 的工厂函数。provideAppInitializer 提供者在应用程序启动时运行此函数。
注意:(别忘了:您应该在 app.config.ts 文件中添加 CUSTOM_LAYOUT_PROVIDERS)
步骤 4:定义应用程序的动态布局
最后,您需要定义应用程序的动态布局。这是一个映射,其中键是布局键,值是布局组件。
您可以像这样将新布局添加到现有布局中:
export const myDynamicLayouts = new Map<string, string>([...DEFAULT_DYNAMIC_LAYOUTS, [eCustomLayout.key, eCustomLayout.component]]);
步骤 5:将动态布局传递给 Core 提供者
最后一步是使用 withOptions 方法将动态布局传递给 provideAbpCore。此方法允许您使用静态方法配置提供者。
操作方法如下:
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideAbpCore(
withOptions({
dynamicLayouts: myDynamicLayouts,
environment,
registerLocaleFn: registerLocale(),
}),
),
],
};
在此代码中,myDynamicLayouts 是您之前定义的动态布局映射。我们使用 withOptions 方法将此映射传递给 provideAbpCore。
现在您已经定义了新布局,可以在路由器定义中使用它。您可以通过添加使用新布局的新路由来实现。
操作方法如下:
// route.provider.ts
import { eCustomLayout } from './custom-layout/custom-layout.provider';
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { provideAppInitializer } from '@angular/core';
export const APP_ROUTE_PROVIDER = [
provideAppInitializer(() => {
configureRoutes();
}),
];
function configureRoutes() {
const routes = inject(RoutesService);
routes.add([
{
path: '/',
name: '::Menu:Home',
iconClass: 'fas fa-home',
order: 1,
layout: eLayoutType.application,
},
{
path: '/dashboard',
name: '::Menu:Dashboard',
iconClass: 'fas fa-chart-line',
order: 2,
layout: eCustomLayout.key as eLayoutType,
requiredPolicy: 'MyProjectName.Dashboard.Host || MyProjectName.Dashboard.Tenant',
},
]);
}
如何替换 LogoComponent
注意
- 如果您的目标仅仅是更改徽标图像或应用程序名称,则无需替换组件。建议通过
@abp/ng.theme.shared提供徽标,以便所有主题/组件都能一致地使用它:
// app.config.ts
import { provideLogo, withEnvironmentOptions } from '@abp/ng.theme.shared';
import { environment } from './environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
provideLogo(withEnvironmentOptions(environment)),
],
};
如果您仍然希望完全替换徽标组件的 UI,请按照以下步骤操作:
在 angular 文件夹中运行以下命令,创建一个名为 LogoComponent 的新组件。
yarn ng generate component logo --inlineTemplate --inlineStyle
打开 src/app/logo 文件夹中生成的 logo.component.ts,并将其内容替换为以下内容:
import { Component } from "@angular/core";
@Component({
selector: "app-logo",
template: `
<a class="navbar-brand" routerLink="/">
<!-- 更改 img src -->
<img
src="https://via.placeholder.com/100x50/343a40/FF0000?text=MyLogo"
alt="logo"
width="100%"
height="auto"
/>
</a>
`,
})
export class LogoComponent {}
打开 src/app 文件夹中的 app.component.ts,并按如下所示进行修改:
import { ..., ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { LogoComponent } from './logo/logo.component'; // 导入 LogoComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入 eThemeBasicComponents
//...
@Component(/* 组件元数据 */)
export class AppComponent implements OnInit {
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: LogoComponent,
key: eThemeBasicComponents.Logo,
});
}
}
最终的 UI 如下所示:
如何替换 RoutesComponent
在 angular 文件夹中运行以下命令,创建一个名为 RoutesComponent 的新组件。
yarn ng generate component routes
打开 src/app/routes 文件夹中生成的 routes.component.ts,并将其内容替换为以下内容:
import { Component, HostBinding } from "@angular/core";
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { LocalizationPipe, PermissionDirective } from "@abp/ng.core";
import { EllipsisDirective } from '@abp/ng.theme.shared';
@Component({
selector: "app-routes",
templateUrl: "routes.component.html",
imports: [
CommonModule,
RouterModule,
NgbDropdownModule,
PermissionDirective,
EllipsisDirective,
LocalizationPipe,
]
})
export class RoutesComponent {
@HostBinding("class.mx-auto")
marginAuto = true;
get smallScreen() {
return window.innerWidth < 992;
}
}
打开 src/app/routes 文件夹中生成的 routes.component.html,并将其内容替换为以下内容:
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" routerLink="/"
><i class="fas fa-home"></i> {{ '::Menu:Home' | abpLocalization
}}</a
>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/my-page"
><i class="fas fa-newspaper mr-1"></i>My Page</a
>
</li>
<li
#navbarRootDropdown
[abpVisibility]="routeContainer"
class="nav-item dropdown"
display="static"
(click)="
navbarRootDropdown.expand
? (navbarRootDropdown.expand = false)
: (navbarRootDropdown.expand = true)
"
>
<a
class="nav-link dropdown-toggle"
data-toggle="dropdown"
href="javascript:void(0)"
>
<i class="fas fa-wrench"></i>
{{ 'AbpUiNavigation::Menu:Administration' | abpLocalization }}
</a>
<div
#routeContainer
class="dropdown-menu border-0 shadow-sm"
(click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && navbarRootDropdown.expand"
>
<div
class="dropdown-submenu"
ngbDropdown
#dropdownSubmenu="ngbDropdown"
placement="right-top"
[autoClose]="true"
*abpPermission="'AbpIdentity.Roles || AbpIdentity.Users'"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<a
abpEllipsis="210px"
[abpEllipsisEnabled]="!smallScreen"
role="button"
class="btn d-block text-start dropdown-toggle"
>
<i class="fa fa-id-card-o"></i>
{{ 'AbpIdentity::Menu:IdentityManagement' | abpLocalization }}
</a>
</div>
<div
#childrenContainer
class="dropdown-menu border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
>
<div class="dropdown-submenu" *abpPermission="'AbpIdentity.Roles'">
<a class="dropdown-item" routerLink="/identity/roles">
{{ 'AbpIdentity::Roles' | abpLocalization }}</a
>
</div>
<div class="dropdown-submenu" *abpPermission="'AbpIdentity.Users'">
<a class="dropdown-item" routerLink="/identity/users">
{{ 'AbpIdentity::Users' | abpLocalization }}</a
>
</div>
</div>
</div>
<div
class="dropdown-submenu"
ngbDropdown
#dropdownSubmenu="ngbDropdown"
placement="right-top"
[autoClose]="true"
*abpPermission="'AbpTenantManagement.Tenants'"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<a
abpEllipsis="210px"
[abpEllipsisEnabled]="!smallScreen"
role="button"
class="btn d-block text-start dropdown-toggle"
>
<i class="fa fa-users"></i>
{{ 'AbpTenantManagement::Menu:TenantManagement' | abpLocalization
}}
</a>
</div>
<div
#childrenContainer
class="dropdown-menu border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
>
<div
class="dropdown-submenu"
*abpPermission="'AbpTenantManagement.Tenants'"
>
<a class="dropdown-item" routerLink="/tenant-management/tenants">
{{ 'AbpTenantManagement::Tenants' | abpLocalization }}</a
>
</div>
</div>
</div>
</div>
</li>
</ul>
打开 src/app 文件夹中的 app.component.ts,并按如下所示进行修改:
import { ..., ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { RoutesComponent } from './routes/routes.component'; // 导入 RoutesComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入 eThemeBasicComponents
//...
@Component(/* 组件元数据 */)
export class AppComponent implements OnInit {
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: RoutesComponent,
key: eThemeBasicComponents.Routes,
});
}
}
最终的 UI 如下所示:
如何替换 NavItemsComponent
在 angular 文件夹中运行以下命令,创建一个名为 NavItemsComponent 的新组件。
yarn ng generate component nav-items
打开 src/app/nav-items 文件夹中生成的 nav-items.component.ts,并将其内容替换为以下内容:
import {
AuthService,
ConfigStateService,
CurrentUserDto,
LanguageInfo,
NAVIGATE_TO_MANAGE_PROFILE,
SessionStateService,
LocalizationPipe
} from '@abp/ng.core';
import { Component, inject, Inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import snq from 'snq';
@Component({
selector: 'app-nav-items',
templateUrl: 'nav-items.component.html',
imports: [
CommonModule,
FormsModule,
NgbDropdownModule,
LocalizationPipe
]
})
export class NavItemsComponent {
private configState = inject(ConfigStateService);
private authService = inject(AuthService);
private sessionState = inject(SessionStateService);
@Inject(NAVIGATE_TO_MANAGE_PROFILE) public navigateToManageProfile: any;
currentUser$: Observable<CurrentUserDto> = this.configState.getOne$('currentUser');
selectedTenant$ = this.sessionState.getTenant$();
languages$: Observable<LanguageInfo[]> = this.configState.getDeep$('localization.languages');
get smallScreen(): boolean {
return window.innerWidth < 992;
}
get defaultLanguage$(): Observable<string> {
return this.languages$.pipe(
map(
languages =>
snq(
() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName
),
''
)
);
}
get dropdownLanguages$(): Observable<LanguageInfo[]> {
return this.languages$.pipe(
map(
languages =>
snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)),
[]
)
);
}
get selectedLangCulture(): string {
return this.sessionState.getLanguage();
}
onChangeLang(cultureName: string) {
this.sessionState.setLanguage(cultureName);
}
navigateToLogin() {
this.authService.navigateToLogin();
}
logout() {
this.authService.logout().subscribe();
}
}
打开 src/app/nav-items 文件夹中生成的 nav-items.component.html,并将其内容替换为以下内容:
<ul class="navbar-nav">
<input
type="search"
placeholder="Search"
class="bg-transparent border-0 text-white"
/>
<li class="nav-item d-flex align-items-center">
<div
*ngIf="(dropdownLanguages$ | async)?.length > 0"
class="dropdown"
ngbDropdown
#languageDropdown="ngbDropdown"
display="static"
>
<a
ngbDropdownToggle
class="nav-link"
href="javascript:void(0)"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{{ defaultLanguage$ | async }}
</a>
<div
class="dropdown-menu dropdown-menu-right border-0 shadow-sm"
aria-labelledby="dropdownMenuLink"
[class.d-block]="smallScreen && languageDropdown.isOpen()"
>
<a
*ngFor="let lang of dropdownLanguages$ | async"
href="javascript:void(0)"
class="dropdown-item"
(click)="onChangeLang(lang.cultureName)"
>{{ lang?.displayName }}</a
>
</div>
</div>
</li>
<li class="nav-item d-flex align-items-center">
<ng-template #loginBtn>
<a role="button" class="nav-link pointer" (click)="navigateToLogin()"
>{{ 'AbpAccount::Login' | abpLocalization }}</a
>
</ng-template>
<div
*ngIf="(currentUser$ | async)?.isAuthenticated; else loginBtn"
ngbDropdown
class="dropdown"
#currentUserDropdown="ngbDropdown"
display="static"
>
<a
ngbDropdownToggle
class="nav-link"
href="javascript:void(0)"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<small *ngIf="(selectedTenant$ | async)?.name as tenantName"
><i>{{ tenantName }}</i>\</small
>
<strong>{{ (currentUser$ | async)?.userName }}</strong>
</a>
<div
class="dropdown-menu dropdown-menu-right border-0 shadow-sm"
aria-labelledby="dropdownMenuLink"
[class.d-block]="smallScreen && currentUserDropdown.isOpen()"
>
<a class="dropdown-item pointer" (click)="navigateToManageProfile()"
><i class="fa fa-cog mr-1"></i>{{ 'AbpAccount::MyAccount' |
abpLocalization }}</a
>
<a class="dropdown-item" href="javascript:void(0)" (click)="logout()"
><i class="fa fa-power-off mr-1"></i>{{ 'AbpUi::Logout' |
abpLocalization }}</a
>
</div>
</div>
</li>
</ul>
打开 src/app 文件夹中的 app.component.ts,并按如下所示进行修改:
import { ..., ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { NavItemsComponent } from './nav-items/nav-items.component'; // 导入 NavItemsComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入 eThemeBasicComponents
//...
@Component(/* 组件元数据 */)
export class AppComponent implements OnInit {
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: NavItemsComponent,
key: eThemeBasicComponents.NavItems,
});
}
}
最终的 UI 如下所示:
另请参阅
- 可替换组件如何与扩展一起工作
- 如何替换 PermissionManagementComponent```json //[doc-seo] { "Description": "了解如何用自定义组件替换ABP组件,以增强灵活性,同时保持系统完整性。" }
# 组件替换
您可以用自定义组件替换某些ABP组件。
您**可以替换**但**无法自定义**默认ABP组件的原因是:禁用或更改该组件的某些部分可能会导致问题。因此,我们将这些组件称为*可替换组件*。
## 如何替换组件
创建一个您想要使用的新组件来代替某个ABP组件。
然后,打开 `app.component.ts` 文件,执行 `ReplaceableComponentsService` 的 `add` 方法,将您的组件替换为ABP组件,如下所示:
```js
import { ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { eIdentityComponents } from '@abp/ng.identity'; // 导入 eIdentityComponents 枚举
import { Component, inject } from '@angular/core';
//...
@Component(/* 组件元数据 */)
export class AppComponent {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: YourNewRoleComponent,
key: eIdentityComponents.Roles,
});
}
}
如何替换布局
每个ABP主题包都有3个名为 ApplicationLayoutComponent、AccountLayoutComponent、EmptyLayoutComponent 的布局。这些布局可以用相同的方式替换。
布局组件模板应包含
<router-outlet></router-outlet>元素。
以下示例描述了如何替换 ApplicationLayoutComponent:
在 angular 文件夹中运行以下命令以生成一个布局:
yarn ng generate component my-application-layout
在您希望页面加载的位置,将以下代码添加到布局模板 (my-application-layout.component.html) 中。
<router-outlet></router-outlet>
打开 src/app 文件夹中的 app.component.ts 文件,并按如下所示进行修改:
import { ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入用于组件键的 eThemeBasicComponents 枚举
import { MyApplicationLayoutComponent } from './my-application-layout/my-application-layout.component'; // 导入 MyApplicationLayoutComponent
import { Component, inject } from '@angular/core';
@Component(/* 组件元数据 */)
export class AppComponent {
private replaceableComponents = inject(ReplaceableComponentsService);
constructor() {
this.replaceableComponents.add({
component: MyApplicationLayoutComponent,
key: eThemeBasicComponents.ApplicationLayout,
});
}
}
如果您希望在运行时替换布局组件(例如:通过按按钮更改布局),请将
ReplaceableComponentsService的add方法的第二个参数传递为true。DynamicLayoutComponent 使用 router-outlet 加载内容。当add方法的第二个参数为true时,路由将被刷新,因此请谨慎使用。您的组件状态将消失,并且任何初始化逻辑(包括 HTTP 请求)都将重复执行。
布局组件
如何添加新的布局组件
要添加新的布局组件,您需要遵循以下步骤:
步骤 1:创建一个新的 Angular 组件
此组件应具有用于动态内容加载的 router-outlet。您可以使用 Angular CLI 创建一个新组件。在终端中运行以下命令:
ng generate component new-layout
此命令将创建一个名为 new-layout 的新组件。现在,打开 new-layout.component.html 文件并向其中添加一个 router-outlet:
<router-outlet></router-outlet>
这个 router-outlet 将充当一个占位符,Angular 会根据当前路由器状态动态填充内容。
步骤 2:为布局组件定义一个变量
虽然此步骤是可选的,但如果您打算多次使用布局组件的值,它可能会很有用。您可以像这样为布局组件定义一个变量:
export const eCustomLayout = {
key: "CustomLayout",
component: "CustomLayoutComponent",
};
在此变量中,key 是布局组件的唯一标识符,component 是布局组件的名称。
当您需要引用布局组件时,可以使用此变量。
步骤 3:将布局组件添加到 ABP 可替换系统中
接下来,您需要将新的布局组件添加到 ReplaceableComponentsService 中。此服务允许您动态地将一个组件替换为另一个组件。
您可以通过为 provideAppInitializer 定义一个使用工厂函数的提供者来实现。在此函数中,您注入 ReplaceableComponentsService 并使用其 add 方法来添加新的布局组件。
操作方法如下:
export const CUSTOM_LAYOUT_PROVIDERS = [
provideAppInitializer(()=>{
configureLayoutFn();
}),
];
function configureLayoutFn() {
const service = inject(ReplaceableComponentsService);
service.add({
key: eCustomLayout.component,
component: CustomLayoutComponent,
});
}
在此代码中,configureLayoutFn 是一个将新布局组件添加到 ReplaceableComponentsService 的工厂函数。provideAppInitializer 提供者在应用程序启动时运行此函数。
注意:(别忘了:您应该在 app.config.ts 文件中添加 CUSTOM_LAYOUT_PROVIDERS)
步骤 4:定义应用程序的动态布局
最后,您需要定义应用程序的动态布局。这是一个映射,其中键是布局键,值是布局组件。
您可以像这样将新布局添加到现有布局中:
export const myDynamicLayouts = new Map<string, string>([...DEFAULT_DYNAMIC_LAYOUTS, [eCustomLayout.key, eCustomLayout.component]]);
步骤 5:将动态布局传递给 Core 提供者
最后一步是使用 withOptions 方法将动态布局传递给 provideAbpCore。此方法允许您使用静态方法配置提供者。
操作方法如下:
export const appConfig: ApplicationConfig = {
providers: [
// ...
provideAbpCore(
withOptions({
dynamicLayouts: myDynamicLayouts,
environment,
registerLocaleFn: registerLocale(),
}),
),
],
};
在此代码中,myDynamicLayouts 是您之前定义的动态布局映射。我们使用 withOptions 方法将此映射传递给 provideAbpCore。
现在您已经定义了新布局,可以在路由器定义中使用它。您可以通过添加使用新布局的新路由来实现。
操作方法如下:
// route.provider.ts
import { eCustomLayout } from './custom-layout/custom-layout.provider';
import { RoutesService, eLayoutType } from '@abp/ng.core';
import { provideAppInitializer } from '@angular/core';
export const APP_ROUTE_PROVIDER = [
provideAppInitializer(() => {
configureRoutes();
}),
];
function configureRoutes() {
const routes = inject(RoutesService);
routes.add([
{
path: '/',
name: '::Menu:Home',
iconClass: 'fas fa-home',
order: 1,
layout: eLayoutType.application,
},
{
path: '/dashboard',
name: '::Menu:Dashboard',
iconClass: 'fas fa-chart-line',
order: 2,
layout: eCustomLayout.key as eLayoutType,
requiredPolicy: 'MyProjectName.Dashboard.Host || MyProjectName.Dashboard.Tenant',
},
]);
}
如何替换 LogoComponent
注意
- 如果您的目标仅仅是更改徽标图像或应用程序名称,则无需替换组件。建议通过
@abp/ng.theme.shared提供徽标,以便所有主题/组件都能一致地使用它:
// app.config.ts
import { provideLogo, withEnvironmentOptions } from '@abp/ng.theme.shared';
import { environment } from './environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
provideLogo(withEnvironmentOptions(environment)),
],
};
如果您仍然希望完全替换徽标组件的 UI,请按照以下步骤操作:
在 angular 文件夹中运行以下命令,创建一个名为 LogoComponent 的新组件。
yarn ng generate component logo --inlineTemplate --inlineStyle
打开 src/app/logo 文件夹中生成的 logo.component.ts,并将其内容替换为以下内容:
import { Component } from "@angular/core";
@Component({
selector: "app-logo",
template: `
<a class="navbar-brand" routerLink="/">
<!-- 更改 img src -->
<img
src="https://via.placeholder.com/100x50/343a40/FF0000?text=MyLogo"
alt="logo"
width="100%"
height="auto"
/>
</a>
`,
})
export class LogoComponent {}
打开 src/app 文件夹中的 app.component.ts,并按如下所示进行修改:
import { ..., ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { LogoComponent } from './logo/logo.component'; // 导入 LogoComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入 eThemeBasicComponents
//...
@Component(/* 组件元数据 */)
export class AppComponent implements OnInit {
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: LogoComponent,
key: eThemeBasicComponents.Logo,
});
}
}
最终的 UI 如下所示:
如何替换 RoutesComponent
在 angular 文件夹中运行以下命令,创建一个名为 RoutesComponent 的新组件。
yarn ng generate component routes
打开 src/app/routes 文件夹中生成的 routes.component.ts,并将其内容替换为以下内容:
import { Component, HostBinding } from "@angular/core";
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { LocalizationPipe, PermissionDirective } from "@abp/ng.core";
import { EllipsisDirective } from '@abp/ng.theme.shared';
@Component({
selector: "app-routes",
templateUrl: "routes.component.html",
imports: [
CommonModule,
RouterModule,
NgbDropdownModule,
PermissionDirective,
EllipsisDirective,
LocalizationPipe,
]
})
export class RoutesComponent {
@HostBinding("class.mx-auto")
marginAuto = true;
get smallScreen() {
return window.innerWidth < 992;
}
}
打开 src/app/routes 文件夹中生成的 routes.component.html,并将其内容替换为以下内容:
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" routerLink="/"
><i class="fas fa-home"></i> {{ '::Menu:Home' | abpLocalization
}}</a
>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/my-page"
><i class="fas fa-newspaper mr-1"></i>My Page</a
>
</li>
<li
#navbarRootDropdown
[abpVisibility]="routeContainer"
class="nav-item dropdown"
display="static"
(click)="
navbarRootDropdown.expand
? (navbarRootDropdown.expand = false)
: (navbarRootDropdown.expand = true)
"
>
<a
class="nav-link dropdown-toggle"
data-toggle="dropdown"
href="javascript:void(0)"
>
<i class="fas fa-wrench"></i>
{{ 'AbpUiNavigation::Menu:Administration' | abpLocalization }}
</a>
<div
#routeContainer
class="dropdown-menu border-0 shadow-sm"
(click)="$event.preventDefault(); $event.stopPropagation()"
[class.d-block]="smallScreen && navbarRootDropdown.expand"
>
<div
class="dropdown-submenu"
ngbDropdown
#dropdownSubmenu="ngbDropdown"
placement="right-top"
[autoClose]="true"
*abpPermission="'AbpIdentity.Roles || AbpIdentity.Users'"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<a
abpEllipsis="210px"
[abpEllipsisEnabled]="!smallScreen"
role="button"
class="btn d-block text-start dropdown-toggle"
>
<i class="fa fa-id-card-o"></i>
{{ 'AbpIdentity::Menu:IdentityManagement' | abpLocalization }}
</a>
</div>
<div
#childrenContainer
class="dropdown-menu border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
>
<div class="dropdown-submenu" *abpPermission="'AbpIdentity.Roles'">
<a class="dropdown-item" routerLink="/identity/roles">
{{ 'AbpIdentity::Roles' | abpLocalization }}</a
>
</div>
<div class="dropdown-submenu" *abpPermission="'AbpIdentity.Users'">
<a class="dropdown-item" routerLink="/identity/users">
{{ 'AbpIdentity::Users' | abpLocalization }}</a
>
</div>
</div>
</div>
<div
class="dropdown-submenu"
ngbDropdown
#dropdownSubmenu="ngbDropdown"
placement="right-top"
[autoClose]="true"
*abpPermission="'AbpTenantManagement.Tenants'"
>
<div ngbDropdownToggle [class.dropdown-toggle]="false">
<a
abpEllipsis="210px"
[abpEllipsisEnabled]="!smallScreen"
role="button"
class="btn d-block text-start dropdown-toggle"
>
<i class="fa fa-users"></i>
{{ 'AbpTenantManagement::Menu:TenantManagement' | abpLocalization
}}
</a>
</div>
<div
#childrenContainer
class="dropdown-menu border-0 shadow-sm"
[class.d-block]="smallScreen && dropdownSubmenu.isOpen()"
>
<div
class="dropdown-submenu"
*abpPermission="'AbpTenantManagement.Tenants'"
>
<a class="dropdown-item" routerLink="/tenant-management/tenants">
{{ 'AbpTenantManagement::Tenants' | abpLocalization }}</a
>
</div>
</div>
</div>
</div>
</li>
</ul>
打开 src/app 文件夹中的 app.component.ts,并按如下所示进行修改:
import { ..., ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { RoutesComponent } from './routes/routes.component'; // 导入 RoutesComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入 eThemeBasicComponents
//...
@Component(/* 组件元数据 */)
export class AppComponent implements OnInit {
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: RoutesComponent,
key: eThemeBasicComponents.Routes,
});
}
}
最终的 UI 如下所示:
如何替换 NavItemsComponent
在 angular 文件夹中运行以下命令,创建一个名为 NavItemsComponent 的新组件。
yarn ng generate component nav-items
打开 src/app/nav-items 文件夹中生成的 nav-items.component.ts,并将其内容替换为以下内容:
import {
AuthService,
ConfigStateService,
CurrentUserDto,
LanguageInfo,
NAVIGATE_TO_MANAGE_PROFILE,
SessionStateService,
LocalizationPipe
} from '@abp/ng.core';
import { Component, inject, Inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import snq from 'snq';
@Component({
selector: 'app-nav-items',
templateUrl: 'nav-items.component.html',
imports: [
CommonModule,
FormsModule,
NgbDropdownModule,
LocalizationPipe
]
})
export class NavItemsComponent {
private configState = inject(ConfigStateService);
private authService = inject(AuthService);
private sessionState = inject(SessionStateService);
@Inject(NAVIGATE_TO_MANAGE_PROFILE) public navigateToManageProfile: any;
currentUser$: Observable<CurrentUserDto> = this.configState.getOne$('currentUser');
selectedTenant$ = this.sessionState.getTenant$();
languages$: Observable<LanguageInfo[]> = this.configState.getDeep$('localization.languages');
get smallScreen(): boolean {
return window.innerWidth < 992;
}
get defaultLanguage$(): Observable<string> {
return this.languages$.pipe(
map(
languages =>
snq(
() => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName
),
''
)
);
}
get dropdownLanguages$(): Observable<LanguageInfo[]> {
return this.languages$.pipe(
map(
languages =>
snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)),
[]
)
);
}
get selectedLangCulture(): string {
return this.sessionState.getLanguage();
}
onChangeLang(cultureName: string) {
this.sessionState.setLanguage(cultureName);
}
navigateToLogin() {
this.authService.navigateToLogin();
}
logout() {
this.authService.logout().subscribe();
}
}
打开 src/app/nav-items 文件夹中生成的 nav-items.component.html,并将其内容替换为以下内容:
<ul class="navbar-nav">
<input
type="search"
placeholder="Search"
class="bg-transparent border-0 text-white"
/>
<li class="nav-item d-flex align-items-center">
<div
*ngIf="(dropdownLanguages$ | async)?.length > 0"
class="dropdown"
ngbDropdown
#languageDropdown="ngbDropdown"
display="static"
>
<a
ngbDropdownToggle
class="nav-link"
href="javascript:void(0)"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
{{ defaultLanguage$ | async }}
</a>
<div
class="dropdown-menu dropdown-menu-right border-0 shadow-sm"
aria-labelledby="dropdownMenuLink"
[class.d-block]="smallScreen && languageDropdown.isOpen()"
>
<a
*ngFor="let lang of dropdownLanguages$ | async"
href="javascript:void(0)"
class="dropdown-item"
(click)="onChangeLang(lang.cultureName)"
>{{ lang?.displayName }}</a
>
</div>
</div>
</li>
<li class="nav-item d-flex align-items-center">
<ng-template #loginBtn>
<a role="button" class="nav-link pointer" (click)="navigateToLogin()"
>{{ 'AbpAccount::Login' | abpLocalization }}</a
>
</ng-template>
<div
*ngIf="(currentUser$ | async)?.isAuthenticated; else loginBtn"
ngbDropdown
class="dropdown"
#currentUserDropdown="ngbDropdown"
display="static"
>
<a
ngbDropdownToggle
class="nav-link"
href="javascript:void(0)"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<small *ngIf="(selectedTenant$ | async)?.name as tenantName"
><i>{{ tenantName }}</i>\</small
>
<strong>{{ (currentUser$ | async)?.userName }}</strong>
</a>
<div
class="dropdown-menu dropdown-menu-right border-0 shadow-sm"
aria-labelledby="dropdownMenuLink"
[class.d-block]="smallScreen && currentUserDropdown.isOpen()"
>
<a class="dropdown-item pointer" (click)="navigateToManageProfile()"
><i class="fa fa-cog mr-1"></i>{{ 'AbpAccount::MyAccount' |
abpLocalization }}</a
>
<a class="dropdown-item" href="javascript:void(0)" (click)="logout()"
><i class="fa fa-power-off mr-1"></i>{{ 'AbpUi::Logout' |
abpLocalization }}</a
>
</div>
</div>
</li>
</ul>
打开 src/app 文件夹中的 app.component.ts,并按如下所示进行修改:
import { ..., ReplaceableComponentsService } from '@abp/ng.core'; // 导入 ReplaceableComponentsService
import { NavItemsComponent } from './nav-items/nav-items.component'; // 导入 NavItemsComponent
import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // 导入 eThemeBasicComponents
//...
@Component(/* 组件元数据 */)
export class AppComponent implements OnInit {
private replaceableComponents = inject(ReplaceableComponentsService);
ngOnInit() {
//...
this.replaceableComponents.add({
component: NavItemsComponent,
key: eThemeBasicComponents.NavItems,
});
}
}
最终的 UI 如下所示:
抠丁客










