Using ember-data to populate a table - ember.js

I'm relatively new to Ember CLI and have just begun to use ember-data to retrieve JSON data from my Laravel Eloquent-powered RESTful API.
I would like to populate a DataTables table with the data returned from the "Accounts" API. Each "Account" also has an embedded "Contact" record available under the "contact" key in the JSON returned from the server. The JSON is correct and an example of an "Account" record coming back from the API is:
{
"accounts": {
"id": 1,
"account_name": "My Account",
"contact": {
"id": 54,
"first_name": "John",
"last_name": "Smith"
}
}
}
In my controller I have called the serialize() method on each "Account" DS.Model in the RecordArray to convert it to a JSON object because this should allow relationships to be serialized too. However, even though I am using the ActiveModelSerializer with the EmbeddedRecordsMixin the relationships in the resulting JSON appear like this:
"contact": {
"id": "54"
}
So only the "id" field is present in the serialized, embedded "Contact" object.
My "account.js" serializer looks like this:
import DS from "ember-data";
export default DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
contact: {embedded: 'always'},
},
});
and my "account.js" model looks like this:
import DS from 'ember-data';
export default DS.Model.extend({
contact: DS.belongsTo('contact', {embedded: 'always'}),
});
I cannot understand what I am doing wrong and I'd be grateful of any help anyone could offer.
Thanks.

Related

Reflexive relation with nested data

I'm sorry if this is a basic question, but since I'm quite new to ember, I'd like to know if there is any best practice for a case like this. For example, I have the follow endpoints that returns the payloads below:
https://api.example.com/v1/user
[
{
"user": "user1",
"firstName": "Foo1",
"lastName": "Bar1",
"url": "https://api.example.com/v1/user/user1"
},
{
"user": "user2",
"firstName": "Foo2",
"lastName": "Bar2",
"url": "https://api.example.com/v1/user/user2"
}
]
And each of the "url" endpoint returns something like this:
https://api.example.com/v1/user/user1
{
"user": "user1",
"firstName": "Foo1",
"lastName": "Bar1",
"age": 21,
"address": "User1 Address"
... more info ...
}
We see that some properties in "/user" are repeated in "/user/user1".
What would be the best practice to create the "user" model?
Should I have two models? Like for example a "users" model for the "/user" and a "user" model for "/user/user1"?
Could somehow have just one model "user" that would fit both endpoints?
Thanks in advance!
This is almost the use case described in the one-to-one docs where you're defining the user data with one model and linking another model with a belongsTo attribute:
// app/models/user.js
import DS from 'ember-data';
export default DS.Model.extend({
user: DS.attr('string'),
firstName: DS.attr('string'),
lastName: DS.attr('string'),
url: DS.attr('string'),
profile: DS.belongsTo('profile')
});
then setup a profile model with any extra values you're wanting to add and define the belongsTo attribute also:
// app/models/profile.js
import DS from 'ember-data';
export default DS.Model.extend({
age: DS.attr('string'),
address: DS.attr('string'),
user: DS.belongsTo('user')
});
In your routes file you'll want to setup the user id to define your URL structure like so:
//app/router.js
Router.map(function() {
this.route('users');
this.route('user', { path: '/user/:user_id' });
});
Then finally you'll need to load the data retrieving the related records and loading them in via your route file.
// app/routes/user.js
import Route from '#ember/routing/route';
export default Route.extend({
model(params) {
return this.store.findRecord('user', params.user_id, {include: 'profile'});
}
});
It's worth pointing out that you may also need a serializer to massage the data into the format you're wanting.

Ember Data relationships not resolved

I'm still learning ember.js and have run into a roadblock with ember data not resolving lookup relationships in models. I have one model 'site' that will be basically a lookup table for every other model to differentiate data based on location.
At this point, I'm doing something wrong or missing a key concept - probably both... (or maybe it's the wee hour!)
Site Model (i.e. the lookup table)
import DS from 'ember-data';
export default DS.Model.extend({
code: DS.attr(),
name: DS.attr(),
});
The site model would have a hasMany relationship to all my other models (will be about 12 when complete)
Associate Model
import DS from 'ember-data';
import { belongsTo } from 'ember-data/relationships';
export default DS.Model.extend({
site: belongsTo('site'),
last: DS.attr(),
first: DS.attr(),
active: DS.attr('boolean'),
fullName: Ember.computed('first', 'last', function() {
return `${this.get('first')} ${this.get('last')}`;
}),
});
The 'associate model' will also be a lookup along with 'site' in some other models.
I'm providing data via the JSON API spec but I'm not including the relationship data because as I understand it, ember data it should be pulling down the site data using the site id attribute.
{
"links": {
"self": "/maint/associates"
},
"data": [
{
"type": "associate",
"id": "1",
"attributes": {
"site": "6",
"last": "Yoder",
"first": "Steven",
"active": "1"
},
"links": {
"self": "/associates/1"
}
}
]
}
In my template file I'm referencing associate.site which gives me an error.
<(unknown mixin):ember431>
If I use associate.code or .name to match the site model, nothing will show in the template. The code from the 'site' table is the data I really want to displayed in the template.
So the obvious questions:
Am I wrong that Ember Data should be resolving this or do I need to
include the relationship in my API response?
I realize that my belongsTo in the 'associate' model only references
site while I want site.code, so how do I make that relationship
known or access the field in my 'associate' model?
I didn't include hasMany relationship in the 'site' model because
there would be many. Do I need to do an inverse relationship in
other models? Examples I've seen don't all show the hasMany
relationships setup.
When I look at the models in ember inspector the site field is not
included in the model. Even if I wasn't getting the correct data
should it still show up?
I like ember so far, just need to understand and get over this roadblock
Update: My backend JSON library would only generate relationship links based on the current spec which would be
"related": "/streams/1/site"
but ember data does call
"related": "/sites/1"
to resolve the relationship
So #Adam Cooper answer is correct if you generate links as he answered or if you can only generate the links based on the current specification.
If you're using the JSONAPIAdapter, which is the default, you want your response to look this:
{
"links": {
"self": "/maint/associates"
},
"data": [{
"type": "associate",
"id": "1",
"attributes": {
"last": "Yoder",
"first": "Steven",
"active": "1"
},
relationships: {
"site": {
"links": {
related: "/sites/6"
}
}
}
}]
}
That will allow Ember Data to look up the site via its relationship. Right now Ember is trying to access the site model which Ember Data can't populate hence the error you're getting. As an aside you could probably do with returning an actual boolean value for active too.

