Standard Post with Image

Bouw je eigen Spotify

Met Angular kun je alles! Zelfs Spotify namaken. In deze blog maken we een eerste opzet.

Angular is een heeeerlijk framework uit de koker van Google. Het is als het ware een springplank om zelf de frontend van een web applicatie mee te bouwen. We hebben dit trouwens ook gebruikt om SnelStart Web mee te maken. Wil je een beetje ervaren hoe we dit gedaan hebben? Lees dan verder.

Sinds september 2017 zijn we overgestapt van AngularJS (a.k.a. Angular 1) naar het nieuwste van het nieuwste, namelijk Angular (zonder JS erachter a.k.a. Angular 2 a.k.a. ng2 a.k.a. ngx, ehh…). In deze nieuwe versie van Angular zitten heel veel gave dingen waardoor de performance van de applicatie nog beter is geworden. Dit heeft vooral te maken met de nieuwe manier van change detection. Change detection klinkt cool en dat is het ook, maar we gaan er in deze blog nog even niet verder op in. Volgende keer okay?

Er zitten in de nieuwe versie van Angular ook veel verbeteringen die het leven van een ontwikkelaar een stuk aangenamer maakt. Eén van die dingen is de Angular CLI. Zo moest je in de vorige versie van Angular zelf de infrastructuur verzorgen voor zaken zoals package management, compileren, bundelen en testen. Nu legt Angular met de Angular CLI een standaard voor je neer. Dat scheelt heel veel werk in de startfase van een project. Ik wil je graag laten zien hoe makkelijk dit is geworden door samen met jou een brand new Angular project op te zetten!

Wat is dan een leuk onderwerp voor een nieuw Angular project? Het allerleukste en allicht ook de grootste kracht van Angular, is communiceren met andere systemen over "het internet". Die andere systemen worden in de ontwikkelwereld liefkozend API genoemd.

Er zijn talloze toffe publieke API’s die je zou kunnen gebruiken voor je eigen experimenten. Eéntje die ik zelf als muziekliefhebber extra interessant vind is dus de API van Spotify. Laten we eens kijken wat we hiermee uit kunnen spoken!

Angular applicatie aanmaken

  1. Download en installeer de nieuwste versie van Node.js via https://nodejs.org

  2. Je kunt jouw favoriete IDE gebruiken. Onze ervaring is dat Visual Studio Code erg lekker samenwerkt met Angular projecten. Je kunt m gratis gebruiken en is op alle systemen te installeren. Download m via https://code.visualstudio.com/

  3. Open een command-line interface in een directory op je filesystem waar je het nieuwe project wilt aanmaken.
    Pro tip voor Windows: ga naar de juiste directory via de File Explorer, houd Shift ingedrukt en klik op de rechtermuisknop, dan verschijnt de optie om daar direct een Powershell venster te openen.

  4. Installeer de Angular CLI:
    npm install -g @angular/cli
    Hierdoor kun je overal vanaf de command-line het “ng” commando uitvoeren.

  5. Maak een nieuw Angular project aan met behulp van de Angular CLI, ik noem het SpotifyReloaded.
    ng new SpotifyReloaded --routing true
    -- routing true is toegevoegd omdat we nog wat magic met de URI moeten uithalen, dat lees je verderop.

  6. Laat m even uitratelen… et voilá: er is een nieuwe directory ontstaan met de naam SpotifyReloaded waar de CLI een basis neergezet heeft voor ons project. Ga daar in en start alvast VS Code op, mits je deze gebruikt:
    cd SpotifyReloaded
    code .

  7. Start je nieuwe Angular project op
    ng serve
    Hiermee wordt naast een lokale webserver meteen een watcher gestart inclusief live reloading. Als je aanpassingen maakt worden deze meteen gecompileerd en je browser vernieuwd.

  8. Zoals de console output al heeft verklapt kun je nu met je browser naar http://localhost:4200 gaan. Hier staat de standaard gegenereerde pagina van de Angular CLI om te laten zien dat alles werkt.

Het werkt inderdaad, maar het is nog niet zo stylish… Daar brengen we in een volgende blog verandering in, okay? Laten we eerst maar eens iets met onze vriend Spotify doen!

Jouw applicatie aanmelden bij Spotify

Neem eens een kijkje op https://developer.spotify.com/web-api/. Hier staat een schat aan informatie over wat je allemaal met de API van Spotify kunt doen. Ook belangrijk is de Terms of Use, in het geval je het “experimenteer-stadium” ontgroeit.

Wij gaan nu onze applicatie aanmelden bij Spotify zodat we van hun API gebruik kunnen maken. Na het aanmelden krijgen we van Spotify een Client ID toegewezen. Met dit Client ID kunnen we namens een Spotify gebruiker onze applicatie autoriseren zodat we gegevens bij Spotify kunnen opvragen.

