added custom item page fields for Projects, OrgUnits and People

This commit is contained in:
Art Lowel
2018-05-28 17:22:17 +02:00
parent fdd9746f64
commit 06d2ac74be
64 changed files with 463 additions and 132 deletions

View File

@@ -45,6 +45,38 @@
}
}
},
"person": {
"page": {
"jobtitle": "Job Title",
"lastname": "Last Name",
"firstname": "First Name",
"email": "Email Address",
"orcid": "ORCID",
"birthdate": "Birth Date",
"staffid": "Staff ID",
"link": {
"full": "Show all metadata"
}
}
},
"project": {
"page": {
"status": "Status",
"id": "ID",
"expectedcompletion": "Expected Completion",
"description": "Description",
"keywords": "Keywords"
}
},
"orgunit": {
"page": {
"dateestablished": "Date established",
"city": "City",
"country": "Country",
"id": "ID",
"description": "Description"
}
},
"nav": {
"home": "Home"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

View File

@@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedModule } from './../shared/shared.module';
import { GenericItemPageFieldComponent } from './simple/field-components/specific-field/generic/generic-item-page-field.component';
import { ItemPageComponent } from './simple/item-page.component';
import { ItemPageRoutingModule } from './item-page-routing.module';
@@ -13,11 +14,16 @@ import { ItemPageDateFieldComponent } from './simple/field-components/specific-f
import { ItemPageAbstractFieldComponent } from './simple/field-components/specific-field/abstract/item-page-abstract-field.component';
import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component';
import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component';
import { ItemPageSpecificFieldComponent } from './simple/field-components/specific-field/item-page-specific-field.component';
import { ItemPageFieldComponent } from './simple/field-components/specific-field/item-page-field.component';
import { FileSectionComponent } from './simple/field-components/file-section/file-section.component';
import { CollectionsComponent } from './field-components/collections/collections.component';
import { FullItemPageComponent } from './full/full-item-page.component';
import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
import { ItemPageFieldsComponent } from './simple/relationship-types/item/item-page-fields.component';
import { OrgUnitPageFieldsComponent } from './simple/relationship-types/orgunit/orgunit-page-fields.component';
import { PersonPageFieldsComponent } from './simple/relationship-types/person/person-page-fields.component';
import { ProjectPageFieldsComponent } from './simple/relationship-types/project/project-page-fields.component';
import { RelationshipTypeSwitcherComponent } from './simple/relationship-types/switcher/relationship-type-switcher.component';
@NgModule({
imports: [
@@ -36,10 +42,22 @@ import { FullFileSectionComponent } from './full/field-components/file-section/f
ItemPageAbstractFieldComponent,
ItemPageUriFieldComponent,
ItemPageTitleFieldComponent,
ItemPageSpecificFieldComponent,
ItemPageFieldComponent,
FileSectionComponent,
CollectionsComponent,
FullFileSectionComponent
FullFileSectionComponent,
RelationshipTypeSwitcherComponent,
ItemPageFieldsComponent,
ProjectPageFieldsComponent,
OrgUnitPageFieldsComponent,
PersonPageFieldsComponent,
GenericItemPageFieldComponent
],
entryComponents: [
ItemPageFieldsComponent,
ProjectPageFieldsComponent,
OrgUnitPageFieldsComponent,
PersonPageFieldsComponent
]
})
export class ItemPageModule {

View File

@@ -1,13 +1,13 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
import { ItemPageFieldComponent } from '../item-page-field.component';
@Component({
selector: 'ds-item-page-abstract-field',
templateUrl: './../item-page-specific-field.component.html'
templateUrl: '../item-page-field.component.html'
})
export class ItemPageAbstractFieldComponent extends ItemPageSpecificFieldComponent {
export class ItemPageAbstractFieldComponent extends ItemPageFieldComponent {
@Input() item: Item;

View File

@@ -1,13 +1,13 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
import { ItemPageFieldComponent } from '../item-page-field.component';
@Component({
selector: 'ds-item-page-author-field',
templateUrl: './../item-page-specific-field.component.html'
templateUrl: '../item-page-field.component.html'
})
export class ItemPageAuthorFieldComponent extends ItemPageSpecificFieldComponent {
export class ItemPageAuthorFieldComponent extends ItemPageFieldComponent {
@Input() item: Item;

View File

@@ -1,13 +1,13 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
import { ItemPageFieldComponent } from '../item-page-field.component';
@Component({
selector: 'ds-item-page-date-field',
templateUrl: './../item-page-specific-field.component.html'
templateUrl: '../item-page-field.component.html'
})
export class ItemPageDateFieldComponent extends ItemPageSpecificFieldComponent {
export class ItemPageDateFieldComponent extends ItemPageFieldComponent {
@Input() item: Item;

View File

@@ -0,0 +1,20 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageFieldComponent } from '../item-page-field.component';
@Component({
selector: 'ds-generic-item-page-field',
templateUrl: '../item-page-field.component.html'
})
export class GenericItemPageFieldComponent extends ItemPageFieldComponent {
@Input() item: Item;
@Input() separator: string;
@Input() fields: string[];
@Input() label: string;
}

View File

@@ -1,3 +1,3 @@
<div class="item-page-specific-field">
<div class="item-page-field">
<ds-metadata-values [values]="item?.filterMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-values>
</div>

View File

@@ -9,9 +9,9 @@ import { Item } from '../../../../core/shared/item.model';
*/
@Component({
templateUrl: './item-page-specific-field.component.html'
templateUrl: './item-page-field.component.html'
})
export class ItemPageSpecificFieldComponent {
export class ItemPageFieldComponent {
@Input() item: Item;

View File

@@ -1,13 +1,13 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
import { ItemPageFieldComponent } from '../item-page-field.component';
@Component({
selector: 'ds-item-page-title-field',
templateUrl: './item-page-title-field.component.html'
})
export class ItemPageTitleFieldComponent extends ItemPageSpecificFieldComponent {
export class ItemPageTitleFieldComponent extends ItemPageFieldComponent {
@Input() item: Item;

View File

@@ -1,3 +1,3 @@
<div class="item-page-specific-field">
<div class="item-page-field">
<ds-metadata-uri-values [values]="item?.filterMetadata(fields)" [separator]="separator" [label]="label"></ds-metadata-uri-values>
</div>

View File

@@ -1,13 +1,13 @@
import { Component, Input } from '@angular/core';
import { Item } from '../../../../../core/shared/item.model';
import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component';
import { ItemPageFieldComponent } from '../item-page-field.component';
@Component({
selector: 'ds-item-page-uri-field',
templateUrl: './item-page-uri-field.component.html'
})
export class ItemPageUriFieldComponent extends ItemPageSpecificFieldComponent {
export class ItemPageUriFieldComponent extends ItemPageFieldComponent {
@Input() item: Item;

View File

@@ -1,27 +1,7 @@
<div class="container" *ngVar="(itemRDObs | async) as itemRD">
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
<div *ngIf="itemRD?.payload as item">
<ds-item-page-title-field [item]="item"></ds-item-page-title-field>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="thumbnailObs | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="item"></ds-item-page-file-section>
<ds-item-page-date-field [item]="item"></ds-item-page-date-field>
<ds-item-page-author-field [item]="item"></ds-item-page-author-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
<ds-item-page-uri-field [item]="item"></ds-item-page-uri-field>
<ds-item-page-collections [item]="item"></ds-item-page-collections>
<div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</div>
</div>
</div>
<ds-relationship-type-switcher [item]="item" [viewMode]="ElementViewMode.Full"></ds-relationship-type-switcher>
</div>
</div>
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>

View File

@@ -12,6 +12,7 @@ import { MetadataService } from '../../core/metadata/metadata.service';
import { fadeInOut } from '../../shared/animations/fade';
import { hasValue } from '../../shared/empty.util';
import * as viewMode from '../../shared/view-mode';
/**
* This component renders a simple item page.
@@ -35,6 +36,8 @@ export class ItemPageComponent implements OnInit {
thumbnailObs: Observable<Bitstream>;
ElementViewMode = viewMode.ElementViewMode;
constructor(
private route: ActivatedRoute,
private items: ItemDataService,

View File

@@ -0,0 +1,21 @@
<ds-item-page-title-field [item]="item"></ds-item-page-title-field>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="item"></ds-item-page-file-section>
<ds-item-page-date-field [item]="item"></ds-item-page-date-field>
<ds-item-page-author-field [item]="item"></ds-item-page-author-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
<ds-item-page-uri-field [item]="item"></ds-item-page-uri-field>
<ds-item-page-collections [item]="item"></ds-item-page-collections>
<div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</div>
</div>
</div>

View File

@@ -0,0 +1 @@
@import '../../../../../styles/variables.scss';

View File

@@ -0,0 +1,23 @@
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import {
DEFAULT_RELATIONSHIP_TYPE,
rendersRelationshipType
} from '../../../../shared/entities/relationship-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/relationship-type-switcher.component';
@rendersRelationshipType('Item', ElementViewMode.Full)
@rendersRelationshipType(DEFAULT_RELATIONSHIP_TYPE, ElementViewMode.Full)
@Component({
selector: 'ds-item-page-fields',
styleUrls: ['./item-page-fields.component.scss'],
templateUrl: './item-page-fields.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ItemPageFieldsComponent {
constructor(@Inject(ITEM) public item: Item) {
}
}

View File

@@ -0,0 +1,32 @@
<h2 class="item-page-title-field">
<ds-metadata-values [values]="item?.filterMetadata(['orgunit.identifier.name'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/orgunit-placeholder.jpg'"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.dateestablished']"
[label]="'orgunit.page.dateestablished'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.city']"
[label]="'orgunit.page.city'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.country']"
[label]="'orgunit.page.country'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.id']"
[label]="'orgunit.page.id'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-generic-item-page-field [item]="item"
[fields]="['orgunit.identifier.description']"
[label]="'orgunit.page.description'">
</ds-generic-item-page-field>
</div>
</div>

View File

@@ -0,0 +1 @@
@import '../../../../../styles/variables.scss';

View File

@@ -0,0 +1,18 @@
import { Component, Inject } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/relationship-type-switcher.component';
@rendersRelationshipType('OrgUnit', ElementViewMode.Full)
@Component({
selector: 'ds-orgunit-page-fields',
styleUrls: ['./orgunit-page-fields.component.scss'],
templateUrl: './orgunit-page-fields.component.html'
})
export class OrgUnitPageFieldsComponent {
constructor(@Inject(ITEM) public item: Item) {
}
}

View File

@@ -0,0 +1,40 @@
<h2 class="item-page-title-field">
<ds-metadata-values [values]="item?.filterMetadata(['dc.contributor.author'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/person-placeholder.png'"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.email']"
[label]="'person.page.email'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.orcid']"
[label]="'person.page.orcid'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.birthdate']"
[label]="'person.page.birthdate'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.staffid']"
[label]="'person.page.staffid'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.jobtitle']"
[label]="'person.page.jobtitle'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.lastname']"
[label]="'person.page.lastname'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['person.identifier.firstname']"
[label]="'person.page.firstname'">
</ds-generic-item-page-field>
</div>
</div>

View File

@@ -0,0 +1 @@
@import '../../../../../styles/variables.scss';

View File

@@ -0,0 +1,18 @@
import { Component, Inject } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/relationship-type-switcher.component';
@rendersRelationshipType('Person', ElementViewMode.Full)
@Component({
selector: 'ds-person-page-fields',
styleUrls: ['./person-page-fields.component.scss'],
templateUrl: './person-page-fields.component.html'
})
export class PersonPageFieldsComponent {
constructor(@Inject(ITEM) public item: Item) {
}
}

View File

@@ -0,0 +1,32 @@
<h2 class="item-page-title-field">
<ds-metadata-values [values]="item?.filterMetadata(['project.identifier.name'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async" [defaultImage]="'assets/images/project-placeholder.png'"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
[fields]="['project.identifier.status']"
[label]="'project.page.status'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['project.identifier.id']"
[label]="'project.page.id'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['project.identifier.expectedcompletion']"
[label]="'project.page.expectedcompletion'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-generic-item-page-field [item]="item"
[fields]="['project.identifier.description']"
[label]="'project.page.description'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
[fields]="['project.identifier.keywords']"
[label]="'project.page.keywords'">
</ds-generic-item-page-field>
</div>
</div>

View File

@@ -0,0 +1 @@
@import '../../../../../styles/variables.scss';

View File

@@ -0,0 +1,18 @@
import { Component, Inject } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator';
import { ElementViewMode } from '../../../../shared/view-mode';
import { ITEM } from '../switcher/relationship-type-switcher.component';
@rendersRelationshipType('Project', ElementViewMode.Full)
@Component({
selector: 'ds-project-page-fields',
styleUrls: ['./project-page-fields.component.scss'],
templateUrl: './project-page-fields.component.html'
})
export class ProjectPageFieldsComponent {
constructor(@Inject(ITEM) public item: Item) {
}
}

View File

@@ -0,0 +1 @@
<ng-container *ngComponentOutlet="getComponent(); injector: objectInjector;"></ng-container>

View File

@@ -0,0 +1 @@
@import '../../../../../styles/variables.scss';

View File

@@ -0,0 +1,36 @@
import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core';
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
import { Item } from '../../../../core/shared/item.model';
import { getComponentByRelationshipType } from '../../../../shared/entities/relationship-type-decorator';
import { rendersDSOType } from '../../../../shared/object-collection/shared/dso-element-decorator';
import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model';
import { ElementViewMode, SetViewMode } from '../../../../shared/view-mode';
export const ITEM: InjectionToken<string> = new InjectionToken<string>('item');
@Component({
selector: 'ds-relationship-type-switcher',
styleUrls: ['./relationship-type-switcher.component.scss'],
templateUrl: './relationship-type-switcher.component.html'
})
export class RelationshipTypeSwitcherComponent implements OnInit {
@Input() item: Item;
@Input() viewMode: ElementViewMode;
objectInjector: Injector;
constructor(private injector: Injector) {
}
ngOnInit(): void {
this.objectInjector = Injector.create({
providers: [{ provide: ITEM, useFactory: () => this.item, deps:[] }],
parent: this.injector
});
}
getComponent(): string {
const type = this.item.findMetadata('relationship.type');
return getComponentByRelationshipType(type, this.viewMode);
}
}

View File

@@ -1,14 +1,10 @@
import { isNotEmpty } from '../shared/empty.util';
import { URLCombiner } from '../core/url-combiner/url-combiner';
import 'core-js/fn/object/entries';
export enum ViewMode {
List = 'list',
Grid = 'grid'
}
import { URLCombiner } from '../core/url-combiner/url-combiner';
import { isNotEmpty } from '../shared/empty.util';
import { SetViewMode } from '../shared/view-mode';
export class SearchOptions {
view?: ViewMode = ViewMode.List;
view?: SetViewMode = SetViewMode.List;
scope?: string;
query?: string;
filters?: any;

View File

@@ -2,7 +2,8 @@ import { Component, Input } from '@angular/core';
import { RemoteData } from '../../core/data/remote-data';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
import { SearchOptions, ViewMode } from '../search-options.model';
import { SetViewMode } from '../../shared/view-mode';
import { SearchOptions} from '../search-options.model';
import { SortOptions } from '../../core/cache/models/sort-options.model';
import { SearchResult } from '../search-result.model';
import { PaginatedList } from '../../core/data/paginated-list';
@@ -24,5 +25,5 @@ export class SearchResultsComponent {
@Input() searchResults: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>;
@Input() searchConfig: SearchOptions;
@Input() sortConfig: SortOptions;
@Input() viewMode: ViewMode;
@Input() viewMode: SetViewMode;
}

View File

@@ -6,7 +6,7 @@ import { Component } from '@angular/core';
import { SearchService } from './search.service';
import { ItemDataService } from './../../core/data/item-data.service';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../../shared/view-mode';
import { RouteService } from '../../shared/route.service';
import { GLOBAL_CONFIG } from '../../../config';
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
@@ -68,7 +68,7 @@ describe('SearchService', () => {
it('should return list view mode', () => {
searchService.getViewMode().subscribe((viewMode) => {
expect(viewMode).toBe(ViewMode.List);
expect(viewMode).toBe(SetViewMode.List);
});
});
});
@@ -124,33 +124,33 @@ describe('SearchService', () => {
});
it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.List);
searchService.setViewMode(SetViewMode.List);
expect(router.navigate).toHaveBeenCalledWith(['/search'], {
queryParams: { view: ViewMode.List },
queryParams: { view: SetViewMode.List },
queryParamsHandling: 'merge'
});
});
it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.Grid);
searchService.setViewMode(SetViewMode.Grid);
expect(router.navigate).toHaveBeenCalledWith(['/search'], {
queryParams: { view: ViewMode.Grid },
queryParams: { view: SetViewMode.Grid },
queryParamsHandling: 'merge'
});
});
it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => {
let viewMode = ViewMode.Grid;
route.testParams = { view: ViewMode.List };
let viewMode = SetViewMode.Grid;
route.testParams = { view: SetViewMode.List };
searchService.getViewMode().subscribe((mode) => viewMode = mode);
expect(viewMode).toEqual(ViewMode.List);
expect(viewMode).toEqual(SetViewMode.List);
});
it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => {
let viewMode = ViewMode.List;
route.testParams = { view: ViewMode.Grid };
let viewMode = SetViewMode.List;
route.testParams = { view: SetViewMode.Grid };
searchService.getViewMode().subscribe((mode) => viewMode = mode);
expect(viewMode).toEqual(ViewMode.Grid);
expect(viewMode).toEqual(SetViewMode.Grid);
});
describe('when search is called', () => {

View File

@@ -5,7 +5,7 @@ import {
} from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { flatMap, map, tap } from 'rxjs/operators';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../../shared/view-mode';
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import {
@@ -217,17 +217,17 @@ export class SearchService implements OnDestroy {
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);
}
getViewMode(): Observable<ViewMode> {
getViewMode(): Observable<SetViewMode> {
return this.route.queryParams.map((params) => {
if (isNotEmpty(params.view) && hasValue(params.view)) {
return params.view;
} else {
return ViewMode.List;
return SetViewMode.List;
}
});
}
setViewMode(viewMode: ViewMode) {
setViewMode(viewMode: SetViewMode) {
const navigationExtras: NavigationExtras = {
queryParams: { view: viewMode },
queryParamsHandling: 'merge'

View File

@@ -1,6 +1,7 @@
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core';
import { SetViewMode } from '../../shared/view-mode';
import { SearchService } from '../search-service/search.service';
import { SearchOptions, ViewMode } from '../search-options.model';
import { SearchOptions} from '../search-options.model';
import { SortDirection } from '../../core/cache/models/sort-options.model';
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
import { PaginatedSearchOptions } from '../paginated-search-options.model';
@@ -48,7 +49,7 @@ export class SearchSettingsComponent implements OnInit {
this.page = +params.page || this.searchOptions.pagination.currentPage;
this.pageSize = +params.pageSize || this.searchOptions.pagination.pageSize;
this.direction = params.sortDirection || this.searchOptions.sort.direction;
if (params.view === ViewMode.Grid) {
if (params.view === SetViewMode.Grid) {
this.pageSizeOptions = this.pageSizeOptions;
} else {
this.pageSizeOptions = this.pageSizeOptions;

View File

@@ -0,0 +1,22 @@
import { hasNoValue } from '../empty.util';
import { ElementViewMode } from '../view-mode';
export const DEFAULT_RELATIONSHIP_TYPE = 'Default';
const map = new Map();
export function rendersRelationshipType(type: string, viewMode: ElementViewMode) {
return function decorator(component: any) {
if (hasNoValue(map.get(viewMode))) {
map.set(viewMode, new Map());
}
map.get(viewMode).set(type, component);
};
}
export function getComponentByRelationshipType(type: string, viewMode: ElementViewMode) {
let component = map.get(viewMode).get(type);
if (hasNoValue(component)) {
component = map.get(viewMode).get(DEFAULT_RELATIONSHIP_TYPE);
}
return component;
}

View File

@@ -1,5 +1,5 @@
import { ObjectCollectionComponent } from './object-collection.component';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../view-mode';
import { element } from 'protractor';
import { By } from '@angular/platform-browser';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
@@ -38,14 +38,14 @@ describe('ObjectCollectionComponent', () => {
}));
it('should only show the grid component when the viewmode is set to grid', () => {
objectCollectionComponent.currentMode = ViewMode.Grid;
objectCollectionComponent.currentMode = SetViewMode.Grid;
expect(fixture.debugElement.query(By.css('ds-object-grid'))).toBeDefined();
expect(fixture.debugElement.query(By.css('ds-object-list'))).toBeNull();
});
it('should only show the list component when the viewmode is set to list', () => {
objectCollectionComponent.currentMode = ViewMode.List;
objectCollectionComponent.currentMode = SetViewMode.List;
expect(fixture.debugElement.query(By.css('ds-object-list'))).toBeDefined();
expect(fixture.debugElement.query(By.css('ds-object-grid'))).toBeNull();

View File

@@ -14,7 +14,7 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o
import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model';
import { ListableObject } from './shared/listable-object.model';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../view-mode';
import { hasValue, isNotEmpty } from '../empty.util';
@Component({
@@ -56,8 +56,8 @@ export class ObjectCollectionComponent implements OnChanges, OnInit {
*/
@Output() sortFieldChange: EventEmitter<string> = new EventEmitter<string>();
data: any = {};
currentMode: ViewMode = ViewMode.List;
viewModeEnum = ViewMode;
currentMode: SetViewMode = SetViewMode.List;
viewModeEnum = SetViewMode;
ngOnChanges(changes: SimpleChanges) {
if (changes.objects && !changes.objects.isFirstChange()) {
@@ -87,7 +87,7 @@ export class ObjectCollectionComponent implements OnChanges, OnInit {
private router: Router) {
}
getViewMode(): ViewMode {
getViewMode(): SetViewMode {
this.route.queryParams.map((params) => {
if (isNotEmpty(params.view) && hasValue(params.view)) {
this.currentMode = params.view;

View File

@@ -1,10 +1,10 @@
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
import { renderElementsFor } from './dso-element-decorator';
import { Item } from '../../../core/shared/item.model';
describe('ElementDecorator', () => {
const gridDecorator = renderElementsFor(Item, ViewMode.Grid);
const listDecorator = renderElementsFor(Item, ViewMode.List);
const gridDecorator = renderElementsFor(Item, SetViewMode.Grid);
const listDecorator = renderElementsFor(Item, SetViewMode.List);
it('should have a decorator for both list and grid', () => {
expect(listDecorator.length).not.toBeNull();
expect(gridDecorator.length).not.toBeNull();

View File

@@ -1,9 +1,9 @@
import { GenericConstructor } from '../../../core/shared/generic-constructor';
import { ListableObject } from './listable-object.model';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
const dsoElementMap = new Map();
export function renderElementsFor(listable: GenericConstructor<ListableObject>, viewMode: ViewMode) {
export function renderElementsFor(listable: GenericConstructor<ListableObject>, viewMode: SetViewMode) {
return function decorator(objectElement: any) {
if (!objectElement) {
return;
@@ -15,6 +15,6 @@ export function renderElementsFor(listable: GenericConstructor<ListableObject>,
};
}
export function rendersDSOType(listable: GenericConstructor<ListableObject>, viewMode: ViewMode) {
export function rendersDSOType(listable: GenericConstructor<ListableObject>, viewMode: SetViewMode) {
return dsoElementMap.get(viewMode).get(listable);
}

View File

@@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core';
import { Collection } from '../../../core/shared/collection.model';
import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
@Component({
@@ -11,5 +11,5 @@ import { AbstractListableElementComponent } from '../../object-collection/shared
templateUrl: './collection-grid-element.component.html'
})
@renderElementsFor(Collection, ViewMode.Grid)
@renderElementsFor(Collection, SetViewMode.Grid)
export class CollectionGridElementComponent extends AbstractListableElementComponent<Collection> {}

View File

@@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core';
import { Community } from '../../../core/shared/community.model';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
@Component({
selector: 'ds-community-grid-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model';
templateUrl: './community-grid-element.component.html'
})
@renderElementsFor(Community, ViewMode.Grid)
@renderElementsFor(Community, SetViewMode.Grid)
export class CommunityGridElementComponent extends AbstractListableElementComponent<Community> {}

View File

@@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
@Component({
selector: 'ds-item-grid-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model';
templateUrl: './item-grid-element.component.html'
})
@renderElementsFor(Item, ViewMode.Grid)
@renderElementsFor(Item, SetViewMode.Grid)
export class ItemGridElementComponent extends AbstractListableElementComponent<Item> {}

View File

@@ -3,7 +3,7 @@ import { Component } from '@angular/core';
import { renderElementsFor} from '../../../object-collection/shared/dso-element-decorator';
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
import { Collection } from '../../../../core/shared/collection.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { SetViewMode } from '../../../view-mode';
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
@Component({
@@ -12,5 +12,5 @@ import { CollectionSearchResult } from '../../../object-collection/shared/collec
templateUrl: 'collection-search-result-grid-element.component.html'
})
@renderElementsFor(CollectionSearchResult, ViewMode.Grid)
@renderElementsFor(CollectionSearchResult, SetViewMode.Grid)
export class CollectionSearchResultGridElementComponent extends SearchResultGridElementComponent<CollectionSearchResult, Collection> {}

View File

@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
import { Community } from '../../../../core/shared/community.model';
import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { SetViewMode } from '../../../view-mode';
import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
@Component({
@@ -11,7 +11,7 @@ import { CommunitySearchResult } from '../../../object-collection/shared/communi
templateUrl: 'community-search-result-grid-element.component.html'
})
@renderElementsFor(CommunitySearchResult, ViewMode.Grid)
@renderElementsFor(CommunitySearchResult, SetViewMode.Grid)
export class CommunitySearchResultGridElementComponent extends SearchResultGridElementComponent<CommunitySearchResult, Community> {
}

View File

@@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
import { Item } from '../../../../core/shared/item.model';
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { SetViewMode } from '../../../view-mode';
import { focusShadow } from '../../../../shared/animations/focus';
@Component({
@@ -14,5 +14,5 @@ import { focusShadow } from '../../../../shared/animations/focus';
animations: [focusShadow],
})
@renderElementsFor(ItemSearchResult, ViewMode.Grid)
@renderElementsFor(ItemSearchResult, SetViewMode.Grid)
export class ItemSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {}

View File

@@ -1,5 +1,5 @@
import { Component, Injector, Input, OnInit } from '@angular/core';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
import { GenericConstructor } from '../../../core/shared/generic-constructor';
import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator';
import { ListableObject } from '../../object-collection/shared/listable-object.model';
@@ -26,6 +26,6 @@ export class WrapperGridElementComponent implements OnInit {
getGridElement(): string {
const f: GenericConstructor<ListableObject> = this.object.constructor as GenericConstructor<ListableObject>;
return rendersDSOType(f, ViewMode.Grid);
return rendersDSOType(f, SetViewMode.Grid);
}
}

View File

@@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core';
import { Collection } from '../../../core/shared/collection.model';
import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
@Component({
@@ -11,5 +11,5 @@ import { AbstractListableElementComponent } from '../../object-collection/shared
templateUrl: './collection-list-element.component.html'
})
@renderElementsFor(Collection, ViewMode.List)
@renderElementsFor(Collection, SetViewMode.List)
export class CollectionListElementComponent extends AbstractListableElementComponent<Collection> {}

View File

@@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core';
import { Community } from '../../../core/shared/community.model';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
@Component({
selector: 'ds-community-list-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model';
templateUrl: './community-list-element.component.html'
})
@renderElementsFor(Community, ViewMode.List)
@renderElementsFor(Community, SetViewMode.List)
export class CommunityListElementComponent extends AbstractListableElementComponent<Community> {}

View File

@@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
@Component({
selector: 'ds-item-list-element',
@@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model';
templateUrl: './item-list-element.component.html'
})
@renderElementsFor(Item, ViewMode.List)
@renderElementsFor(Item, SetViewMode.List)
export class ItemListElementComponent extends AbstractListableElementComponent<Item> {}

View File

@@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Collection } from '../../../../core/shared/collection.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { SetViewMode } from '../../../view-mode';
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
@Component({
@@ -13,5 +13,5 @@ import { CollectionSearchResult } from '../../../object-collection/shared/collec
templateUrl: 'collection-search-result-list-element.component.html'
})
@renderElementsFor(CollectionSearchResult, ViewMode.List)
@renderElementsFor(CollectionSearchResult, SetViewMode.List)
export class CollectionSearchResultListElementComponent extends SearchResultListElementComponent<CollectionSearchResult, Collection> {}

View File

@@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Community } from '../../../../core/shared/community.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { SetViewMode } from '../../../view-mode';
import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
@Component({
@@ -13,7 +13,7 @@ import { CommunitySearchResult } from '../../../object-collection/shared/communi
templateUrl: 'community-search-result-list-element.component.html'
})
@renderElementsFor(CommunitySearchResult, ViewMode.List)
@renderElementsFor(CommunitySearchResult, SetViewMode.List)
export class CommunitySearchResultListElementComponent extends SearchResultListElementComponent<CommunitySearchResult, Community> {
}

View File

@@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Item } from '../../../../core/shared/item.model';
import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { SetViewMode } from '../../../view-mode';
import { ListableObject } from '../../../object-collection/shared/listable-object.model';
import { focusBackground } from '../../../animations/focus';
@@ -16,6 +16,6 @@ import { focusBackground } from '../../../animations/focus';
})
@renderElementsFor(ItemSearchResult, ViewMode.List)
@renderElementsFor(ItemSearchResult, SetViewMode.List)
export class ItemSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -1,5 +1,5 @@
import { Component, Injector, Input, OnInit } from '@angular/core';
import { ViewMode } from '../../../+search-page/search-options.model';
import { SetViewMode } from '../../view-mode';
import { GenericConstructor } from '../../../core/shared/generic-constructor';
import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator'
import { ListableObject } from '../../object-collection/shared/listable-object.model';
@@ -24,6 +24,6 @@ export class WrapperListElementComponent implements OnInit {
getListElement(): string {
const f: GenericConstructor<ListableObject> = this.object.constructor as GenericConstructor<ListableObject>;
return rendersDSOType(f, ViewMode.List);
return rendersDSOType(f, SetViewMode.List);
}
}

View File

@@ -1,5 +1,5 @@
import { Observable } from 'rxjs/Observable';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../view-mode';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export class HALEndpointServiceStub {

View File

@@ -1,23 +1,23 @@
import { Observable } from 'rxjs/Observable';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../view-mode';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export class SearchServiceStub {
private _viewMode: ViewMode;
private _viewMode: SetViewMode;
private subject?: BehaviorSubject<any> = new BehaviorSubject(this.testViewMode);
viewMode = this.subject.asObservable();
constructor(private searchLink: string = '/search') {
this.setViewMode(ViewMode.List);
this.setViewMode(SetViewMode.List);
}
getViewMode(): Observable<ViewMode> {
getViewMode(): Observable<SetViewMode> {
return this.viewMode;
}
setViewMode(viewMode: ViewMode) {
setViewMode(viewMode: SetViewMode) {
this.testViewMode = viewMode;
}
@@ -25,11 +25,11 @@ export class SearchServiceStub {
return null;
}
get testViewMode(): ViewMode {
get testViewMode(): SetViewMode {
return this._viewMode;
}
set testViewMode(viewMode: ViewMode) {
set testViewMode(viewMode: SetViewMode) {
this._viewMode = viewMode;
this.subject.next(viewMode);
}

View File

@@ -8,7 +8,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
import { SearchService } from '../../+search-page/search-service/search.service';
import { ViewModeSwitchComponent } from './view-mode-switch.component';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../view-mode';
import { SearchServiceStub } from '../testing/search-service-stub';
@Component({ template: '' })
@@ -55,19 +55,19 @@ describe('ViewModeSwitchComponent', () => {
});
it('should set list button as active when on list mode', fakeAsync(() => {
searchService.setViewMode(ViewMode.List);
searchService.setViewMode(SetViewMode.List);
tick();
fixture.detectChanges();
expect(comp.currentMode).toBe(ViewMode.List);
expect(comp.currentMode).toBe(SetViewMode.List);
expect(listButton.classList).toContain('active');
expect(gridButton.classList).not.toContain('active');
}));
it('should set grid button as active when on grid mode', fakeAsync(() => {
searchService.setViewMode(ViewMode.Grid);
searchService.setViewMode(SetViewMode.Grid);
tick();
fixture.detectChanges();
expect(comp.currentMode).toBe(ViewMode.Grid);
expect(comp.currentMode).toBe(SetViewMode.Grid);
expect(listButton.classList).not.toContain('active');
expect(gridButton.classList).toContain('active');
}));

View File

@@ -1,6 +1,6 @@
import { Subscription } from 'rxjs/Subscription';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ViewMode } from '../../+search-page/search-options.model';
import { SetViewMode } from '../view-mode';
import { SearchService } from './../../+search-page/search-service/search.service';
/**
@@ -12,20 +12,20 @@ import { SearchService } from './../../+search-page/search-service/search.servic
templateUrl: './view-mode-switch.component.html'
})
export class ViewModeSwitchComponent implements OnInit, OnDestroy {
currentMode: ViewMode = ViewMode.List;
viewModeEnum = ViewMode;
currentMode: SetViewMode = SetViewMode.List;
viewModeEnum = SetViewMode;
private sub: Subscription;
constructor(private searchService: SearchService) {
}
ngOnInit(): void {
this.sub = this.searchService.getViewMode().subscribe((viewMode: ViewMode) => {
this.sub = this.searchService.getViewMode().subscribe((viewMode: SetViewMode) => {
this.currentMode = viewMode;
});
}
switchViewTo(viewMode: ViewMode) {
switchViewTo(viewMode: SetViewMode) {
this.searchService.setViewMode(viewMode);
}

View File

@@ -0,0 +1,11 @@
export enum SetViewMode {
List,
Grid
}
export enum ElementViewMode {
Full,
setElement
}
export type ViewMode = SetViewMode | ElementViewMode;

View File

@@ -1,4 +1,4 @@
<div class="thumbnail">
<img *ngIf="thumbnail" [src]="thumbnail.content" (error)="errorHandler($event)"/>
<img *ngIf="!thumbnail" [src]="holderSource | dsSafeUrl"/>
<img *ngIf="!thumbnail" [src]="defaultImage | dsSafeUrl"/>
</div>

View File

@@ -1 +1,4 @@
@import '../../styles/variables.scss';
img {
max-width: 100%;
}

View File

@@ -36,7 +36,7 @@ describe('ThumbnailComponent', () => {
it('should display placeholder', () => {
fixture.detectChanges();
const image: HTMLElement = de.query(By.css('img')).nativeElement;
expect(image.getAttribute('src')).toBe(comp.holderSource);
expect(image.getAttribute('src')).toBe(comp.defaultImage);
});
});

View File

@@ -21,10 +21,10 @@ export class ThumbnailComponent {
/**
* The default 'holder.js' image
*/
holderSource = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23EEEEEE%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
@Input() defaultImage? = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23EEEEEE%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
errorHandler(event) {
event.currentTarget.src = this.holderSource;
event.currentTarget.src = this.defaultImage;
}
}