...
Description
In this example, we create a custom state that shows a chart with an overview of all objects in the system. The resulting donut doughnut chart is clickable to drill down into the specific object types.
...
Use a CLI command to generate a state component with a custom name and sidebar link. The link appears in the navigation menu on the left. (sidebar-navigation = default)
Code Block language bash linenumbers true eo g state custom
Use a CLI command to install the maps package (takes some time).
Code Block language bash linenumbers true npm install -P ng2-charts
Import ChartsModule module to CustomStatesModule.
Code Block language js title custom-states.module.ts linenumbers true import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {Route, RouterModule} from '@angular/router'; import {EoFrameworkModule} from '@eo-sdk/client'; import {EoLinkPlugin} from '@eo-sdk/client'; import {AuthGuard} from '@eo-sdk/client'; import {CustomComponent} from './custom/custom.component'; import {ChartsModule} from 'ng2-charts'; export const routes: Route[] = [ {path: CustomComponent.path, component: CustomComponent, canActivate: [AuthGuard]}, ]; export const links: EoLinkPlugin[] = [ CustomComponent ]; @NgModule({ imports: [ CommonModule, EoFrameworkModule, RouterModule.forChild(routes), ChartsModule ], declarations: [CustomComponent] }) export class CustomStatesModule { }
Update custom.component.ts to load current chart data.
Code Block language js title custom.component.ts linenumbers true import {Component, ViewChild} from '@angular/core'; import {Chart} from 'chart.js'; import {TranslateService, SearchService} from '@eo-sdk/core'; import {BaseChartDirective} from 'ng2-charts'; import {AppSearchService, UnsubscribeOnDestroy} from '@eo-sdk/client'; import {takeUntil} from 'rxjs/operators'; @Component({ selector: 'eo-custom', templateUrl: './custom.component.html', styleUrls: ['./custom.component.scss'] }) export class CustomComponent extends UnsubscribeOnDestroy { static id = 'eo.custom.state.custom'; static path = 'custom/custom'; static matchType = new RegExp('sidebar-navigation'); groupCount: number; totalCount: number; chart; activeType = 0; types = []; @ViewChild(BaseChartDirective) chartEl: BaseChartDirective; constructor(private translate: TranslateService, private searchService: SearchService, private appSearchService: AppSearchService) { super(); this.fetchData(); } chartClicked(el?) { if (!el || (this.activeType === this.groupCount && el.active[0])) { this.activeType = el ? el.active[0]._index : this.groupCount; this.chart = this.dataToChart(this.types[this.activeType]); setTimeout(() => { this.chartEl.chart.config.data.labels = this.chart.labels; this.chartEl.chart.update(); }, 0); } } dataToChart(groups: any[]): Chart { return { data: [{ data: groups.map(g => g.count), label: '' }], labels: groups.map(g => g.label) }; } private fetchData() { this.appSearchService.setTerm(''); this.appSearchService .queryState$ .pipe( takeUntil(this.componentDestroyed$) ) .subscribe(res => { this.totalCount = res.count; this.groupCount = this.appSearchService.objectTypeGroups.length; this.types = []; const main = this.appSearchService.objectTypeGroups.map((g) => { const type = g.types.map(t => ({label: t.label, count: res.aggregations.type.get(t.qname) || 0})); this.types.push(type.sort((a, b) => b.count - a.count)); return { label: g.label === '0' ? this.translate.instant('eo.quicksearch.result.group.global') : g.label, count: type.map(t => t.count).reduce((a, b) => a + b, 0) }; }); this.types.push(main.sort((a, b) => b.count - a.count)); this.chartClicked(); } ); } }
Update custom-state.component.html to include a chart component.
Code Block language xml title custom-state.component.html linenumbers true <button class="summary" (click)="chartClicked()"> <span class="label" *ngIf="activeType != groupCount">{{types[groupCount][activeType].label}}: </span> <span class="count" *ngIf="activeType != groupCount"> {{types[groupCount][activeType].count}} of </span> <span class="total">{{totalCount}}</span> </button> <div class="chart-container"> <canvas baseChart *ngIf="chart" height="100%" [datasets]="chart.data" [labels]="chart.labels" [chartType]="'doughnut'" (chartClick)="chartClicked($event)"></canvas> </div>
To finish this Statestate, add some styling
Code Block language css title custom-state.component.scss linenumbers true :host { flex: 1; display: flex; flex-flow: column; .summary { align-items: baseline; position: absolute; right: 30px; top: 30px; flex: 1 1 auto; display: flex; background: papayawhip; border-radius: 4px; padding: 16px; border: 1px solid rgba(255, 255, 255, 0.2); .label { font-size: 1.5em; } .count { font-size: 2.5em; } .total { font-size: 3.5em; } } .chart-container { flex: 0 0 auto; } }
Use a CLI command to generate labels/translations.
Code Block language bash linenumbers true eo g label eo.custom.state.custom --en "Custom state" --de "Custom state"
Example 2:
...
Full screen State + Result List + Content Preview
Description
This example provides two new states. The first one shows the search result list in full screen, out of outside the usual client context. The second one shows the content preview of an object, also in full screen.
...
Use a CLI command to generate a simple preview state component. The link appears in the navigation menu on the left. (sidebar-navigation = default)
Code Block language bash title Terminal eo g state simple-preview
Update the simple-preview component implementation. Please update queryParams based on your scheme!
Code Block language xml title simple-preview.component.html <eo-media *ngIf="item" [dmsObject]="item" [useVersion]="item.id === item.content?.id" #viewer> </eo-media>
Code Block language css title simple-preview.component.scss /* fullscreen setup */ :host { position: fixed !important; top: 0; right: 0; bottom: 0; left: 0; z-index: 10 !important; }
Code Block language js title simple-preview.component.ts import {Component, OnInit} from '@angular/core'; import {DmsObject, DmsParams, DmsService} from '@eo-sdk/core'; import {ActivatedRoute} from '@angular/router'; import {UnsubscribeOnDestroy} from '@eo-sdk/client'; import {takeUntil, filter} from 'rxjs/operators'; @Component({ selector: 'eo-simple-preview', templateUrl: './simple-preview.component.html', styleUrls: ['./simple-preview.component.scss'] }) export class SimplePreviewComponent extends UnsubscribeOnDestroy implements OnInit { static id = 'eo.custom.state.simple-preview'; static path = 'custom/simple-preview'; static matchType = new RegExp('sidebar-navigation'); /* set specific existing ID of DMS object to provide correct link */ static queryParams = {id: '0000'}; item: DmsObject; constructor(private dmsService: DmsService, private route: ActivatedRoute) { super(); } loadDmsObject(params: DmsParams) { this.dmsService .getDmsObjectByParams(params) .subscribe(val => this.item = val) } ngOnInit() { this.route .queryParams .pipe(takeUntil(this.componentDestroyed$), filter(params => params.id)) .subscribe(params => this.loadDmsObject(params as DmsParams)); } }
Use a CLI command to generate a simple list state component. The link appears in the navigation menu on the left. (sidebar-navigation = default)
Code Block language bash title Terminal eo g state simple-list
Update the simple-list component implementation (a double-click redirects to the simple preview state). Please update queryParams based on your scheme!
Code Block language xml title simple-list.component.html <eo-result-list [query]="query" [hasIcon]="true" (onDoubleClick)="onDoubleClick($event)"></eo-result-list>
Code Block language css title simple-list.component.scss /* fullscreen setup */ :host { position: fixed !important; top: 0; right: 0; bottom: 0; left: 0; z-index: 10 !important; }
Code Block language js title simple-list.component.ts import {Component, OnInit} from '@angular/core'; import {SearchService, SearchQuery, SearchResult} from '@eo-sdk/core'; import {ActivatedRoute, Router, NavigationExtras} from '@angular/router'; import {takeUntil, filter} from 'rxjs/operators'; import {UnsubscribeOnDestroy} from '@eo-sdk/client'; import {SimplePreviewComponent} from '../simple-preview/simple-preview.component'; @Component({ selector: 'eo-simple-list', templateUrl: './simple-list.component.html', styleUrls: ['./simple-list.component.scss'] }) export class SimpleListComponent extends UnsubscribeOnDestroy implements OnInit { static id = 'eo.custom.state.simple-list'; static path = 'custom/simple-list'; static matchType = new RegExp('sidebar-navigation'); /* set specific existing query to provide correct link */ /* multiple ways how to set query based on input format */ // static queryParams = {query : '%7B%22types%22%3A%5B%22eoxemail%22%5D%7D'}; // static queryParams = {query : decodeURIComponent('%257B%2522types%2522%253A%255B%2522eoxemail%2522%255D%257D')}; static queryParams = {query: encodeURIComponent(JSON.stringify({'types': ['eoxemail']}))}; query: SearchQuery; searchResult: SearchResult = new SearchResult(); constructor(private route: ActivatedRoute, private router: Router, private searchService: SearchService) { super(); } getParams() { this.route .queryParams .pipe(takeUntil(this.componentDestroyed$), filter(params => params.query)) .subscribe((params: any) => { this.query = JSON.parse(decodeURIComponent(params.query)); const defaultQuery = this.searchService.buildQuery(this.query); this.searchService .getChunkedResult(defaultQuery, 0, 10000) .subscribe(result => { this.searchResult = result; }); }); } onDoubleClick(event) { const {id, version, typeName} = event.data; const queryParams: NavigationExtras = {queryParams: {id, version, 'type': typeName}}; const url = this.router.createUrlTree([SimplePreviewComponent.path], queryParams).toString(); window.open(url); } ngOnInit() { this.getParams(); } }
We need to generate a guard to deactivate the original behavior of the result list component
Code Block language bash title Terminal ng generate guard eo-custom-client/custom-states/simple-list/can-deactivate-list --spec false
Update can-deactivate-list.guard.ts
Code Block language js title can-deactivate-list.guard.ts linenumbers true import {Injectable} from '@angular/core'; import {CanDeactivate} from '@angular/router'; import {SimpleListComponent} from './simple-list.component'; @Injectable({ providedIn: 'root' }) export class CanDeactivateListGuard implements CanDeactivate<SimpleListComponent> { canDeactivate() { return false; } }
Add CanDeactivateListGuard guard to the custom-states module.
Code Block language js title custom-states.module.ts linenumbers true import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {Route, RouterModule} from '@angular/router'; import {EoFrameworkModule} from '@eo-sdk/client'; import {EoLinkPlugin} from '@eo-sdk/client'; import {AuthGuard} from '@eo-sdk/client'; import {SimpleListComponent} from './simple-list/simple-list.component'; import {CanDeactivateListGuard} from './simple-list/can-deactivate-list.guard'; import {SimplePreviewComponent} from './simple-preview/simple-preview.component'; export const routes: Route[] = [ {path: SimplePreviewComponent.path, component: SimplePreviewComponent, canActivate: [AuthGuard]}, {path: SimpleListComponent.path, component: SimpleListComponent, canActivate: [AuthGuard], canDeactivate: [CanDeactivateListGuard]}, ]; export const links: EoLinkPlugin[] = [ SimplePreviewComponent, SimpleListComponent, ]; @NgModule({ imports: [ CommonModule, EoFrameworkModule, RouterModule.forChild(routes) ], declarations: [SimpleListComponent, SimplePreviewComponent] }) export class CustomStatesModule { }
Use a CLI command to generate labels/translations.
Code Block language bash linenumbers true eo g label eo.custom.state.simple-list --en "Simple List" --de "Einfache Liste"
Code Block language bash linenumbers true eo g label eo.custom.state.simple-preview --en "Simple Preview" --de "Einfache Vorschau"
Example 3: Custom
...
Gallery State and Action
Description
In this example, we add a new custom action that opens an image gallery.
...
Use a CLI command to generate a state component.
Code Block language bash linenumbers true eo g state gallery
Use a CLI command to install the gallery package + and hammerjs as dependency (takes some time).
Code Block language bash linenumbers true npm install -P @ngx-gallery/core npm install -P hammerjs
Import GalleryModule module & and hammerjs to CustomStatesModule.
Code Block language js title custom-states.module.ts linenumbers true import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {Route, RouterModule} from '@angular/router'; import {EoFrameworkModule} from '@eo-sdk/client'; import {EoLinkPlugin} from '@eo-sdk/client'; import {AuthGuard} from '@eo-sdk/client'; import {GalleryComponent} from './gallery/gallery.component'; import {GalleryModule} from '@ngx-gallery/core'; import 'hammerjs'; export const routes: Route[] = [ {path: GalleryComponent.path, component: GalleryComponent, canActivate: [AuthGuard]}, ]; export const links: EoLinkPlugin[] = [ GalleryComponent, ]; @NgModule({ imports: [ CommonModule, EoFrameworkModule, GalleryModule.withConfig({ gestures:false }), RouterModule.forChild(routes) ], declarations: [GalleryComponent] }) export class CustomStatesModule { }
Update gallery.component.ts to load files/images.
Code Block language js title gallery.component.ts linenumbers true import {Component, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import {DmsService} from '@eo-sdk/core'; import {GalleryItem, ImageItem} from '@ngx-gallery/core'; @Component({ selector: 'eo-gallery', templateUrl: './gallery.component.html', styleUrls: ['./gallery.component.scss'] }) export class GalleryComponent implements OnInit { static id = 'eo.custom.state.gallery'; static path = 'custom/gallery'; // to hide link from sidebar-navigation just change matchType to non existing state static matchType = new RegExp('sidebar-navigation.no-link'); images: GalleryItem[] = []; constructor(private route: ActivatedRoute, private dmsService: DmsService) { } ngOnInit() { const files = (this.route.snapshot.queryParams['files'] || '').split(','); this.images = files.map(id => new ImageItem({ src: this.dmsService.getPreview(id), thumb: this.dmsService.getSlide(id) })); } }
Update gallery.component.html to include a gallery component.
Code Block language xml title gallery.component.html linenumbers true <gallery [items]="images" [gestures]="false" *ngIf="images.length"></gallery>
To finish this Statestate, add some styling.
Code Block language css title gallery.component.scss linenumbers true :host { display: block; height: 100%; gallery { height: 100%; } }
Use a CLI command to generate an action component.
Code Block language bash linenumbers true eo g action open-in-gallery
Update open-in-gallery.component.ts to enable a redirect to the gallery state.
Code Block language js title open-in-gallery.component.ts linenumbers true import {Component} from '@angular/core'; import {of as observableOf} from 'rxjs'; import {Router} from '@angular/router'; import {DmsObjectTarget, SelectionRange, SimpleAction} from '@eo-sdk/client'; import {DmsObject, TranslateService} from '@eo-sdk/core'; @Component({ selector: 'eo-open-in-gallery', template: ``, styles: [] }) export class OpenInGalleryComponent extends DmsObjectTarget implements SimpleAction { label: string; description: string; priority = 0; iconSrc = 'assets/_default/svg/ic_finalized.svg'; group = 'common'; range = SelectionRange.MULTI_SELECT; constructor(private translate: TranslateService, private router: Router) { super(); this.label = this.translate.instant('eo.custom.action.gallery'); this.description = this.translate.instant('eo.custom.action.gallery'); } isExecutable(item: DmsObject) { return observableOf(item.content && item.content.contents && item.content.contents[0].mimetype.match(/^image/)); } run(selection: DmsObject[]) { const queryParams = selection.map(i => i.id).join(','); this.router.navigate(['custom/gallery'], {queryParams: {files: queryParams}}); } }
Use a CLI command to generate labels/translations.
Code Block language bash linenumbers true eo g label eo.custom.action.gallery --en "Open in gallery" --de "In Galerie-Ansicht öffnen"