Opret en service
Opret en ny service:
1 |
$ ng g s _service/ApiCom |
Jeg vælger at kalder den ApiCom (Api Communication), men det er helt op til dig at vælge et navn. Derudover placerer jeg alle mine services under ~/mitProjekt/src/app/_service.
Dette er den nye service og jeg har derudover importeret Angular’s indbyggede http klient og føjet en ny instans til constructor
funktionen:
1 2 3 4 5 6 7 8 9 10 |
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class ApiComService { constructor(private http: HttpClient) { } } |
Der findes en hjemmeside med eksempeldata, som kan benyttes i undervisningssituationer o.lign. Den hedder jsonplaceholder.typicode.com og den vil jeg bruge her også:
Nu tilføjer vi nemlig en ny funktion i vores service modul, der henter data på nogle brugere:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class ApiComService { constructor(private http: HttpClient) { } getUsers() { return this.http.get('http://jsonplaceholder.typicode.com/users'); } } |
Voila! Det var det… nu kan vi så bruge den nye service i et komponent der skal præsentere disse data:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { Component, OnInit } from '@angular/core'; import { ApiCom } from '../_service/api-com.service'; import { Observable } from 'rxjs'; @Component({ selector: 'et-komponent', templateUrl: './et-komponent.component.html', styleUrls: ['./et-komponent.component.scss'] }) export class EtKomponentComponent implements OnInit { users$: Object; constructor(private data: ApiComService) { } ngOnInit() { this.data.getUsers().subscribe( data => this.users$ = data ) } } |
Bemærk at vi importerer vores egen service og derudover Observable
fra rxjs
. Sidstnævnte gennemgås ikke i detaljer her, men kort sagt er det et modul der vil indeholde de data vi henter via vores api service. Vi har også erklæret et users$ object og placeret hentningen af data i ngOnInit
. Det betyder at så snart EtKomponent bliver aktiveret, hentes data.
I EtKomponent
‘s html fil, kan man nu præsentere disse data:
1 2 3 4 5 6 7 8 9 10 |
<h1>Brugere</h1> <ul> <li *ngFor="let user of users$"> <span> Navn: {{user.name}}<br> Email: {{user.email}} </span> </li> </ul> |
Funktionen *ngFor
itererer over alle elementer i users$
og dermed udskrives en hele listen.
Husk at føje din ApiCom
service til app.module.ts
:
1 |
import { ApiComService} from './_service/api-com.service'; |
… og i samme fil, skal den også føjes til
providers
:
1 |
providers: [ApiComService], |
dev, test, prod… api adressen er forskellig
Du har sikkert bemærket filerne ~/mitProject/environment.ts og environment.prod.ts
. Her angiver du de værdier der differentierer mellem de forskellige miljøer (typisk udvikling, test og produktion). I mit projekt skal jeg distingvere mellem test og produktion.
Når du kører ng serve -o
bruger Angular environment.ts
:
1 2 3 4 |
export const environment = { production:false, apiUrl:"/api/" }; |
…og ved
ng serve -o --prod
bruger Angular environment.prod.ts
:
1 2 3 4 |
export const environment = { production:true, apiUrl:"https://api.eksempel.com" }; |
Nu kan du importere environment i din api service som dermed har adgang til den url der skal benyttes i et givent miljø:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { environment } from '../../environments/environment'; //<= Her importeres environment @Injectable({ providedIn: 'root' }) export class ApiComService { constructor(private http: HttpClient) { } private _edApiUrl = environment.apiUrl; //<= Bemærk her hentes api adressen edPost(jsonParams) { console.log("Fetching data from "+this._edApiUrl+" with the following params:"); console.log(jsonParams); return this.http.post(this._edApiUrl,jsonParams); } } |
Så langt så godt…. men vi kaster lige et blik på environment.ts igen. Her har vi i apiUrl blot angivet en relativ url i stedet for en absolut. Her står nemlig blot /api. Det er der en forklaring på:
Hvis du udvikler på en server og har både front- og backend under samme domæne, kan du blot angive adressen på dit test-domæne i environment.ts. Men hvis du, ligesom jeg, udvikler din frontend på localhost og din backend ligger på en server, får du hurtigt en CORS udfordring… Det kommer ofte til udtryk ved denne fejl:
Failed to load ‘https://test.eksempel.com’: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:4200’ is therefore not allowed access.
Det løser vi i følgende afsnit:
Proxy indstillinger
For at undgå en CORS fejl når du udvikler lokalt og ønsker at benytte en ekstern ressource, kan du konfigurere en proxy. Du opretter ganske enkelt filen ~mitProjekt/proxy.conf.json
:
1 2 3 4 5 6 7 8 9 10 |
{ "/api": { "target": "https://test-api.eksempel.com", "secure": false, "pathRewrite": { "^/api": "" }, "changeOrigin": true } } |
… og føjer den til angular.json
:
1 2 3 4 5 6 |
"serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { "browserTarget": "edApp:build", "proxyConfig": "proxy.conf.json" //<= indsæt her }, |
Fremover når du benytter
ng serve
vil Angular medtage dine proxy indstillinger og i kulissen dirigere alle forespørgsler til /api
videre til https://test-api.eksempel.com … Din browser tror at forespørgslen går til localhost:4200/api
og du undgår dermed CORS fejl 🙂