Skip to content

Commit 2f7b6a5

Browse files
authored
Merge pull request #5 from actionanand/features/1-http-feature
Features/1 http feature
2 parents fe5aa0c + e024c30 commit 2f7b6a5

File tree

11 files changed

+147
-18
lines changed

11 files changed

+147
-18
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ Change the value of `target` in `proxy file` as your backend api url
1919
yarn develop
2020
```
2121

22+
## Generating Environmental files in Angular 15+
23+
24+
```sh
25+
ng g environments
26+
```
27+
2228
## Cloning Guide
2329

2430
1. Clone only the remote primary HEAD (default: origin/master)

angular.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,13 @@
5151
"development": {
5252
"optimization": false,
5353
"extractLicenses": false,
54-
"sourceMap": true
54+
"sourceMap": true,
55+
"fileReplacements": [
56+
{
57+
"replace": "src/environments/environment.ts",
58+
"with": "src/environments/environment.development.ts"
59+
}
60+
]
5561
}
5662
},
5763
"defaultConfiguration": "production"
Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
<app-places-container title="Available Places">
2+
@if (isFetching() && !errorMsg()) {
3+
<p class="fallback-text">fetching available places!</p>
4+
}
5+
6+
@if (errorMsg()) {
7+
<p class="fallback-text">{{ errorMsg() }}</p>
8+
}
9+
210
@if (places()) {
3-
<app-places [places]="places()!" />
11+
<app-places [places]="places()!" (selectPlace)="onSelectPlaces($event)" />
412
} @else if (places()?.length === 0) {
5-
<p class="fallback-text">Unfortunately, no places could be found.</p>
13+
<p class="fallback-text">Unfortunately, no places could be found.</p>
614
}
715
</app-places-container>
Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { Component, signal } from '@angular/core';
1+
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
22

33
import { Place } from '../../models/place.model';
44
import { PlacesComponent } from '../places.component';
55
import { PlacesContainerComponent } from '../places-container/places-container.component';
6+
import { PlacesService } from '../../services/places.service';
67

78
@Component({
89
selector: 'app-available-places',
@@ -11,6 +12,39 @@ import { PlacesContainerComponent } from '../places-container/places-container.c
1112
styleUrl: './available-places.component.css',
1213
imports: [PlacesComponent, PlacesContainerComponent],
1314
})
14-
export class AvailablePlacesComponent {
15+
export class AvailablePlacesComponent implements OnInit {
1516
places = signal<Place[] | undefined>(undefined);
17+
isFetching = signal(false);
18+
errorMsg = signal('');
19+
20+
private placeServ = inject(PlacesService);
21+
private destroyRef = inject(DestroyRef);
22+
23+
ngOnInit(): void {
24+
this.isFetching.set(true);
25+
26+
const availablePlaceSub = this.placeServ.loadAvailablePlaces().subscribe({
27+
next: resp => {
28+
this.places.set(resp?.places);
29+
this.errorMsg.set('');
30+
},
31+
complete: () => {
32+
this.isFetching.set(false);
33+
},
34+
error: (err: Error) => {
35+
console.error(err.message);
36+
this.errorMsg.set(err.message);
37+
},
38+
});
39+
40+
this.destroyRef.onDestroy(() => availablePlaceSub.unsubscribe());
41+
}
42+
43+
onSelectPlaces(selectedPlace: Place) {
44+
const selectPlaceSub = this.placeServ.addPlaceToUserPlaces(selectedPlace.id).subscribe({
45+
next: resp => console.log('Place added. ', resp),
46+
});
47+
48+
this.destroyRef.onDestroy(() => selectPlaceSub.unsubscribe());
49+
}
1650
}

src/app/places/places.component.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22
@for (place of places(); track place.id) {
33
<li class="place-item">
44
<button (click)="onSelectPlace(place)">
5-
<img
6-
[src]="'http://localhost:3000/' + place.image.src"
7-
[alt]="place.image.alt"
8-
/>
5+
<img [src]="url + place.image.src" [alt]="place.image.alt" />
96
<h3>{{ place.title }}</h3>
107
</button>
118
</li>

src/app/places/places.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Component, input, output } from '@angular/core';
22

33
import { Place } from '../models/place.model';
4+
import { environment as env } from '../../environments/environment.development';
45

56
@Component({
67
selector: 'app-places',
@@ -13,6 +14,8 @@ export class PlacesComponent {
1314
places = input.required<Place[]>();
1415
selectPlace = output<Place>();
1516

17+
protected url = env.backendUrl;
18+
1619
onSelectPlace(place: Place) {
1720
this.selectPlace.emit(place);
1821
}
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
11
<app-places-container title="Your Favorite Places">
2-
<p>Todo...</p>
2+
@if (isFetching() && !errorMsg()) {
3+
<p class="fallback-text">fetching available places!</p>
4+
}
5+
6+
@if (errorMsg()) {
7+
<p class="fallback-text">{{ errorMsg() }}</p>
8+
}
9+
10+
@if (places()) {
11+
<app-places [places]="places()!" />
12+
} @else if (places()?.length === 0) {
13+
<p class="fallback-text">Unfortunately, no places could be found.</p>
14+
}
315
</app-places-container>
Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { Component } from '@angular/core';
1+
import { Component, DestroyRef, inject, OnInit, signal } from '@angular/core';
22

33
import { PlacesContainerComponent } from '../places-container/places-container.component';
44
import { PlacesComponent } from '../places.component';
5+
import { Place } from '../../models/place.model';
6+
import { PlacesService } from '../../services/places.service';
57

68
@Component({
79
selector: 'app-user-places',
@@ -10,5 +12,31 @@ import { PlacesComponent } from '../places.component';
1012
styleUrl: './user-places.component.css',
1113
imports: [PlacesContainerComponent, PlacesComponent],
1214
})
13-
export class UserPlacesComponent {
15+
export class UserPlacesComponent implements OnInit {
16+
places = signal<Place[] | undefined>(undefined);
17+
isFetching = signal(false);
18+
errorMsg = signal('');
19+
20+
private placeServ = inject(PlacesService);
21+
private destroyRef = inject(DestroyRef);
22+
23+
ngOnInit(): void {
24+
this.isFetching.set(true);
25+
26+
const availablePlaceSub = this.placeServ.loadUserPlaces().subscribe({
27+
next: resp => {
28+
this.places.set(resp?.places);
29+
this.errorMsg.set('');
30+
},
31+
complete: () => {
32+
this.isFetching.set(false);
33+
},
34+
error: (err: Error) => {
35+
console.error(err.message);
36+
this.errorMsg.set(err.message);
37+
},
38+
});
39+
40+
this.destroyRef.onDestroy(() => availablePlaceSub.unsubscribe());
41+
}
1442
}

src/app/services/places.service.ts

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
/* eslint-disable @typescript-eslint/no-unused-vars */
2-
import { Injectable, signal } from '@angular/core';
2+
import { inject, Injectable, signal } from '@angular/core';
3+
import { HttpClient } from '@angular/common/http';
4+
5+
import { catchError, map, tap } from 'rxjs/operators';
6+
import { throwError } from 'rxjs';
37

48
import { Place } from '../models/place.model';
59

@@ -8,15 +12,40 @@ import { Place } from '../models/place.model';
812
})
913
export class PlacesService {
1014
private userPlaces = signal<Place[]>([]);
11-
private readonly url = '/api/v2/places';
12-
1315
loadedUserPlaces = this.userPlaces.asReadonly();
1416

15-
loadAvailablePlaces() {}
17+
private http = inject(HttpClient);
1618

17-
loadUserPlaces() {}
19+
loadAvailablePlaces() {
20+
return this.fetchPlaces('/api/v2/places', 'Error loading available places!');
21+
}
1822

19-
addPlaceToUserPlaces(place: Place) {}
23+
loadUserPlaces() {
24+
return this.fetchPlaces('/api/v2/user-places', 'Error loading user places!');
25+
}
26+
27+
addPlaceToUserPlaces(placeId: string) {
28+
return this.http.put('/api/v2/user-places', {
29+
placeId,
30+
});
31+
}
2032

2133
removeUserPlace(place: Place) {}
34+
35+
private fetchPlaces(url: string, errMsg: string) {
36+
return this.http
37+
.get<{ places: Place[] }>(url, {
38+
observe: 'response', // it'll give full response including status code
39+
})
40+
.pipe(
41+
tap(rawResp => {
42+
console.log('Raw Response: ', rawResp);
43+
}),
44+
map(data => data.body),
45+
catchError(error => {
46+
console.error(error);
47+
return throwError(() => new Error(errMsg));
48+
}),
49+
);
50+
}
2251
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const environment = {
2+
backendUrl: 'https://3000-actionanand-angularhttp-six5y8k89a8.ws-us116.gitpod.io/',
3+
};

0 commit comments

Comments
 (0)