Er zijn verschillende manieren (flows) van autoriseren bij Spotify, je kunt ze hier nalezen: https://developer.spotify.com/web-api/authorization-guide/. Wij gaan de Implicit Grant Flow gebruiken. Dit is niet de ideale flow, maar wij hebben hier te maken met een client side applicatie waarvoor dit de enige door Spotify toegelaten flow is. Je wilt Spotify wel te vriend houden toch?

  1. Ga naar https://developer.spotify.com/my-applications/

  2. Log in met een Spotify account of registreer je eerst even (het is gratis)

  3. Kies de optie om een App(lication) aan te maken

  4. Vul de naam van je applicatie in en eventueel een omschrijving en maak aan
    2.png

  5. Noteer de gegenereerde sleutel die bij Client ID staat. Deze heb je later nog nodig.

  6. Voeg de Redirect URI http://localhost:4200 toe. Dit is de URI waar Spotify naar terug moet gaan nadat de Spotify gebruiker is ingelogd vanuit onze eigen applicatie. Wanneer dat gebeurt krijgen we nog wat extra gegevens van Spotify achter deze URI geplakt, dat zie je straks.
    3.png
    Vergeet niet op te slaan! Dit gebeurt op het moment van schrijven nog niet automatisch na het toevoegen van de Redirect URI.

Vanuit jouw applicatie autoriseren bij Spotify

Voordat we daadwerkelijk data kunnen ophalen via de Spotify API, moeten we een Spotify gebruiker laten inloggen vanuit onze applicatie. Daarom maken we eerst een lekker simpele knop waarop staat “Verbind met Spotify” die deze eerste stap gaat regelen.

  1. Ga terug naar je IDE en open het bestand app.component.html.

  2. Vervang de volledige content door:
    <button (click)="connectSpotify()">Verbind met Spotify</button>
    
  3. Open app.component.ts en voeg aan de class AppComponent de volgende methode toe (let er op dat je jouwClientID vervangt):
    connectSpotify() {
           const clientId = "jouwClientID";
           location.href = "https://accounts.spotify.com/authorize?response_type=token&redirect_uri=http://localhost:4200&client_id=" + clientId;
    } 
    Voor de precieze code wijzigingen, zie https://github.com/jvanrossum/SpotifyReloaded/commit/113a05ece8bec8e6a84936c4c495490c3f5b6d77

  4. Ga naar http://localhost:4200 in de browser, dit moet er ongeveer zo uit zien:
    4.png

  5. Als je nu op de knop klikt krijg je het koppelscherm van Spotify:
    5.png

Wanneer je vervolgens inlogt krijg je nog een extra scherm om de koppeling te bevestigen en kom je uiteindelijk terug in onze eigen applicatie. Als je goed naar de URI in de browser kijkt zie je dat http://localhost:4200 nu gevolgd wordt door #access_token=……. en daarna nog twee parameters, te weten token_type en expires_in. De access_token parameter hebben we nodig om data op te kunnen halen bij Spotify, namens deze gebruiker.

Data ophalen bij Spotify

Nu is eindelijk het heuglijke moment aangebroken dat we echt gegevens kunnen ophalen bij Spotify! Overigens kunnen we ook gegevens ópslaan in het account van de gebruiker, zoals playlists, maar in deze blog gaan we alleen nog even wat ophalen.

Leuk om eerst eens te proberen is de ingelogde gebruiker laten zien, inclusief zijn of haar afbeelding. Om dit voor elkaar te krijgen moeten we het access_token opvangen en deze doorgeven als Authorization request header bij het aanroepen van het Spotify API endpoint om de huidige gebruiker op te vragen, zie: https://developer.spotify.com/web-api/get-current-users-profile/. Klinkt als een goed plan.

  1. Ga terug naar je IDE en open het bestand app.component.ts

  2. Voeg de volgende methode toe zodat het access_token uit de URI opgevangen wordt en (tijdelijk) opgeslagen wordt:
      ngOnInit() {
        this.route.fragment.subscribe((fragment: string) => {
          if (fragment.startsWith("access_token=")) {
            let fragmentJson = this.getJsonFromFragmentParams(fragment);
            this.accessToken = fragmentJson.access_token;
          }
        })
      }
    
    Er wordt hierboven ook nog een methode getJsonFromFragmentParams aangeroepen en this.route is een service van de Angular router die geinjecteerd wordt. Je kunt alles overnemen door even het volledige overzicht van de code wijzigingen hier te bekijken: https://github.com/jvanrossum/SpotifyReloaded/commit/481d75ba3c236d6c7ebc6c415178c389821b766f

    En wat is dan die ngOnInit? Hoe wordt die aangeroepen? Waarom doe je dat niet in de constructor?... Goeie vragen! ngOnInit is één van de lifecycle hooks van Angular. Deze wordt door Angular aangeroepen bij het initializeren van de applicatie. Ik zal er nu niet al te diep op ingaan, maar je kunt hier alvast meer lezen over het verschil tussen ngOnInit en de constructor. Het is dus vooral een best practice. We houden er bij SnelStart van om de constructor puur voor DI te gebruiken en de rest in de ngOnInit te doen, dan hoef je ook niet te zoeken.

  3. Zo, nu kunnen we met de volgende methode het accessToken meegeven in de headers om de user data op te halen:
      getUserData() {
        let options: RequestOptionsArgs = {
          headers: new Headers({
            "Authorization": "Bearer " + this.accessToken,
          })
        };
        this.http.get("https://api.spotify.com/v1/me", options)
          .map((resp) => resp.json())
          .subscribe((me: any) => {
            this.name = me.display_name;
            this.imgSrc = me.images[0].url;
          });
      }
    
    Let op: de getUserData methode moet nog aangeroepen worden en this.http is een geïnjecteerde service die aan de constructor moet worden toegevoegd. Er is ook een aanpassing in app.module.ts nodig voor het toevoegen van de HttpModule. Een volledig overzicht van alle code wijzigingen voor deze én de volgende stap kun je hier vinden: https://github.com/jvanrossum/SpotifyReloaded/commit/6425c3ca34be42cf63b9091581a6f7734f037e80

  4. Open nog even app.component.html en vervang de volledige content met het volgende:
    <button *ngIf="!isConnectedToSpotify()" (click)="connectSpotify()">Verbind met Spotify</button>
    <ng-container *ngIf="isConnectedToSpotify()">
      <div><img [src]="imgSrc"/></div>
      {{ name }} 
    </ng-container>
    
  5. Ga nu naar http://localhost:4200 in de browser en verbind met Spotify. Dan moet het er ongeveer zo uit zien:
    6.png
    Overigens krijg je alleen een afbeelding als je inlogt met Facebook of wanneer je een Premium account hebt en dit zelf hebt ingesteld. Als je goed oplet krijg je dan een paar errors in je browser console, je kunt het e.e.a. aan de code defensiever maken, zie: https://github.com/jvanrossum/SpotifyReloaded/commit/22a366f6e90a5eb084e24e9505af9ad716cb5bc6 

