44024: simple search UI with working search results

This commit is contained in:
Lotte Hofstede
2017-08-21 15:32:52 +02:00
parent 563cf6e820
commit bacb048bb6
26 changed files with 183 additions and 39 deletions

View File

@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, Inject } from '@angular/core';
import { Collection } from '../../core/shared/collection.model';
import { ObjectListElementComponent } from '../object-list-element/object-list-element.component';
@@ -11,4 +11,4 @@ import { listElementFor } from '../list-element-decorator';
})
@listElementFor(Collection)
export class CollectionListElementComponent extends ObjectListElementComponent {}
export class CollectionListElementComponent extends ObjectListElementComponent<Collection> {}

View File

@@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core';
import { Component, Input, Inject } from '@angular/core';
import { Community } from '../../core/shared/community.model';
import { ObjectListElementComponent } from '../object-list-element/object-list-element.component';
@@ -11,4 +11,4 @@ import { listElementFor } from '../list-element-decorator';
})
@listElementFor(Community)
export class CommunityListElementComponent extends ObjectListElementComponent {}
export class CommunityListElementComponent extends ObjectListElementComponent<Community> {}

View File

@@ -1,4 +1,4 @@
import { Component, Input } from '@angular/core';
import { Component, Input, Inject } from '@angular/core';
import { Item } from '../../core/shared/item.model';
import { ObjectListElementComponent } from '../object-list-element/object-list-element.component';
@@ -11,4 +11,4 @@ import { listElementFor } from '../list-element-decorator';
})
@listElementFor(Item)
export class ItemListElementComponent extends ObjectListElementComponent {}
export class ItemListElementComponent extends ObjectListElementComponent<Item> {}

View File

@@ -1,12 +1,16 @@
import { ListableObject } from './listable-object/listable-object.model';
import { GenericConstructor } from '../core/shared/generic-constructor';
const listElementForMetadataKey = Symbol('listElementFor');
export function listElementFor(value: GenericConstructor<ListableObject>) {
return Reflect.metadata(listElementForMetadataKey, value);
const listElementMap = new Map();
export function listElementFor(listable: GenericConstructor<ListableObject>) {
return function decorator(objectElement: any) {
if (!objectElement) {
return;
}
listElementMap.set(listable, objectElement);
};
}
export function getListElementFor(target: any) {
return Reflect.getOwnMetadata(listElementForMetadataKey, target);
export function getListElementFor(listable: GenericConstructor<ListableObject>) {
return listElementMap.get(listable);
}

View File

@@ -1,3 +1 @@
export interface ListableObject {
}
export interface ListableObject {}

View File

@@ -6,8 +6,9 @@ import { ListableObject } from '../listable-object/listable-object.model';
styleUrls: ['./object-list-element.component.scss'],
templateUrl: './object-list-element.component.html'
})
export class ObjectListElementComponent {
// In the current version of Angular4, @Input is not supported by the NgComponentOutlet - instead we're using DI
constructor(@Inject('objectElementProvider') public object: ListableObject) { }
export class ObjectListElementComponent <T extends ListableObject> {
object: T;
public constructor(@Inject('objectElementProvider') public listable: ListableObject) {
this.object = listable as T;
}
}

View File

@@ -0,0 +1,6 @@
<a [routerLink]="['/collections/' + dso.id]" class="lead">
{{dso.name}}
</a>
<div *ngIf="dso.shortDescription" class="text-muted">
{{dso.shortDescription}}
</div>

View File

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

View File

@@ -0,0 +1,15 @@
import { Component } from '@angular/core';
import { listElementFor } from '../../list-element-decorator';
import { CollectionSearchResult } from './collection-search-result.model';
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Collection } from '../../../core/shared/collection.model';
@Component({
selector: 'ds-collection-search-result-list-element',
styleUrls: ['collection-search-result-list-element.component.scss'],
templateUrl: 'collection-search-result-list-element.component.html'
})
@listElementFor(CollectionSearchResult)
export class CollectionSearchResultListElementComponent extends SearchResultListElementComponent<CollectionSearchResult, Collection> {}

View File

@@ -0,0 +1,5 @@
import { SearchResult } from '../../../search/search-result.model';
import { Collection } from '../../../core/shared/collection.model';
export class CollectionSearchResult extends SearchResult<Collection> {
}

View File

@@ -0,0 +1,6 @@
<a [routerLink]="['/communities/' + dso.id]" class="lead">
{{dso.name}}
</a>
<div *ngIf="dso.shortDescription" class="text-muted">
{{dso.shortDescription}}
</div>

View File

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

View File

