docs(toh-6/dart): getHero makes HTTP get-by-id (#2936)
Dart follow-up to #2906
This commit is contained in:
parent
bc416b8e0f
commit
499d4b3c88
|
@ -15,7 +15,7 @@ class HeroService {
|
||||||
static final _headers = {'Content-Type': 'application/json'};
|
static final _headers = {'Content-Type': 'application/json'};
|
||||||
// #enddocregion update
|
// #enddocregion update
|
||||||
// #docregion getHeroes
|
// #docregion getHeroes
|
||||||
static const _heroesUrl = 'app/heroes'; // URL to web API
|
static const _heroesUrl = 'api/heroes'; // URL to web API
|
||||||
|
|
||||||
final Client _http;
|
final Client _http;
|
||||||
|
|
||||||
|
@ -46,8 +46,16 @@ class HeroService {
|
||||||
}
|
}
|
||||||
// #enddocregion handleError, getHeroes
|
// #enddocregion handleError, getHeroes
|
||||||
|
|
||||||
Future<Hero> getHero(int id) async =>
|
// #docregion getHero
|
||||||
(await getHeroes()).firstWhere((hero) => hero.id == id);
|
Future<Hero> getHero(int id) async {
|
||||||
|
try {
|
||||||
|
final response = await _http.get('$_heroesUrl/$id');
|
||||||
|
return new Hero.fromJson(_extractData(response));
|
||||||
|
} catch (e) {
|
||||||
|
throw _handleError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion getHero
|
||||||
|
|
||||||
// #docregion create
|
// #docregion create
|
||||||
Future<Hero> create(String name) async {
|
Future<Hero> create(String name) async {
|
||||||
|
@ -64,7 +72,7 @@ class HeroService {
|
||||||
|
|
||||||
Future<Hero> update(Hero hero) async {
|
Future<Hero> update(Hero hero) async {
|
||||||
try {
|
try {
|
||||||
var url = '$_heroesUrl/${hero.id}';
|
final url = '$_heroesUrl/${hero.id}';
|
||||||
final response =
|
final response =
|
||||||
await _http.put(url, headers: _headers, body: JSON.encode(hero));
|
await _http.put(url, headers: _headers, body: JSON.encode(hero));
|
||||||
return new Hero.fromJson(_extractData(response));
|
return new Hero.fromJson(_extractData(response));
|
||||||
|
@ -77,7 +85,7 @@ class HeroService {
|
||||||
// #docregion delete
|
// #docregion delete
|
||||||
Future<Null> delete(int id) async {
|
Future<Null> delete(int id) async {
|
||||||
try {
|
try {
|
||||||
var url = '$_heroesUrl/$id';
|
final url = '$_heroesUrl/$id';
|
||||||
await _http.delete(url, headers: _headers);
|
await _http.delete(url, headers: _headers);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw _handleError(e);
|
throw _handleError(e);
|
||||||
|
|
|
@ -31,9 +31,14 @@ class InMemoryDataService extends MockClient {
|
||||||
var data;
|
var data;
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case 'GET':
|
case 'GET':
|
||||||
String prefix = request.url.queryParameters['name'] ?? '';
|
final id = int.parse(request.url.pathSegments.last, onError: (_) => null);
|
||||||
final regExp = new RegExp(prefix, caseSensitive: false);
|
if (id != null) {
|
||||||
data = _heroesDb.where((hero) => hero.name.contains(regExp)).toList();
|
data = _heroesDb.firstWhere((hero) => hero.id == id); // throws if no match
|
||||||
|
} else {
|
||||||
|
String prefix = request.url.queryParameters['name'] ?? '';
|
||||||
|
final regExp = new RegExp(prefix, caseSensitive: false);
|
||||||
|
data = _heroesDb.where((hero) => hero.name.contains(regExp)).toList();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
// #enddocregion init-disabled
|
// #enddocregion init-disabled
|
||||||
case 'POST':
|
case 'POST':
|
||||||
|
|
|
@ -212,12 +212,32 @@ block get-heroes-details
|
||||||
We've also decided to return a user friendly form of the error to
|
We've also decided to return a user friendly form of the error to
|
||||||
the caller in a !{rejected_promise} so that the caller can display a proper error message to the user.
|
the caller in a !{rejected_promise} so that the caller can display a proper error message to the user.
|
||||||
|
|
||||||
### Unchanged `getHeroes` API
|
### Get hero by id
|
||||||
|
The `HeroDetailComponent` asks the `HeroService` to fetch a single hero to edit.
|
||||||
|
|
||||||
Although we made significant *internal* changes to `getHeroes()`, the public signature did not change.
|
The `HeroService` currently fetches all heroes and then finds the desired hero
|
||||||
We still return a !{_Promise}. We won't have to update any of the components that call `getHeroes()`.
|
by filtering for the one with the matching `id`.
|
||||||
|
That's fine in a simulation. It's wasteful to ask a real server for _all_ heroes when we only want one.
|
||||||
|
Most web APIs support a _get-by-id_ request in the form `api/hero/:id` (e.g., `api/hero/11`).
|
||||||
|
|
||||||
Our stakeholders are thrilled with the added flexibility from the API integration.
|
Update the `HeroService.getHero` method to make a _get-by-id_ request,
|
||||||
|
applying what we just learned to write `getHeroes`:
|
||||||
|
+makeExcerpt('app/hero.service.ts', 'getHero', '')
|
||||||
|
:marked
|
||||||
|
It's almost the same as `getHeroes`.
|
||||||
|
The URL identifies _which_ hero the server should update by encoding the hero id into the URL
|
||||||
|
to match the `api/hero/:id` pattern.
|
||||||
|
|
||||||
|
We also adjust to the fact that the `data` in the response is a single hero object rather than !{_an} !{_array}.
|
||||||
|
|
||||||
|
### Unchanged _getHeroes_ API
|
||||||
|
|
||||||
|
Although we made significant *internal* changes to `getHeroes()` and `getHero()`,
|
||||||
|
the public signatures did not change.
|
||||||
|
We still return a !{_Promise} from both methods.
|
||||||
|
We won't have to update any of the components that call them.
|
||||||
|
|
||||||
|
Our stakeholders are thrilled with the web API integration so far.
|
||||||
Now they want the ability to create and delete heroes.
|
Now they want the ability to create and delete heroes.
|
||||||
|
|
||||||
Let's see first what happens when we try to update a hero's details.
|
Let's see first what happens when we try to update a hero's details.
|
||||||
|
@ -230,15 +250,12 @@ block get-heroes-details
|
||||||
it. As we type, the hero name is updated in the view heading.
|
it. As we type, the hero name is updated in the view heading.
|
||||||
But when we hit the `Back` button, the changes are lost!
|
But when we hit the `Back` button, the changes are lost!
|
||||||
|
|
||||||
.l-sub-section
|
Updates weren't lost before. What changed?
|
||||||
:marked
|
When the app used a list of mock heroes, updates were applied directly to the
|
||||||
Updates weren't lost before, what's happening?
|
hero objects within the single, app-wide, shared list. Now that we are fetching data
|
||||||
When the app used a list of mock heroes, changes were made directly to the
|
from a server, if we want changes to persist, we'll need to write them back to
|
||||||
hero objects in the single, app-wide shared list. Now that we are fetching data
|
the server.
|
||||||
from a server, if we want changes to persist, we'll need to write them back to
|
|
||||||
the server.
|
|
||||||
|
|
||||||
:marked
|
|
||||||
### Save hero details
|
### Save hero details
|
||||||
|
|
||||||
Let's ensure that edits to a hero's name aren't lost. Start by adding,
|
Let's ensure that edits to a hero's name aren't lost. Start by adding,
|
||||||
|
|
|
@ -228,7 +228,7 @@ block get-heroes-details
|
||||||
The URL identifies _which_ hero the server should update by encoding the hero id into the URL
|
The URL identifies _which_ hero the server should update by encoding the hero id into the URL
|
||||||
to match the `api/hero/:id` pattern.
|
to match the `api/hero/:id` pattern.
|
||||||
|
|
||||||
We also adjust to the fact that the `data` in the response is a single hero object rather than array.
|
We also adjust to the fact that the `data` in the response is a single hero object rather than !{_an} !{_array}.
|
||||||
|
|
||||||
### Unchanged _getHeroes_ API
|
### Unchanged _getHeroes_ API
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue