Applications require data going in and out of the interface. Connecting to a data source is an important aspect of developing an application. Otherwise, you have a complex system of forms, events, and listings with no data to display.
In this chapter, you will learn some of the basics of wiring up a data source in Ember. You will use an API created for this book and create an adapter for your application.
This chapter is a little different from the other chapters in the book. It has more information and less coding. However, this chapter will give you an important real-world view of application development with a server and database that may not be under your control. In the next chapter you will return to your regularly scheduled coding.
As mentioned in Chapter 21, adapters
are the translators of your application. When you communicate with a data source, your
application will need to request and send data in a variety of ways.
Ember Data comes with built-in adapter objects to handle some of
the most common data scenarios: JSONAPI
and generic “RESTful” APIs.
You are going to use the JSONAPIAdapter
object to
connect to a data source and return JSONAPI
-formatted data.
The RESTAdapter
object is set of methods to work with
data formatted from APIs generated from Rails
and ActiveRecord plug-ins for
Rails.
The JSONAPI
spec was created to give API
consumers a predictable and scalable pattern for sending and
receiving data from servers. While there are numerous server
languages – and API object pattern conventions for each –
JSONAPI
set out to be a pattern any language
could use so that front-end applications were not affected by a change in server
technology. You can find out more about JSONAPI
at its website,
jsonapi.org.
In this chapter, you will also learn about security issues as well as serializers, which are the translation layer in the adapter flow. Finally, you will be introduced to transforms, the tool to coerce your data into the types your models expect. Adapters, serializers, and transforms work together as shown in Figure 22.1.
At the end of this chapter, Tracker will look like Figure 22.2.
The Ember team has built their
framework with specific conventions in mind. Adapters are
a large part of those conventions. The JSONAPIAdapter
will communicate with a REST
API
for all requests originating from the store
.
Each request will add the model name and appropriate attribute data
to a relative path of the domain.
To generate a specific URL for Ajax
requests, the adapter needs the properties host
and namespace
. The adapter makes Ajax
requests and expects the JSON responses to be formed with a
particular structure. For example, the
JSONAPIAdapter
would expect a response for
witnesses on a GET
request to look like this:
{ "links": { "self": "http://bnr-tracker-api.herokuapp.com/api/witnesses" }, "data": [ { "id": "5556013e89ad2a030066f6e0", "type": "witnesses", "attributes": { "lname": "Gandee", "fname": "Todd" }, "links": { "self": "/api/witnesses/5556013e89ad2a030066f6e0" }, "relationships": { "sightings": { "data": [], "links": { "self": "/api/witnesses/5556013e89ad2a030066f6e0/relationships/sightings" } } } } ] }
With each response, for example, the type
of each object is
expected to be the model name requested, in order to resolve all records for
that model type. Also, an id
is expected to be
the primary key for each individual model object.
Begin by generating an application adapter:
ember g adapter application
Your application will be making requests to the Big Nerd Ranch
Tracker API. As we have said, the JSONAPIAdapter
property
values require a host URL and a
namespace, which is added to the end of the host when making an Ajax request for model data.
Open app/adapters/application.js and declare the host and namespace.
import JSONAPIAdapter from 'ember-data/adapters/json-api'; export default DS.JSONAPIAdapter.extend({ host: 'https://bnr-tracker-api.herokuapp.com', namespace: 'api' });
Like other Ember classes, naming patterns also apply to adapters – and adapters can be created to customize any model’s API needs. Irregularities in the data structure on the server can be contained in a single adapter rather than forcing all models to conform to edge cases.
What you have added to app/adapters/application.js is a global setting for all data requests. This is all you need for Tracker, because the API is sending all JSON responses from the same host and namespace. But if the witness model, for example, needed a different namespace or host, you could create an app/adapters/witness.js file and configure that particular adapter for witness requests.
Next, you need to retrieve your data from the API via the
store
. The API has cryptids and witnesses ready
to go.
Open app/routes/witnesses.js. Delete the dummy data and replace it with a call to the retrieval method you learned in Chapter 21.
... model(){let witnessRecord = this.store.createRecord('witness', {fname: "Todd",lname: "Gandee",email: "[email protected]"});return [witnessRecord];return this.store.findAll('witness'); } });
Now, restart your application from the terminal with ember server
and point your browser to http://localhost:4200/witnesses (Figure 22.3).
Like most lifecycle flows in Ember,
adapters have a number of methods that get your data from the API to the
store
and to your routes, controllers, and
templates. The purpose of a specific adapter, like the
JSONAPIAdapter
, is to handle a broad pattern
and deal with an expected input/output for
each model. Our example uses a Node.js
server backed by a MongoDB
database using a json-api
node
module to have
API endpoints that work with JSONAPI
-spec’d data.
Working with an API that has a specific pattern to follow (with minor edge cases of irregularity) makes for a happy programmer. You also might need to handle some extra data in your request, like authentication or request headers. Adapters can be customized to deal with any scenario.
Ember previously had built-in adapters for
other data source scenarios, like localStorage
and fixture data. These adapters, and others, are now addons. They
can be added via Ember CLI
when your model data needs to sync with other sources.
If you find that you have to hack together an adapter, dive into the documentation to find your answers. Some methods and properties of note are: ajaxOptions, ajaxError, handleResponse, and headers.
At this point, your app is making requests to a server to receive a collection of witnesses. Before you move on to reading about the content security policy, serializers, and transforms, you will use the same method, this.store.findAll, to retrieve all the cryptid records from the API. Because there is no view template for cryptids, you will examine the returned records with the Ember Inspector.
Add the method call to app/routes/cryptids.js:
import Ember from 'ember'; export default Ember.Route.extend({ model(){ return this.store.findAll('cryptid'); } });
Now that you have the data returning, reload the app in the browser and navigate to http://localhost:4200/cryptids. Use the Ember Inspector to examine the returning data: In the DevTools, select the Ember tab and click Data, then cryptid(4) (Figure 22.4).
Up to this point, there has not been a need to use the
Ember Inspector in depth. It is
used here to show you how to examine in-memory data.
Ember retrieves the data
from the API and populates the store
with
the correct model data. When debugging issues with
model data, tracing the path of the request can be tedious.
The Ember Inspector should be
the first place to look when you have this problem.
3.145.17.18