@@ -0,0 +1,17 @@
import { Component } from '@angular/core';
import { listElementFor } from '../../list-element-decorator';
import { CommunitySearchResult } from './community-search-result.model';
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Community } from '../../../core/shared/community.model';
@Component({
selector: 'ds-community-search-result-list-element',
styleUrls: ['community-search-result-list-element.component.scss'],
templateUrl: 'community-search-result-list-element.component.html'
})
@listElementFor(CommunitySearchResult)
export class CommunitySearchResultListElementComponent extends SearchResultListElementComponent<CommunitySearchResult, Community> {
}

View File

@@ -0,0 +1,5 @@
import { SearchResult } from '../../../search/search-result.model';
import { Community } from '../../../core/shared/community.model';
export class CommunitySearchResult extends SearchResult<Community> {
}

View File

@@ -0,0 +1,14 @@
<a [routerLink]="['/items/' + dso.id]" class="lead">
{{dso.findMetadata("dc.title")}}
</a>
<div>
<span class="text-muted">
<span *ngIf="dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']);" class="item-list-authors">
<span *ngFor="let authorMd of dso.filterMetadata(['dc.contributor.author', 'dc.creator', 'dc.contributor.*']); let last=last;">{{authorMd.value}}
<span *ngIf="!last">; </span>
</span>
</span>
(<span *ngIf="dso.findMetadata('dc.publisher')" class="item-list-publisher">{{dso.findMetadata("dc.publisher")}}, </span><span *ngIf="dso.findMetadata('dc.date.issued')" class="item-list-date">{{dso.findMetadata("dc.date.issued")}}</span>)
</span>
<div *ngIf="dso.findMetadata('dc.description.abstract')" class="item-list-abstract">{{dso.findMetadata("dc.description.abstract") | dsTruncate:[200] }}</div>
</div>

View File

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

View File

@@ -0,0 +1,15 @@
import { Component } from '@angular/core';
import { listElementFor } from '../../list-element-decorator';
import { ItemSearchResult } from './item-search-result.model';
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Item } from '../../../core/shared/item.model';
@Component({
selector: 'ds-item-search-result-list-element',
styleUrls: ['item-search-result-list-element.component.scss'],
templateUrl: 'item-search-result-list-element.component.html'
})
@listElementFor(ItemSearchResult)
export class ItemSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {}

View File

@@ -0,0 +1,5 @@
import { SearchResult } from '../../../search/search-result.model';
import { Item } from '../../../core/shared/item.model';
export class ItemSearchResult extends SearchResult<Item> {
}

View File

@@ -0,0 +1,19 @@
import { Component, Inject } from '@angular/core';
import { ObjectListElementComponent } from '../object-list-element/object-list-element.component';
import { ListableObject } from '../listable-object/listable-object.model';
import { SearchResult } from '../../search/search-result.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
@Component({
selector: 'ds-search-result-list-element',
template: ``
})
export class SearchResultListElementComponent<T extends SearchResult<K>, K extends DSpaceObject> extends ObjectListElementComponent<T> {
dso: K;
public constructor(@Inject('objectElementProvider') public listable: ListableObject) {
super(listable);
this.dso = this.object.dspaceObject;
}
}

View File

