## 替换组件
你可以将一些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: `
`,
})
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)