🔎 Added search functionality

This commit is contained in:
corner 2019-09-29 16:01:10 +02:00
parent a476dcdc1a
commit 2168577c6d
12 changed files with 189 additions and 11 deletions

View File

@ -3,13 +3,15 @@ import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './components/home/home.component'; import { HomeComponent } from './components/home/home.component';
import { ArtistsComponent } from './components/artists/artists.component'; import { ArtistsComponent } from './components/artists/artists.component';
import { AlbumsComponent } from './components/albums/albums.component'; import { AlbumsComponent } from './components/albums/albums.component';
import { SearchComponent } from './components/search/search.component';
const routes: Routes = [ const routes: Routes = [
{ path: '', component: HomeComponent }, { path: '', component: HomeComponent },
{ path: 'artists/:id', component: ArtistsComponent }, { path: 'artists/:id', component: ArtistsComponent },
{ path: 'artists', component: ArtistsComponent }, { path: 'artists', component: ArtistsComponent },
{ path: 'albums/:id', component: AlbumsComponent }, { path: 'albums/:id', component: AlbumsComponent },
{ path: 'albums', component: AlbumsComponent } { path: 'albums', component: AlbumsComponent },
{ path: 'search/:query', component: SearchComponent }
]; ];
@NgModule({ @NgModule({

View File

@ -29,11 +29,11 @@
<!-- Sidenav toggle --> <!-- Sidenav toggle -->
<nav class="navbar navbar-light bg-light justify-content-between border-bottom"> <nav class="navbar navbar-light bg-light justify-content-between border-bottom">
<button type="button" class="btn btn-outline-secondary" (click)="toggleNav();"> <button type="button" class="btn btn-outline-secondary" (click)="toggleNav();">
<fa-icon [icon]="this.navToggled ? faChevronLeft : faChevronRight" [fixedWidth]="true"></fa-icon> <fa-icon [icon]="this.navToggled ? faChevronRight : faChevronLeft" [fixedWidth]="true"></fa-icon>
</button> </button>
<span class="navbar-brand">{{ router.url }}</span> <span class="navbar-brand">{{ router.url }}</span>
<form class="form-inline" id="navbar-search"> <form (ngSubmit)="onSubmit()" [formGroup]="searchForm" class="form-inline" id="navbar-search">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"> <input formControlName="query" class="form-control mr-sm-2" type="search" placeholder="Search..." aria-label="Search">
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button> <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
</form> </form>
</nav> </nav>

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { faGlobeEurope, faMusic, faCompactDisc, faUsers, faCog, faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons'; import { faGlobeEurope, faMusic, faCompactDisc, faUsers, faCog, faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons';
import { Router, UrlSegment } from '@angular/router'; import { Router } from '@angular/router';
import { FormBuilder } from '@angular/forms';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
@ -8,7 +9,6 @@ import { Router, UrlSegment } from '@angular/router';
styleUrls: ['./app.component.scss'] styleUrls: ['./app.component.scss']
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
title = 'Cassettea';
navToggled; navToggled;
// FontAwesome // FontAwesome
@ -20,8 +20,14 @@ export class AppComponent implements OnInit {
faChevronLeft = faChevronLeft; faChevronLeft = faChevronLeft;
faChevronRight = faChevronRight; faChevronRight = faChevronRight;
searchForm = this.fb.group({
query: ''
});
constructor(public router: Router) { } constructor(
public router: Router,
private fb: FormBuilder
) { }
ngOnInit() { ngOnInit() {
this.navToggled = false; this.navToggled = false;
@ -30,4 +36,8 @@ export class AppComponent implements OnInit {
toggleNav() { toggleNav() {
this.navToggled = (this.navToggled) ? false : true; this.navToggled = (this.navToggled) ? false : true;
} }
onSubmit() {
this.router.navigate([`search/${this.searchForm.value.query}`]);
}
} }

View File

@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { FormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module'; import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@ -12,6 +12,7 @@ import { ArtistsComponent } from './components/artists/artists.component';
import { AlbumsComponent } from './components/albums/albums.component'; import { AlbumsComponent } from './components/albums/albums.component';
import { ControlsComponent } from './components/controls/controls.component'; import { ControlsComponent } from './components/controls/controls.component';
import { SonglistComponent } from './components/songlist/songlist.component'; import { SonglistComponent } from './components/songlist/songlist.component';
import { SearchComponent } from './components/search/search.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -20,14 +21,16 @@ import { SonglistComponent } from './components/songlist/songlist.component';
ArtistsComponent, ArtistsComponent,
AlbumsComponent, AlbumsComponent,
ControlsComponent, ControlsComponent,
SonglistComponent SonglistComponent,
SearchComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
AppRoutingModule, AppRoutingModule,
HttpClientModule, HttpClientModule,
FontAwesomeModule, FontAwesomeModule,
FormsModule FormsModule,
ReactiveFormsModule
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

View File

@ -9,6 +9,7 @@ export class Song {
artist: number; artist: number;
album: number; album: number;
path: string; path: string;
type = 'Song';
data: DataService; data: DataService;
@ -46,6 +47,7 @@ export class Artist {
name: string; name: string;
albums: Array<number>; albums: Array<number>;
songs: Array<number>; songs: Array<number>;
type = 'Artist';
constructor(data: any) { constructor(data: any) {
this.id = data.data.result[0].id; this.id = data.data.result[0].id;
@ -65,6 +67,7 @@ export class Album {
name: string; name: string;
artist: number; artist: number;
image: string; image: string;
type = 'Album';
data: DataService; data: DataService;

View File

@ -0,0 +1,41 @@
<div class="container mt-3">
<div class="artists">
<h4>
<fa-icon class="mr-1" [icon]="faUser"></fa-icon> Artists
</h4>
<ul *ngIf="artists.length > 0; else noArtists">
<li *ngFor="let artist of artists">
<a [routerLink]="'/artists/' + artist.id">{{ artist.name }}</a>
</li>
</ul>
<ng-template #noArtists>
There were no artists found.
</ng-template>
</div>
<div class="albums">
<h4>
<fa-icon class="mr-1" [icon]="faCompactDisc"></fa-icon> Albums
</h4>
<ul *ngIf="albums.length > 0; else noAlbums">
<li *ngFor="let album of albums">
<a [routerLink]="'/albums/' + album.id">{{ album.name }}</a>
</li>
</ul>
<ng-template #noAlbums>
There were no albums found.
</ng-template>
</div>
<div class="songs">
<h4><fa-icon class="mr-1" [icon]="faMusic"></fa-icon> Songs</h4>
<div *ngIf="songs.length > 0; else noSongs">
<app-songlist *ngIf="dataLoaded" [songs]="songs"></app-songlist>
</div>
<ng-template #noSongs>
There were no songs found.
</ng-template>
</div>
</div>

View File

@ -0,0 +1,17 @@
div.container {
display: grid;
grid-template-areas: 'artists songs'
'albums songs';
div.artists {
grid-area: artists;
}
div.albums {
grid-area: albums;
}
div.songs {
grid-area: songs;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SearchComponent } from './search.component';
describe('SearchComponent', () => {
let component: SearchComponent;
let fixture: ComponentFixture<SearchComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SearchComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,67 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ApiService } from 'src/app/services/api.service';
import { faMusic, faCompactDisc, faUser } from '@fortawesome/free-solid-svg-icons';
import { Song, Album, Artist } from '../../classes/entities';
@Component({
selector: 'app-search',
templateUrl: './search.component.html',
styleUrls: ['./search.component.scss']
})
export class SearchComponent implements OnInit {
// ? fontAwesome imports
faMusic = faMusic;
faCompactDisc = faCompactDisc;
faUser = faUser;
songs: Song[] = [];
albums: Album[] = [];
artists: Artist[] = [];
dataLoaded = false;
constructor(
private route: ActivatedRoute,
private api: ApiService
) { }
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.songs = [];
this.albums = [];
this.artists = [];
this.api.search(params.get('query')).then(results => {
if (results.length) {
results.forEach(result => {
if (result.type === 'Song') {
this.songs.push(result);
} else if (result.type === 'Album') {
this.albums.push(result);
} else if (result.type === 'Artist') {
this.artists.push(result);
}
});
} else {
if (results.type === 'Song') {
this.songs.push(results);
} else if (results.type === 'Album') {
this.albums.push(results);
} else if (results.type === 'Artist') {
this.artists.push(results);
}
}
this.dataLoaded = true;
});
});
}
}

View File

@ -9,7 +9,7 @@ import { AudioService } from 'src/app/services/audio.service';
styleUrls: ['./songlist.component.scss'] styleUrls: ['./songlist.component.scss']
}) })
export class SonglistComponent implements OnInit { export class SonglistComponent implements OnInit {
@Input() songs: Song[]; @Input() songs: any;
songsInfo = []; songsInfo = [];
currentSongId: number; currentSongId: number;

View File

@ -70,4 +70,10 @@ export class ApiService {
// ... and convert that data to entities. // ... and convert that data to entities.
return this.entityService.createEntityFromData(data); return this.entityService.createEntityFromData(data);
} }
async search(query: string) {
const data = await this.data.search(query);
return this.entityService.createEntityFromData(data);
}
} }

View File

@ -81,4 +81,8 @@ export class DataService {
getAllArtists(): Promise<AxiosResponse<ApiData>> { getAllArtists(): Promise<AxiosResponse<ApiData>> {
return this.get('artist', 'all'); return this.get('artist', 'all');
} }
search(query: string): Promise<AxiosResponse<ApiData>> {
return this.axiosInstance.get(`${this.apiUrl}/search/${query}`);
}
} }