@@ -1,6 +1,7 @@
import { Component, Input, Injector, ReflectiveInjector, OnInit } from '@angular/core';
import { ListableObject } from '../listable-object/listable-object.model';
import { getListElementFor } from '../list-element-decorator'
import { GenericConstructor } from '../../core/shared/generic-constructor';
@Component({
selector: 'ds-wrapper-list-element',
@@ -15,11 +16,12 @@ export class WrapperListElementComponent implements OnInit {
ngOnInit(): void {
this.objectInjector = ReflectiveInjector.resolveAndCreate(
[{provide: 'objectElementProvider', useFactory: () => ({ providedObject: this.object }) }], this.injector);
[{provide: 'objectElementProvider', useFactory: () => (this.object) }], this.injector);
}
private getListElement(): string {
return getListElementFor(this.object.constructor).constructor.name;
getListElement(): string {
const f: GenericConstructor<ListableObject> = this.object.constructor as GenericConstructor<ListableObject>;
return getListElementFor(f);
}
}

View File

@@ -19,7 +19,7 @@ import { DSpaceObject } from '../core/shared/dspace-object.model';
})
export class SearchPageComponent implements OnInit {
private sub;
private results: RemoteData<Array<SearchResult<DSpaceObject>>>;
results: RemoteData<Array<SearchResult<DSpaceObject>>>;
constructor(
private service: SearchService,

View File

@@ -9,6 +9,10 @@ import { SearchPageRoutingModule } from './search-page-routing.module';
import { SearchPageComponent } from './search-page.component';
import { SearchFormComponent } from '../shared/search-form/search-form.component';
import { SearchResultsComponent } from './search-results/search-results.compontent';
import { SearchModule } from '../search/search.module';
import { ItemSearchResultListElementComponent } from '../object-list/search-result-list-element/item-search-result/item-search-result-list-element.component';
import { CollectionSearchResultListElementComponent } from '../object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component';
import { CommunitySearchResultListElementComponent } from '../object-list/search-result-list-element/community-search-result/community-search-result-list-element.component';
@NgModule({
imports: [
@@ -17,11 +21,20 @@ import { SearchResultsComponent } from './search-results/search-results.componte
TranslateModule,
RouterModule,
SharedModule,
SearchModule
],
declarations: [
SearchPageComponent,
SearchFormComponent,
SearchResultsComponent
SearchResultsComponent,
ItemSearchResultListElementComponent,
CollectionSearchResultListElementComponent,
CommunitySearchResultListElementComponent
],
entryComponents: [
ItemSearchResultListElementComponent,
CollectionSearchResultListElementComponent,
CommunitySearchResultListElementComponent
]
})
export class SearchPageModule { }

View File

@@ -1,5 +1,6 @@
import { Component, OnInit, Input } from '@angular/core';
import { RemoteData } from '../../core/data/remote-data';
import { SearchResult } from '../../search/search-result.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
/**
@@ -14,7 +15,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model';
})
export class SearchResultsComponent implements OnInit {
@Input() searchResults: RemoteData<DSpaceObject[]>;
@Input() searchResults: RemoteData<Array<SearchResult<DSpaceObject>>>;
ngOnInit(): void {
// onInit

View File

@@ -9,6 +9,7 @@ import { SearchOptions } from './search.models';
import { hasValue, isNotEmpty } from '../shared/empty.util';
import { Metadatum } from '../core/shared/metadatum.model';
import { Item } from '../core/shared/item.model';
import { ItemSearchResult } from '../object-list/search-result-list-element/item-search-result/item-search-result.model';
@Injectable()
export class SearchService {
@@ -62,12 +63,12 @@ export class SearchService {
elementsPerPage: returningPageInfo.elementsPerPage
});
const payload = itemsRD.payload.map((items: Item[]) => {
return items.sort(()=>{
return items.sort(() => {
const values = [-1, 0, 1];
return values[Math.floor(Math.random() * values.length)];
})
.map((item: Item, index: number) => {
const mockResult: SearchResult<DSpaceObject> = new SearchResult();
const mockResult: SearchResult<DSpaceObject> = new ItemSearchResult();
mockResult.dspaceObject = item;
const highlight = new Metadatum();
highlight.key = 'dc.description.abstract';

View File

@@ -1,15 +1,19 @@
<form [formGroup]="searchFormGroup">
<form [formGroup]="searchFormGroup" action="/search">
<div class="input-group">
<input type="text" class="form-control" aria-label="Search input">
<div ngbDropdown class="input-group-btn">
<button type="submit" class="btn btn-secondary" id="searchDropdown" ngbDropdownToggle>
Go
<div class="input-group-btn" ngbDropdown>
<button type="submit" class="btn btn-secondary">Search DSpace</button>
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false" id="searchDropdown" ngbDropdownToggle>
<span class="sr-only">Toggle Dropdown</span>
</button>
<div ngbDropdownMenu aria-labelledby="searchDropdown">
<div ngbDropdownMenu class="dropdown-menu dropdown-menu-right" aria-labelledby="searchDropdown">
<a class="dropdown-item" href="#">Search DSpace</a>
<a class="dropdown-item" href="#">Search this collection</a>
<a class="dropdown-item" href="#">Search this Collection</a>
<a class="dropdown-item" href="#">Search this Community</a>
</div>
</div>
</div>
</div>
</form>

View File

@@ -25,6 +25,7 @@ import { CommunityListElementComponent } from '../object-list/community-list-ele
import { CollectionListElementComponent } from '../object-list/collection-list-element/collection-list-element.component';
import { TruncatePipe } from './utils/truncate.pipe';
import { WrapperListElementComponent } from '../object-list/wrapper-list-element/wrapper-list-element.component';
import { SearchResultListElementComponent } from '../object-list/search-result-list-element/search-result-list-element.component';
const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -54,10 +55,15 @@ const COMPONENTS = [
ComcolPageLogoComponent,
ObjectListComponent,
ObjectListElementComponent,
WrapperListElementComponent,
WrapperListElementComponent
];
const ENTRY_COMPONENTS = [
// put shared entry components (components that are created dynamically) here
ItemListElementComponent,
CollectionListElementComponent,
CommunityListElementComponent
CommunityListElementComponent,
SearchResultListElementComponent
];
const PROVIDERS = [
@@ -72,7 +78,8 @@ const PROVIDERS = [
],
declarations: [
...PIPES,
...COMPONENTS
...COMPONENTS,
...ENTRY_COMPONENTS
],
exports: [
...MODULES,
@@ -81,6 +88,9 @@ const PROVIDERS = [
],
providers: [
...PROVIDERS
],
entryComponents: [
...ENTRY_COMPONENTS
]
})
export class SharedModule {