Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

Example 1: Custom Chart

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 chart is clickable to drill down into the specific object types.

Implementation

  1. 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
  2. Use a CLI command to install maps package (takes some time).

    npm install -P ng2-charts
  3. Import ChartsModule module to CustomStatesModule.

    custom-states.module.ts
    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 {
    }
  4. Update custom.component.ts to load current chart data.

    custom.component.ts
    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();
            }
          );
      }
    }
    
  5. 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}}: &nbsp;</span>
      <span class="count" *ngIf="activeType != groupCount"> {{types[groupCount][activeType].count}} of &nbsp;</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>
  6. 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;
      }
    }
  7. 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

Description

This example provides two new states. The first one shows the search result list in full screen, out of the usual client context. The second one shows the content preview of an object, also in full screen.


Implementation

  1. Use a CLI command to generate a simple preview state component. The link appears in the navigation menu on the left. (sidebar-navigation = default)

    Terminal
    eo g state simple-preview
  2. 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.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));
      }
    
    }
    
  3. Use a CLI command to generate a simple list state component. The link appears in the navigation menu on the left. (sidebar-navigation = default)

    Terminal
    eo g state simple-list
  4. 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.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();
      }
    
    }
    
  5. We need to generate guard to deactivate original behavior of the result list component

    Terminal
    ng generate guard eo-custom-client/custom-states/simple-list/can-deactivate-list --spec false
  6. Update can-deactivate-list.guard.ts

    can-deactivate-list.guard.ts
    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;
      }
    }
    
    
  7. Add CanDeactivateListGuard guard to custom-states module.

    custom-states.module.ts
    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 {
    }
    
    
  8. 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

Description

In this example we add a new custom action that opens an image gallery.

Implementation

  1. Use a CLI command to generate a state component.

    eo g state gallery
  2. Use a CLI command to install gallery package + hammerjs as dependency (takes some time).

    npm install -P @ngx-gallery/core
    npm install -P hammerjs
  3. Import GalleryModule module & hammerjs to CustomStatesModule.

    custom-states.module.ts
    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 {
    }
    
    
  4. Update gallery.component.ts to load files/images.

    gallery.component.ts
    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)
          }));
      }
    
    }
    
    
  5. Update gallery.component.html to include a gallery component.

    gallery.component.html
    <gallery [items]="images" [gestures]="false" *ngIf="images.length"></gallery>
  6. To finish this State, add some styling.

    gallery.component.scss
    :host {
      display: block;
      height: 100%;
      gallery {
        height: 100%;
      }
    }
  7. Use a CLI command to generate an action component.

    eo g action open-in-gallery
  8. Update open-in-gallery.component.ts to enable redirect to gallery state.

    open-in-gallery.component.ts
    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}});
      }
    
    }
    
    
  9. Use a CLI command to generate labels/translations.

    eo g label eo.custom.action.gallery --en "Open in gallery" --de "In Galerie-Ansicht öffnen"
  • No labels