(front) Autocomplete search on dashboard with Datalist

Create Elasticsearch service to search suggestions

Send to page when choose a suggestion - bad way
It's impossible to use Object with datalist
So it's impossible to distinguish an album from an artist if they have
the same name
This commit is contained in:
2021-08-12 23:23:55 +02:00
parent 5fbcfbae68
commit 52a0cde401
5 changed files with 94 additions and 1 deletions

View File

@@ -1,6 +1,7 @@
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard/dashboard.component'; import { DashboardComponent } from './dashboard/dashboard.component';
@@ -26,6 +27,7 @@ import { AlbumsComponent } from './albums/albums.component';
imports: [ imports: [
BrowserModule, BrowserModule,
HttpClientModule, HttpClientModule,
FormsModule,
AppRoutingModule AppRoutingModule
], ],
declarations: [ declarations: [

View File

@@ -77,6 +77,25 @@
</div> </div>
</div> </div>
<form class="navbar-form">
<div class="form-group" style="display:inline;">
<div class="input-group" style="display:table;">
<span class="input-group-addon" style="width:1%;">
<span class="glyphicon glyphicon-search"></span>
</span>
<input class="form-control" type="text" name="search" placeholder="Search Here" autocomplete="off" autofocus="autofocus"
id="searchSuggest"
list="dynmicUserIds"
[(ngModel)]="searchTerm"
(keyup)="onSearchChange()"
(change)="onSearchSelected($event)">
<datalist id="dynmicUserIds">
<option *ngFor="let item of suggested" [value]="item.name" [label]="item.type" [id]="item">{{item}}</option>
</datalist>
</div>
</div>
</form>
<div class="col-md-12"> <div class="col-md-12">
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>

View File

@@ -1,10 +1,12 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ElsService } from './../els.service'; import { ElsService } from './../els.service';
import { Song } from './../model/song'; import { Song } from './../model/song';
import { Bucket } from './../model/bucket'; import { Bucket } from './../model/bucket';
import { Album } from './../model/album'; import { Album } from './../model/album';
import { Artist } from './../model/artist'; import { Artist } from './../model/artist';
import { Suggested } from '../model/suggested';
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
@@ -30,7 +32,10 @@ export class DashboardComponent implements OnInit {
lastAddedAlbums: Bucket[] = []; lastAddedAlbums: Bucket[] = [];
albumArtists = []; albumArtists = [];
constructor(private elsService: ElsService) { } searchTerm = ''
suggested : Suggested[] = []
constructor(private elsService: ElsService, private route: Router) { }
ngOnInit(): void { ngOnInit(): void {
this.elsService.getTime().then(result => { this.elsService.getTime().then(result => {
@@ -113,4 +118,19 @@ export class DashboardComponent implements OnInit {
} }
}); });
} }
onSearchChange() {
this.elsService.getSuggest(this.searchTerm).subscribe(result => this.suggested = result);
}
onSearchSelected($event) {
let selected = $event.target.value
// FIXME Not possible to get good element (just value)
// Need to use a plugin to do this correctly
this.suggested.forEach(element => {
if (element.name == selected) {
this.route.navigate(['/' + element.type + '/' + element.name])
}
});
}
} }

View File

@@ -8,6 +8,7 @@ import { Song } from './model/song';
import { Album } from './model/album'; import { Album } from './model/album';
import { Artist } from './model/artist'; import { Artist } from './model/artist';
import { Bucket } from './model/bucket'; import { Bucket } from './model/bucket';
import { Suggested } from './model/suggested';
@Injectable() @Injectable()
export class ElsService { export class ElsService {
@@ -15,6 +16,7 @@ export class ElsService {
public static readonly SONG_INDEX_NAME = '/itunes-songs'; public static readonly SONG_INDEX_NAME = '/itunes-songs';
public static readonly ARTIST_INDEX_NAME = '/itunes-artists'; public static readonly ARTIST_INDEX_NAME = '/itunes-artists';
public static readonly ALBUM_INDEX_NAME = '/itunes-albums'; public static readonly ALBUM_INDEX_NAME = '/itunes-albums';
public static readonly SUGGEST_INDEX_NAME = '/itunes-suggest';
protected static readonly ACTION_SEARCH = '/_search'; protected static readonly ACTION_SEARCH = '/_search';
protected static readonly ACTION_COUNT = '/_count'; protected static readonly ACTION_COUNT = '/_count';
@@ -381,6 +383,34 @@ export class ElsService {
); );
} }
getSuggest(text: string): Observable<Suggested[]> {
console.log('search sugget: ' + text);
return this.http
.post<any>(this.elsUrl + ElsService.SUGGEST_INDEX_NAME + ElsService.ACTION_SEARCH,
JSON.stringify({
'_source': ['album', 'artist'],
'suggest': {
'album-suggest': {
'prefix': text,
'completion': {
'field': 'album_suggest'
}
},
'artist-suggest': {
'prefix': text,
'completion': {
'field': 'artist_suggest'
}
}
}
}), {headers: this.headers})
.pipe(
map(res => this.responseSuggesterToSuggested(res, 'album-suggest', 'artist-suggest')),
catchError(error => this.handleError(error, 'getSuggest(' + text + ')'))
);
}
/** Process a result to return just one result. /** Process a result to return just one result.
* Used to get an album or an artist. * Used to get an album or an artist.
* Take a name to put in console output if no result or more than one result. * Take a name to put in console output if no result or more than one result.
@@ -462,6 +492,20 @@ export class ElsService {
return result; return result;
} }
protected responseSuggesterToSuggested(res: any, ...suggestName: string[]): Suggested[] {
const result: Array<Suggested> = []
suggestName.forEach(sname => {
res['suggest'][sname][0]['options'].forEach(option => {
let suggest = new Suggested()
// TODO If more than one key, raise exception
suggest.type = String(Object.keys(option['_source']))
suggest.name = option['_source'][suggest.type]
result.push(suggest)
});
});
return result;
}
protected handleError(error: any, origin: string): Promise<any> { protected handleError(error: any, origin: string): Promise<any> {
console.error('An error occurred!'); console.error('An error occurred!');
console.error('Origin function: ', origin); console.error('Origin function: ', origin);

View File

@@ -0,0 +1,8 @@
export class Suggested {
name: string;
type: string;
public toString() : string {
return `${this.name} (${this.type})`;
}
}