diff --git a/docs/en/UI/Angular/Component-Replacement.md b/docs/en/UI/Angular/Component-Replacement.md index fb85aa476e..b11acbc6b5 100644 --- a/docs/en/UI/Angular/Component-Replacement.md +++ b/docs/en/UI/Angular/Component-Replacement.md @@ -1,10 +1,10 @@ -## Component Replacement +# 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 +## 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`. @@ -15,16 +15,20 @@ import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddRepl import { eIdentityComponents } from '@abp/ng.identity'; // imported eIdentityComponents enum import { Store } from '@ngxs/store'; // imported Store //... -export class AppComponent { + +@Component(/* component metadata */) +export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { + // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: YourNewRoleComponent, key: eIdentityComponents.Roles, }), ); + //... } } @@ -33,13 +37,13 @@ export class AppComponent { ![Example Usage](./images/component-replacement.gif) -### How to Replace a Layout +## How to Replace a Layout -Each ABP theme module has 3 layouts named `ApplicationLayoutComponent`, `AccountLayoutComponent`, `EmptyLayoutComponent`. These layouts can be replaced with the same way. +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 below example describes how to replace the `ApplicationLayoutComponent`: +The example below describes how to replace the `ApplicationLayoutComponent`: Run the following command to generate a layout in `angular` folder: @@ -55,7 +59,7 @@ Add the following code in your layout template (`my-layout.component.html`) wher ``` -Open the `app.component.ts` and add the below content: +Open `app.component.ts` in `src/app` folder and modify it as shown below: ```js import { ..., AddReplaceableComponent } from '@abp/ng.core'; // imported AddReplaceableComponent @@ -63,11 +67,13 @@ import { eThemeBasicComponents } from '@abp/ng.theme.basic'; // imported eThemeB import { MyApplicationLayoutComponent } from './shared/my-application-layout/my-application-layout.component'; // imported MyApplicationLayoutComponent import { Store } from '@ngxs/store'; // imported Store //... -export class AppComponent { + +@Component(/* component metadata */) +export class AppComponent implements OnInit { constructor(..., private store: Store) {} // injected Store ngOnInit() { - // added below content + // added dispatch this.store.dispatch( new AddReplaceableComponent({ component: MyApplicationLayoutComponent, @@ -80,6 +86,463 @@ export class AppComponent { } ``` +### 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) + ## What's Next? - [Custom Setting Page](./Custom-Setting-Page.md) diff --git a/docs/en/UI/Angular/images/layout-components.png b/docs/en/UI/Angular/images/layout-components.png new file mode 100644 index 0000000000..3193b47bd1 Binary files /dev/null and b/docs/en/UI/Angular/images/layout-components.png differ diff --git a/docs/en/UI/Angular/images/logo-component.png b/docs/en/UI/Angular/images/logo-component.png new file mode 100644 index 0000000000..5c11fbf617 Binary files /dev/null and b/docs/en/UI/Angular/images/logo-component.png differ diff --git a/docs/en/UI/Angular/images/nav-items-component.png b/docs/en/UI/Angular/images/nav-items-component.png new file mode 100644 index 0000000000..d3e69916b6 Binary files /dev/null and b/docs/en/UI/Angular/images/nav-items-component.png differ diff --git a/docs/en/UI/Angular/images/replaced-logo-component.png b/docs/en/UI/Angular/images/replaced-logo-component.png new file mode 100644 index 0000000000..4661b390a8 Binary files /dev/null and b/docs/en/UI/Angular/images/replaced-logo-component.png differ diff --git a/docs/en/UI/Angular/images/replaced-nav-items-component.png b/docs/en/UI/Angular/images/replaced-nav-items-component.png new file mode 100644 index 0000000000..e4cc0339ae Binary files /dev/null and b/docs/en/UI/Angular/images/replaced-nav-items-component.png differ diff --git a/docs/en/UI/Angular/images/replaced-routes-component.png b/docs/en/UI/Angular/images/replaced-routes-component.png new file mode 100644 index 0000000000..3a6336afe0 Binary files /dev/null and b/docs/en/UI/Angular/images/replaced-routes-component.png differ diff --git a/docs/en/UI/Angular/images/routes-component.png b/docs/en/UI/Angular/images/routes-component.png new file mode 100644 index 0000000000..b708e86d36 Binary files /dev/null and b/docs/en/UI/Angular/images/routes-component.png differ