ActivatedRoute: Inviare params alle backend API

Nello sviluppo di un frontend con Angular può esserci la necessità di passare un parametro come query string al backend per poi utilizzarlo come filtro nelle nostre API. La soluzione -che apparentemente può sembrare complicata- si risolve in pochissimi passaggi. La parte più interessante a mio avviso sarà poi il lavoro che si svolgerà a backend per ottimizzare il tutto.

ActivatedRoute: utilizzo step by step

Per arrivare ad utilizzare ActivatedRoute e ricevere il nostro parametro in ingresso dobbiamo prima svolgere delle operazioni preliminari. Eccole in ordine e spero di non essermi perso dei passaggi nel scriverle.

Nell’esempio proposto utilizzeremo il componente “features-work” con una modalità doppia.

  • /features-work

  • /features-work/{Id}

Nel primo caso verranno mostrate tutte le features dei lavori offerti. Nel secondo il parametro id andrà ad effettuare un filtro per categoria attraverso la nostra API in C# app.module.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { RouterModule } from '@angular/router';

import { FeaturesWorkComponent } from './features-work/features-work.component';

@NgModule({
  declarations: [
    ...,
		FeaturesWorkComponent
  ],
  imports: [
    BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
    HttpClientModule,
    FormsModule,
    RouterModule.forRoot([
      ... ,
	    { path: 'features-work', component: FeaturesWorkComponent },
	    { path: 'features-work/:id', component: FeaturesWorkComponent }
    ] )
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }


features-work.component.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

import { Component, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router, ActivatedRoute } from '@angular/router';
import { isUndefined, isNullOrUndefined } from 'util';

@Component({
	selector: 'app-features-work',
	templateUrl: './features-work.component.html'
})
export class FeaturesWorkComponent {
	public featuresWork: FeaturesWork[];

	private id = "";
    
	constructor(private activatedRoute: ActivatedRoute,http: HttpClient, @Inject('BASE_URL') baseUrl: string) {

		this.activatedRoute.paramMap.subscribe(params => {
			this.id = params.get("id");
		});

		let url = baseUrl + 'api/FeaturesWorks';

		if (isNullOrUndefined(this.id) == false  ) {
			url = url + "/" + this.id;
		}

		http.get<FeaturesWork[]>(url).subscribe(result => {
		  this.featuresWork = result;
		  console.log(this.featuresWork);
    }, error => console.error(error));
  }
}

interface FeaturesWork {
	Id: string;
	ImagePath: number;
	Heading: number;
	Content: string;
}


Come potete notare le modifiche da fare nella parte di frontend Angular sono davvero poche e ben mirate. Nello specifico cosa abbiamo fatto?

  • In app.module.ts abbiamo detto che è possibile entrare in “features-work” con due rotte. La prima senza parametri e la seconda con un parametro chiamato Id

  • In features-work.component.ts sfruttiamo ActivatedRoute all’interno del costruttore (tramite injection) per capire se vi è o meno la presenza del paramentro Id. In questo modo il link con cui chiamaremo la nostra API sarà diverso.

HttpGet API: Come ottenere tutti (o quasi) i valori da mostrare

Una volta composto l’url di chiamata della nostra API dobbiamo distingure il comportamento. Per farlo utilizzeremo due HttpGet distinti dalla presenza o meno del parametro. FeaturesWork.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

 public class FeaturesWork
    {
        public Guid Id { get; set; }
        public string ImagePath { get; set; }
        public string Heading { get; set; }
        public string Content { get; set; }
        public int SortPosition { get; set; }
        public bool IsDeleted { get; set; }
    }

Controllers - FeaturesWorksController

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

[Route("api/[controller]")]
[ApiController]
public class FeaturesWorksController : ControllerBase
{

	private const string _connectionString = "connection for SQL Db";
	private const string _tableName = "TableName";
	private const string _filterField = "CategoryId";

	public FeaturesWorksController()
	{
	}

	// GET: api/FeaturesWorks
	[HttpGet]
	public async Task<IEnumerable<FeaturesWork>> GetFeaturesWork()
	{
		using (var connection = new SqlConnection(_connectionString))
		{
			var result = await connection.QueryAsync<FeaturesWork>(
				$@"SELECT * FROM {_tableName} ");

			return result;
		}
	}

	// GET: api/FeaturesWorks/5
	[HttpGet("{id}")]
	public async Task<IEnumerable<FeaturesWork>> GetFeaturesWork(string id)
	{
		using (var connection = new SqlConnection(_connectionString))
		{
			var result = await connection.QueryAsync<FeaturesWork>(
				$@"SELECT * 
					FROM {_tableName} 
					WHERE {_filterField} = '{id}' ");

			return result;
		}
	}
}

Come potete notare non ho fatto nulla di particolare all’interno della nostra API. Quello che è degno di nota è l’utilizzo di DAPPER e non di EntityFramework in quanto personalmente non mi piace e lo evito più che volentieri.

Pensavate fosse più complicato da gestire il tutto? Inizialmente anche io, ma quando ho preso in mano Angular non avevo in mente cosa aspettarmi. Ora che lo utilizzo da qualche mese devo dire che comincio a fare delle cose molto più divertenti abbandonando i json di test.