How to dasherize filter URL sent by Ember query?

I am trying to learn Ember by developing a simple TODO manager application.
I am using ember-data and JSONAPISerializer with a hand rolled REST JSON API backend for this application.
I have the following model which represents a task
app/model/task.js
export default DS.Model.extend({
title: DS.attr ('string'),
description: DS.attr ('string'),
isComplete: DS.attr ('boolean')
});
The corresponding JSON data from backend looks like this
{
"data": [{
"id": "1",
"type": "task",
"attributes": {
"title": "Complete Ember TODO manager application",
"description": "Build a simple Ember application for easily managing tasks",
"is-complete": "false"
}
}]
}
As per the convention, the Ember model uses camel cased names and the JSON API backend uses dasherized names.
A basic feature in this application is to filter tasks by their status, so basically one can see either ongoing or completed or all tasks.
For fetching only the ongoing tasks, I have the following query in the model hook of the corresponding route
app/routes/tasks/ongoing.js
export default Ember.Route.extend({
model () {
return this.get('store').query('task', {
filter: {
isComplete: 'false'
}
});
}
});
So when the query is sent to the backend it goes as
restjsonapi/tasks?filter[isComplete]=false
The problem is that the backend expects "is-completed" and does not understand "isComplete".
Is there a way to dasherize the URL emitted by Ember query?
Edit (possible workaround):
I might have been trying to solve this the wrong way I think. I have changed the JSON data to have underscores instead of dashes and worked around this problem.
When using store.query, Ember then finds the relevant adapter for your model type and calls adapter.query which in turn seems to pass your query to jQuery.ajax as a value for the data attribute. See here:
https://github.com/emberjs/data/blob/v2.7.0/addon/adapters/rest.js#L504
The conversion from Object to Query String is then done by jQuery using encodeURIComponent as you can see there: https://github.com/jquery/jquery/blob/e4fd41f8fa4190fbbb6cb98cf7ace64f6e00685d/src/serialize.js#L68
So you won't be able to change this behaviour by passing an option to Ember.
However, you could try something like:
export default Ember.Route.extend({
model () {
return this.get('store').query('task', {
filter: {
"is-complete": 'false'
}
});
}
});
And it should work.

Converting backend json response to Ember store expected format

I am pretty new to ember and trying to do some POC's to fit our existing applications.
Using ember 1.13.12 and ember data 1.13.15
I have two models,
company.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
employee: DS.hasMany('employee',{async:true}),
});
employee.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
alias: DS.attr('string')
});
The serializers extend the JSONSerializer and sets the primarykey for both models and the option embedded always for employee model.
Backend json response for /employee/7, - this results in error - Ember.A(...) map not a function
{
"name": "abc"
"employee": {
"employeeId": 7,
"name": "employee7",
"alias": "e7"
}
}
Backend json response for /employees - this gets pushed to the ember store without issues
{
"name": "abc"
"employee": [
{
"employeeId": 1,
"name": "employee1",
"alias": "e1"
},
{
"employeeId": 7,
"name": "employee7",
"alias": "e7"
}
]
}
With the above response I faced two issues,
Not having an 'id' for the 'company' model to push to the store (Workaround ended up changing the backend response to have an 'id')
When I try getting a single employee say employee/7/ the json response from backend does not return as an array (Workaround added one of the normalize hook and added a line payload.employee = [payload.employee] and it worked)
Is there a way I can get past the above issues without changing the backend response and workarounds.

Ember not loading embedded polymorphic relationship

I'm using Ember's RESTAdapter and have a pretty standard polymorphic relationship:
// models/order.js
import DS from 'ember-date';
export default DS.Model.extend({
transferableItem: DS.belongsTo('transferable-item', { polymorphic: true })
});
// models/transferable-item.js
import DS from 'ember-date';
export default DS.Model.extend({
order: DS.belongsTo('order')
});
// models/ticket.js
import TransferableItem from './transferable-item';
export default TransferableItem.extend();
My JSON looks like this:
{
"orders": [{
"id": 111,
"transferableItem": 999
"transferableItemType": "Ticket"
}],
"tickets": [{
"id": 999
}]
}
Looking in Ember Inspector, both Orders and Tickets properly load. However, the link between the two of them is broken. I get this error:
You looked up the 'transferableItem' relationship on a 'order' with id
999 but some of the associated records were not loaded. Either make
sure they are all loaded together with the parent record, or specify
that the relationship is async (DS.belongsTo({ async: true }))
According to Ember Inspector, there are no transferable-items loaded, so in a way, this error makes sense. However, since this is a polymorphic relationship, shouldn't it just try to use the associated Ticket, which is in fact loaded?
Kept on digging, and discovered that the syntax needs to change when the record is embedded. The JSON should look like this:
{
"orders": [{
"id": 111,
"transferableItem": {
"id": 999
"type": "ticket"
}
}],
"tickets": [{
"id": 999
}]
}