## 替换组件 你可以将一些ABP的组件替换为你自己的自定义组件. 你可以**替换**但**不能自定义**默认ABP组件的原因是禁用或更改该组件的一部分可能会导致问题. 所以我们把这些组件称为可替换组件. ### 如何替换组件 创建一个你想要使用的新组件,添加到 `AppModule` 中的 `declarations` 和`entryComponents` 中. 然后打开 `app.component.ts` 使用 `AddReplaceableComponent` 将你的组件替换ABP组件. 如下所示: ```js import { AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent action import { eIdentityComponents } from '@abp/ng.identity'; // imported eIdentityComponents enum import { Store } from '@ngxs/store'; // imported Store //... @Component(/* component metadata */) export class AppComponent { constructor( private store: Store // injected Store ) { // dispatched the AddReplaceableComponent action this.store.dispatch( new AddReplaceableComponent({ component: YourNewRoleComponent, key: eIdentityComponents.Roles, }), ); } } ``` ![Example Usage](./images/component-replacement.gif) ### 如何替换布局 每个ABP主题模块有3个布局,分别是`ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. 这些布局可以用相同的方式替换. > 一个布局组件模板应该包含 `` 元素. 下面的例子解释了如何更换 `ApplicationLayoutComponent`: 运行以下命令在 `angular` 文件夹中生成布局: ```bash yarn ng generate component my-application-layout ``` 在你的布局模板(`my-application-layout.component.html`)中添加以下代码: ```html ``` 打开 `src/app` 文件夹下的 `app.component.ts` 文件添加以下内容: ```js import { AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents enum for component keys import { Store } from '@ngxs/store'; // imported Store import { MyApplicationLayoutComponent } from './my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent @Component(/* component metadata */) export class AppComponent { constructor( private store: Store, // injected Store ) { // dispatched the AddReplaceableComponent action this.store.dispatch( new AddReplaceableComponent({ component: MyApplicationLayoutComponent, key: eThemeBasicComponents.ApplicationLayout, }), ); } } ``` ### 布局组件 ![Layout Components](./images/layout-components.png) #### 如何替换LogoComponent ![LogoComponent](./images/logo-component.png) 在 `angular` 目录下运行以下命令创建新的组件 `LogoComponent`: ```bash yarn ng generate component logo --inlineTemplate --inlineStyle --entryComponent # You don't need the --entryComponent option in Angular 9 ``` 打开 `src/app/logo` 目录下生成的 `logo.component.ts` 并使用以下内容替换它: ```js import { Component } from '@angular/core'; @Component({ selector: 'app-logo', template: ` logo `, }) export class LogoComponent {} ``` 打开 `src/app` 目录下的 `app.component.ts` 做以下修改: ```js import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent import { Store } from '@ngxs/store'; // imported Store import { LogoComponent } from './logo/logo.component'; // imported NavItemsComponent import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents //... @Component(/* component metadata */) export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { //... // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: LogoComponent, key: eThemeBasicComponents.Logo, }), ); } } ``` 最终UI如下: ![New logo](./images/replaced-logo-component.png) #### 如何替换RoutesComponent ![RoutesComponent](./images/routes-component.png) 在 `angular` 目录下运行以下命令创建新的组件 `RoutesComponent`: ```bash yarn ng generate component routes --entryComponent # You don't need the --entryComponent option in Angular 9 ``` 打开 `src/app/routes` 目录下生成的 `routes.component.ts` 并使用以下内容替换它: ```js import { ABP, ReplaceableComponents } from '@abp/ng.core'; import { Component, HostBinding, Inject, Renderer2, TrackByFunction, AfterViewInit, } from '@angular/core'; import { fromEvent } from 'rxjs'; import { debounceTime } from 'rxjs/operators'; @Component({ selector: 'app-routes', templateUrl: 'routes.component.html', }) export class RoutesComponent implements AfterViewInit { @HostBinding('class.mx-auto') marginAuto = true; smallScreen = window.innerWidth < 992; constructor(private renderer: Renderer2) {} ngAfterViewInit() { fromEvent(window, 'resize') .pipe(debounceTime(150)) .subscribe(() => { this.smallScreen = window.innerWidth < 992; }); } } ``` 打开 `src/app/routes` 目录下生成的 `routes.component.html` 并使用以下内容替换它: ```html ``` 打开 `src/app` 目录下的 `app.component.ts` 做以下修改: ```js import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent import { Store } from '@ngxs/store'; // imported Store import { RoutesComponent } from './routes/routes.component'; // imported NavItemsComponent import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents //... @Component(/* component metadata */) export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { //... // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: RoutesComponent, key: eThemeBasicComponents.Routes, }), ); } } ``` 最终UI如下: ![New routes](./images/replaced-routes-component.png) #### 如何替换NavItemsComponent ![NavItemsComponent](./images/nav-items-component.png) 在 `angular` 目录下运行以下命令创建新的组件 `NavItemsComponent`: ```bash yarn ng generate component nav-items --entryComponent # You don't need the --entryComponent option in Angular 9 ``` 打开 `src/app/nav-items` 目录下生成的 `nav-items.component.ts` 并使用以下内容替换它: ```js import { ApplicationConfiguration, AuthService, ConfigState, SessionState, SetLanguage, } from '@abp/ng.core'; import { Component, AfterViewInit } from '@angular/core'; import { Navigate, RouterState } from '@ngxs/router-plugin'; import { Select, Store } from '@ngxs/store'; import { Observable, fromEvent } from 'rxjs'; import { map, debounceTime } from 'rxjs/operators'; import snq from 'snq'; @Component({ selector: 'app-nav-items', templateUrl: 'nav-items.component.html', }) export class NavItemsComponent implements AfterViewInit { @Select(ConfigState.getOne('currentUser')) currentUser$: Observable; @Select(ConfigState.getDeep('localization.languages')) languages$: Observable; smallScreen = window.innerWidth < 992; get defaultLanguage$(): Observable { return this.languages$.pipe( map( languages => snq( () => languages.find(lang => lang.cultureName === this.selectedLangCulture).displayName, ), '', ), ); } get dropdownLanguages$(): Observable { return this.languages$.pipe( map( languages => snq(() => languages.filter(lang => lang.cultureName !== this.selectedLangCulture)), [], ), ); } get selectedLangCulture(): string { return this.store.selectSnapshot(SessionState.getLanguage); } constructor(private store: Store, private authService: AuthService) {} ngAfterViewInit() { fromEvent(window, 'resize') .pipe(debounceTime(150)) .subscribe(() => { this.smallScreen = window.innerWidth < 992; }); } onChangeLang(cultureName: string) { this.store.dispatch(new SetLanguage(cultureName)); } logout() { this.authService.logout().subscribe(() => { this.store.dispatch( new Navigate(['/'], null, { state: { redirectUrl: this.store.selectSnapshot(RouterState).state.url }, }), ); }); } } ``` 打开 `src/app/nav-items` 目录下生成的 `nav-items.component.html` 并使用以下内容替换它: ```html ``` 打开 `src/app` 目录下的 `app.component.ts` 做以下修改: ```js import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent import { Store } from '@ngxs/store'; // imported Store import { NavItemsComponent } from './nav-items/nav-items.component'; // imported NavItemsComponent import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeBasicComponents //... @Component(/* component metadata */) export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { //... // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: NavItemsComponent, key: eThemeBasicComponents.NavItems, }), ); } } ``` 最终UI如下: ![New nav-items](./images/replaced-nav-items-component.png) ## 另请参阅 - [如何替换PermissionManagementComponent](./Permission-Management-Component-Replacement.md) ## 下一步是什么? - [自定义设置页面](./Custom-Setting-Page.md)