🔎 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 { ArtistsComponent } from './components/artists/artists.component';
import { AlbumsComponent } from './components/albums/albums.component';
import { SearchComponent } from './components/search/search.component';
const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'artists/:id', component: ArtistsComponent },
{ path: 'artists', component: ArtistsComponent },
{ path: 'albums/:id', component: AlbumsComponent },
{ path: 'albums', component: AlbumsComponent }
{ path: 'albums', component: AlbumsComponent },
{ path: 'search/:query', component: SearchComponent }
];
@NgModule({

View File

@ -29,11 +29,11 @@
<!-- Sidenav toggle -->
<nav class="navbar navbar-light bg-light justify-content-between border-bottom">
<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>
<span class="navbar-brand">{{ router.url }}</span>
<form class="form-inline" id="navbar-search">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
<form (ngSubmit)="onSubmit()" [formGroup]="searchForm" class="form-inline" id="navbar-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>
</form>
</nav>

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
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({
selector: 'app-root',
@ -8,7 +9,6 @@ import { Router, UrlSegment } from '@angular/router';
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
title = 'Cassettea';
navToggled;
// FontAwesome
@ -20,8 +20,14 @@ export class AppComponent implements OnInit {
faChevronLeft = faChevronLeft;
faChevronRight = faChevronRight;
searchForm = this.fb.group({
query: ''
});
constructor(public router: Router) { }
constructor(
public router: Router,
private fb: FormBuilder
) { }
ngOnInit() {
this.navToggled = false;
@ -30,4 +36,8 @@ export class AppComponent implements OnInit {
toggleNav() {
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 { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { FormsModule } from '@angular/forms';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@ -12,6 +12,7 @@ import { ArtistsComponent } from './components/artists/artists.component';
import { AlbumsComponent } from './components/albums/albums.component';
import { ControlsComponent } from './components/controls/controls.component';
import { SonglistComponent } from './components/songlist/songlist.component';
import { SearchComponent } from './components/search/search.component';
@NgModule({
declarations: [
@ -20,14 +21,16 @@ import { SonglistComponent } from './components/songlist/songlist.component';
ArtistsComponent,
AlbumsComponent,
ControlsComponent,
SonglistComponent
SonglistComponent,
SearchComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
FontAwesomeModule,
FormsModule
FormsModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent]

View File

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

View File

@ -70,4 +70,10 @@ export class ApiService {
// ... and convert that data to entities.
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>> {
return this.get('artist', 'all');
}
search(query: string): Promise<AxiosResponse<ApiData>> {
return this.axiosInstance.get(`${this.apiUrl}/search/${query}`);
}
}