Example 1: Custom Chart
Use a CLI command to generate a state component with custom name and sidebar link. The link appears in the navigation menu on the left. (sidebar-navigation = default)
eo g state custom
Use a CLI command to install maps package (takes some time).
npm install -P ng2-charts
Import ChartsModule module to CustomStatesModule.
custom-states.module.tsimport {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.
custom.component.tsimport {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.
custom-state.component.html<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 State, add some styling
custom-state.component.scss: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.
eo g label eo.custom.state.custom --en "Custom state" --de "Custom state"
Example 2: Fullscreen state + result list + content preview
Use a CLI command to generate a simple preview state component. The link appears in the navigation menu on the left. (sidebar-navigation = default)
Terminaleo g state simple-preview
Update simple-preview component implementation. Please update queryParams based on your scheme!
simple-preview.component.html<eo-media *ngIf="item" [dmsObject]="item" [useVersion]="item.id === item.content?.id" #viewer> </eo-media>
simple-preview.component.scss/* fullscreen setup */ :host { position: fixed !important; top: 0; right: 0; bottom: 0; left: 0; z-index: 10 !important; }
simple-preview.component.tsimport {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)
Terminaleo g state simple-list
Update simple-list component implementation (a double-click redirects to simple preview state). Please update queryParams based on your scheme!
simple-list.component.html<eo-result-list [query]="query" [hasIcon]="true" (onDoubleClick)="onDoubleClick($event)"></eo-result-list>
simple-list.component.scss/* fullscreen setup */ :host { position: fixed !important; top: 0; right: 0; bottom: 0; left: 0; z-index: 10 !important; }
simple-list.component.tsimport {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 guard to deactivate original behavior of the result list component
Terminalng generate guard eo-custom-client/custom-states/simple-list/can-deactivate-list --spec false
Update can-deactivate-list.guard.ts
can-deactivate-list.guard.tsimport {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 custom-states module.
custom-states.module.tsimport {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.
eo g label eo.custom.state.simple-list --en "Simple List" --de "Einfache Liste"
eo g label eo.custom.state.simple-preview --en "Simple Preview" --de "Einfache Vorschau"
Example 3: Custom gallery state & action
Use a CLI command to generate a state component.
eo g state gallery
Use a CLI command to install gallery package + hammerjs as dependency (takes some time).
npm install -P @ngx-gallery/core npm install -P hammerjs
Import GalleryModule module & hammerjs to CustomStatesModule.
custom-states.module.tsimport {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.
gallery.component.tsimport {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.
gallery.component.html<gallery [items]="images" [gestures]="false" *ngIf="images.length"></gallery>
To finish this State, add some styling.
gallery.component.scss:host { display: block; height: 100%; gallery { height: 100%; } }
Use a CLI command to generate an action component.
eo g action open-in-gallery
Update open-in-gallery.component.ts to enable redirect to gallery state.
open-in-gallery.component.tsimport {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.
eo g label eo.custom.action.gallery --en "Open in gallery" --de "In Galerie-Ansicht öffnen"