# Component Replacement You can replace some ABP components with your custom components. The reason that you **can replace** but **cannot customize** default ABP components is disabling or changing a part of that component can cause problems. So we named those components as _Replaceable Components_. ## How to Replace a Component Create a new component that you want to use instead of an ABP component. Add that component to `declarations` and `entryComponents` in the `AppModule`. Then, open the `app.component.ts` and dispatch the `AddReplaceableComponent` action to replace your component with an ABP component as shown below: ```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) ## How to Replace a Layout Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced the same way. > A layout component template should contain `` element. The example below describes how to replace the `ApplicationLayoutComponent`: Run the following command to generate a layout in `angular` folder: ```bash yarn ng generate component my-application-layout ``` Add the following code in your layout template (`my-layout.component.html`) where you want the page to be loaded. ```html ``` Open `app.component.ts` in `src/app` folder and modify it as shown below: ```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, }), ); } } ``` > If you like to replace a layout component at runtime (e.g: changing the layout by pressing a button), pass the second parameter of the AddReplaceableComponent action as true. DynamicLayoutComponent loads content using a router-outlet. When the second parameter of AddReplaceableComponent is true, the route will be refreshed, so use it with caution. Your component state will be gone and any initiation logic (including HTTP requests) will be repeated. ### Layout Components ![Layout Components](./images/layout-components.png) #### How to Replace LogoComponent ![LogoComponent](./images/logo-component.png) Run the following command in `angular` folder to create a new component called `LogoComponent`. ```bash yarn ng generate component logo --inlineTemplate --inlineStyle --entryComponent # You don't need the --entryComponent option in Angular 9 ``` Open the generated `logo.component.ts` in `src/app/logo` folder and replace its content with the following: ```js import { Component } from '@angular/core'; @Component({ selector: 'app-logo', template: ` logo `, }) export class LogoComponent {} ``` Open `app.component.ts` in `src/app` folder and modify it as shown below: ```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, }), ); } } ``` The final UI looks like below: ![New logo](./images/replaced-logo-component.png) #### How to Replace RoutesComponent ![RoutesComponent](./images/routes-component.png) Run the following command in `angular` folder to create a new component called `RoutesComponent`. ```bash yarn ng generate component routes --entryComponent # You don't need the --entryComponent option in Angular 9 ``` Open the generated `routes.component.ts` in `src/app/routes` folder and replace its content with the following: ```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; }); } } ``` Open the generated `routes.component.html` in `src/app/routes` folder and replace its content with the following: ```html ``` Open `app.component.ts` in `src/app` folder and modify it as shown below: ```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, }), ); } } ``` The final UI looks like below: ![New routes](./images/replaced-routes-component.png) #### How to Replace NavItemsComponent ![NavItemsComponent](./images/nav-items-component.png) Run the following command in `angular` folder to create a new component called `NavItemsComponent`. ```bash yarn ng generate component nav-items --entryComponent # You don't need the --entryComponent option in Angular 9 ``` Open the generated `nav-items.component.ts` in `src/app/nav-items` folder and replace the content with the following: ```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 }, }), ); }); } } ``` Open the generated `nav-items.component.html` in `src/app/nav-items` folder and replace the content with the following: ```html ``` Open `app.component.ts` in `src/app` folder and modify it as shown below: ```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, }), ); } } ``` The final UI looks like below: ![New nav-items](./images/replaced-nav-items-component.png) ## See Also - [How to Replace PermissionManagementComponent](./Permission-Management-Component-Replacement.md) ## What's Next? - [Custom Setting Page](./Custom-Setting-Page.md)