21
Models and Data Binding

For the next part of the Tracker app, you will focus only on the data layer.

You have already worked quite a bit with data in the form of object literals. You have created and modified objects and their properties, and you have created functions to quickly make objects with default values. You know how to store data in localStorage and sessionStorage.

In Tracker, you are going to work with data in the form of models. Models are essentially functions that create objects with specific properties and methods. They are the architecture of data flowing through your application.

Ember has an object class that can take care of your initial need to define your app’s data architecture: Ember.Object. All Ember classes inherit from this class. With a simple definition and naming pattern, Ember gives you the power to create, retrieve, update, and destroy model instances while the app is running.

However, for your modern application you need more than what Ember.Object provides. Your models need to be able to persist themselves when business logic asks them to retrieve or save data from a data source.

Enter Ember Data, a JavaScript library built on top of Ember.Object, which will help you add model-specific functionality. Ember Data adds classes built on Ember.Object that abstract the complexity of working with various data sources: RESTful APIs, localStorage, and even static fixture data.

Ember Data also adds an in-memory store for data. The data store is where you create, retrieve, update, and delete your model instances.

Model Definitions

Ember CLI has already loaded the Ember Data library, so you are ready to build your models.

In the last chapter, you used ember g route [route name] to make Ember CLI create route files for you. You can also use ember generate to create a model file with the command ember g model [model name].

Create the model files you will need to back your routes for cryptids, sightings, and witnesses:

ember g model cryptid
ember g model sighting
ember g model witness

Cryptids will have a model definition in the file app/models/cryptid.js. Open this file and add attributes for name, cryptid type (species), profile image, and sightings to the cryptid model:

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  cryptidType: DS.attr('string'),
  profileImg: DS.attr('string'),
  sightings: DS.hasMany('sighting')

});

Ember Data, referenced here as DS (for “data store”), has an attr method that is used to specify model attributes. When data is parsed from the source, attr returns the value. If you give attr an attribute type, it will be coerced to that type. If you do not set the attribute type, your data will be passed through to the appropriate key unchanged.

There are a few attribute types built in: string, number, boolean, and date. You can also create custom model attributes using transforms, which you will learn about in Chapter 22.

attr can also take a second argument to specify default values. This optional argument is a hash with a single key: defaultValue. Here are some examples:

name: DS.attr('string', {defaultValue: 'Bob'}),
isNew: DS.attr('boolean', {defaultValue: true}),
createdAt: DS.attr('date', {defaultValue: new Date()}),
numOfChildren: DS.attr('number', {defaultValue: 1})

In the cryptid definition, you used the string attribute type for the name, cryptidType, and profileImg attributes. (Why a string type for profileImg? It will reference the image path, not the image itself.)

The sightings attribute uses a different method to define its data: hasMany. This method is part of Ember Data’s relationship methods. When you query a RESTful API for a cryptid, it will have associated sightings. That association will be returned as an array of sighting ids referencing an instance of a sighting model.

Ember Data has methods to handle one-to-one, one-to-many, and many-to-many relationship types:

Relationship “Owning” model “Owned” model
one-to-one DS.hasOne DS.belongsTo
one-to-many DS.hasMany DS.belongsTo
many-to-many DS.hasMany DS.hasMany

The first argument is the model to associate. In your app, a cryptid will have many sighting instances (you would be surprised how often people see these creatures). The second argument is an optional hash which, similar to attr’s second argument, is a configuration object to set values when evaluating the function. It contains an async key and a value (with a default of true).

Model relationships could require requests to a server to retrieve other model data. For cryptids, a request to sightings is needed to display sighting data for each cryptid. The same is true for the inverse relationship of sightings belonging to a cryptid. The default value, async: true, requires a separate request and API endpoint to retrieve the linked data.

If your API has the ability to send all the data together, you can set the async value to false. For the Tracker app, leave the value as the default, true.

Next, open the model for witnesses in app/models/witness.js and add attributes for a witness’s first and last name, email address, and recorded sightings:

import DS from 'ember-data';

export default DS.Model.extend({
  fName: DS.attr('string'),
  lName: DS.attr('string'),
  email: DS.attr('string'),
  sightings: DS.hasMany('sighting')
});

You defined a witness to be an object that contains a first name (fName), a last name (lName), an email address (email), and a many-to-many relationship to sightings (sightings).

Finally, open your third model file: app/models/sighting.js. Add attributes to your sightings model for the who, what, where, and when of the sighting as well as the date the sighting was recorded:

import DS from 'ember-data';

export default DS.Model.extend({
  location: DS.attr('string'),
  createdAt: DS.attr('date'),
  sightedAt: DS.attr('date'),
  cryptid: DS.belongsTo('cryptid'),
  witnesses: DS.hasMany('witness')
});

Sightings are defined much like witnesses and cryptids, with basic properties defined as strings. The location is a value the user will input in the app, while createdAt and sightedAt will be added server-side when the sighting has been added to the database.

The relationship for the property cryptid is something new, DS.belongsTo('cryptid'). This method is a one-to-many relationship linking a cryptid instance to the sighting instance – one-to-many because each cryptid will have many sightings.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.10.1