🔎 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 { 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({
|
||||||
|
@ -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>
|
||||||
|
@ -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}`]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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]
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
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']
|
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;
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user