Zoeken naar Artiest

Hmm, wat kunnen we nog meer doen? Misschien één van de basisfuncties van Spotify: zoeken naar muziek! Laten we eens proberen hoe ver we komen met zoeken naar een artiest. Het zoeken kan met het volgende API endpoint van Spotify: https://developer.spotify.com/web-api/search-item/.

Als je goed oplet, kun je in dit voorbeeld de kracht van de RxJS voelen. RxJS is naast de Angular CLI één van de twee gaafste dingen die in de nieuwe Angular beschikbaar zijn gekomen. Komt ie.

  1. Ga terug naar je IDE en open app.component.ts

  2. Voeg een methode toe voor het zoeken, waarbij we een algemene methode maken voor het ophalen van de header (DRY principe).
      search(searchTerm: string): Observable<any> {
        return this.http.get("https://api.spotify.com/v1/search?type=artist&q=" + searchTerm, this.getRequestionOptions())
          .map((resp) => resp.json().artists.items);
      }
    
    Voor een overzicht van alle code wijzigingen, inclusief de laatste stappen die hierop volgen, zie https://github.com/jvanrossum/SpotifyReloaded/commit/2dd22698f8edcfb578eb017cd447b3880d88e7ce

  3. We voegen ook een searchTerms Subject toe die de search zal triggeren als er iets in het zoekveld wordt ingetypt:
      triggerSearch(searchTerm: string) {
        this.searchTerms.next(searchTerm);
      }
    
  4. Als laatste actie in app.component.ts maken we in de ngOninit methode een artists Observable beschikbaar aan de html die de resultaten van de search bevat:
        this.artists = this.searchTerms
          .debounceTime(300)
          .distinctUntilChanged()  
          .switchMap(searchTerm => this.search(searchTerm));
    
    Subject en Observable zijn termen die horen bij eerder genoemde RxJS. In het laatste stukje code zie je een goed voorbeeld van hoe makkelijk deze library het maakt om A. 300 ms te wachten op het reageren van het intypen van een nieuwe zoekterm voordat er gezocht gaat worden bij Spotify en B. dat er bij een ongewijzigde zoekterm niet opnieuw gezocht wordt. Awesome. Je kunt nog veel meer gave dingen doen met RxJS, wel de moeite waard voor een volgende blog?

  5. Ten slotte voegen we het invoerveld en de zoekresultaten toe aan app.component.html:
      <hr />
      <input #searchBox (keyup)="triggerSearch(searchBox.value)" />
      <div>
        <div *ngFor="let artist of artists | async">
          {{ artist.name }}
        </div>
      </div>
    
  6. Ga nu voor de laatste keer naar http://localhost:4200 in de browser, verbind eventueel opnieuw met Spotify en zoek op een artiest. Dan zou het er ongeveer zo uit moeten zien:
    7.png

    Kon je dit volgen? Vond je dit leuk om te zien of zelf iets mee te doen? Kom dan eens langs bij SnelStart, want je kunt er hier gewoon je werk van maken!

Bij SnelStart bouwen we applicaties met Angular als frontend. We koppelen Angular net als je hier ziet aan API’s, in dat geval API’s die we zelf maken met het .NET Framework. We hosten alles op het cloud platform Azure. Kortom: we zijn continue bezig met de nieuwste technieken. Doe jij mee?

SnelStart Door Merg en Been YouTube