🔎 Added search functionality
This commit is contained in:
parent
a476dcdc1a
commit
2168577c6d
@ -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({
|
||||
|
@ -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>
|
||||
|
@ -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}`]);
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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;
|
||||
|
||||
|
41
src/app/components/search/search.component.html
Normal file
41
src/app/components/search/search.component.html
Normal 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>
|
17
src/app/components/search/search.component.scss
Normal file
17
src/app/components/search/search.component.scss
Normal 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;
|
||||
}
|
||||
}
|
25
src/app/components/search/search.component.spec.ts
Normal file
25
src/app/components/search/search.component.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
67
src/app/components/search/search.component.ts
Normal file
67
src/app/components/search/search.component.ts
Normal 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;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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}`);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user