I tried a simple emberapp to display some userdata.
My API returns me this json:
{
"roles":[
{
"id":5,
"name":"admin",
"alias":"Administrator",
"users":[
{
"id":1,
"username":"Evolutio",
"email":"mail#evolutio.tld",
"display_role":"Administrator"
}
]
},
{
"id":2,
"name":"user",
"alias":"Benutzer",
"users":[
]
},
{
"id":1,
"name":"banned",
"alias":"Gesperrt",
"users":[
]
},
{
"id":3,
"name":"mod",
"alias":"Moderator",
"users":[
]
},
{
"id":4,
"name":"support",
"alias":"Supporter",
"users":[
]
}
]
}
my user/model.js:
import DS from 'ember-data';
export default DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string'),
display_role: DS.attr('string'),
roles: DS.belongsTo('role'),
});
and my role/model.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
alias: DS.attr('string'),
users: DS.hasMany('user'),
});
With this setup I got this error in my developer console:
Error while processing route: team.index Assertion Failed: Passing classes to store methods has been removed. Please pass a dasherized string instead of undefined EmberError#
I didn't get the mistake. Maybe anyone can help me for this.
You would need to sideload the data and make your API return data like this:
{
"users": [{
"id": 1,
"username": "Evolutio",
"email": "mail#evolutio.tld",
"display_role": "Administrator",
"roles": [5]
}],
"roles": [{
"id": 5,
"name": "admin",
"alias": "Administrator",
"users": [1]
}, {
"id": 2,
"name": "user",
"alias": "Benutzer",
"users": []
}, {
"id": 1,
"name": "banned",
"alias": "Gesperrt",
"users": []
}, {
"id": 3,
"name": "mod",
"alias": "Moderator",
"users": []
}, {
"id": 4,
"name": "support",
"alias": "Supporter",
"users": []
}]
}
If you must embed the data like you did, you might want to look into EmbeddedRecordsMixin[http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html]
But I would like to suggest few things:
1) follow json-api(http://jsonapi.org/) since ember data follows it and supports it out of the box( starting from ver 1.13.0 I think)
2) Take it or throw it idea - I don't know your requirement but ideally user model would have hasMany relationship to roles. So I would do things bit differently like this:
//user/model.js:
import DS from 'ember-data';
export default DS.Model.extend({
username: DS.attr('string'),
email: DS.attr('string'),
//display_role: DS.attr('string'),//you don't need this, since user can have multiple roles and also you can access it from the user model itself by doing model.roles.forEach((role)=>{ role.alias or role.name})
roles: DS.belongsTo('role')
});
//role/model.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
alias: DS.attr('string')
});
because generally there will be fewer roles than users and also you wouldn't want to load all that data(roles and users) in one request call.
Related
I'm trying to use ember data with REST API from server. The project is quiet simple and works fine with Fetch API, but i would like to implement the same result using Ember data and understand it's principles. I could not use ember twiddle with this configuration. Below will be the main parts of the project. It was made by Ember CLI version 4.0.1.
The JSON responce from url http://localhost:3000/api/v1/items looks like:
[
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
},
{
"id": 2,
"name": "Item 2",
"description": "22222",
"created_at": "2022-01-07T11:43:29.787Z",
"updated_at": "2022-01-07T11:43:29.787Z"
},
{
"id": 3,
"name": "Item 3",
"description": "33333",
"created_at": "2022-01-07T11:43:37.885Z",
"updated_at": "2022-01-07T11:43:37.885Z"
}
]
The responce from a single item request such as http://localhost:3000/api/v1/items/1:
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
}
app/models/item.js:
import Model, { attr } from '#ember-data/model';
export default class ItemModel extends Model {
#attr name;
#attr description;
}
app/routes/items.js:
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default class ItemsRoute extends Route {
#service store;
async model() {
return this.store.findAll('item');
}
}
app/routes/item.js:
import Route from '#ember/routing/route';
import { inject as service } from '#ember/service';
export default class ItemRoute extends Route {
#service store;
async model(params) {
return this.store.findRecord('item', params.id);
}
}
app/templates/items.hbs:
{{#each #model as |item|}}
<LinkTo #route='item' #model={{item}}>{{item.name}}</LinkTo>
{{/each}}
app/adapters/application.js:
import RESTAdapter from '#ember-data/adapter/rest';
export default class ItemAdapter extends RESTAdapter {
host = 'http://localhost:3000';
namespace = 'api/v1';
buildURL(...args) {
return `${super.buildURL(...args)}.json`;
}
}
app/serializers/application.js:
import RESTSerializer from '#ember-data/serializer/rest';
export default class ItemSerializer extends RESTSerializer {}
app/router.js:
import EmberRouter from '#ember/routing/router';
import config from 'dist/config/environment';
export default class Router extends EmberRouter {
location = config.locationType;
rootURL = config.rootURL;
}
Router.map(function () {
this.route('items');
this.route('item', { path: '/:id' });
});
When I move to route /items on the ember client, the following error appears in the console:
WARNING: Encountered "0" in payload, but no model was found for model
name "0" (resolved model name using
<dist#serializer:item::constructor>.modelNameFromPayloadKey("0"))
And the same errors for "1" and "2". The template doesn't render links in the each loop.
Probably, there are some features in Ember data with REST API, and I would really like to figure it out. Thanks for attention!
I believe you have this issue because your data is not in the RESTSerializer format but rather in the JSONSerializer one:
the JSONSerializer expects the response to be a JSON object that looks
similar to this
{
"id": "1",
"title": "Rails is omakase",
"tag": "rails",
"comments": ["1", "2"]
}
So i guess you should just use this one.
If for any reason you wanna keep the RESTSerializer you should return your data similar to this format:
{
"item":
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
}
}
or for a list
{
"items": [
{
"id": 1,
"name": "Item 1",
"description": "12345",
"created_at": "2022-01-07T11:43:21.755Z",
"updated_at": "2022-01-07T11:43:21.755Z"
},
...
]
Ember Data 2.1.0 ignores the links property in my JSON response and fires the following request instead:
/point_logs/3e5ff053422f40e3a8057fc5e8100c47
Also for each point log found it fires a single request. Instead I want it to fetch a collection of all point logs.
In Ember 1.x everything worked fine though. Looked everywhere for an answer but unfortunately I was unable to find a solution so far.
Appliance Model
var DirectObject = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
type: DS.attr('string'),
createdDate: DS.attr('date'),
modifiedDate: DS.attr('date'),
deletedDate: DS.attr('date'),
pointLog: DS.belongsTo('pointLog')
});
Pointlog Model
var PointLog = DS.Model.extend({
unit: DS.attr('string'),
type: DS.attr('string'),
lastConsecutiveLogDate: DS.attr('date'),
updatedDate: DS.attr('date'),
directObject: DS.belongsTo('directObject')
});
JSON Response
{
"data": [
{
"id": "6dbcf32a3e064a36a1db4847329cc90d",
"type": "appliance",
"attributes": {
"name": "3974737",
"description": "",
"type": "zz_misc",
"createdDate": "2015-09-15T14:23:02.768Z",
"modifiedDate": "2015-10-08T08:39:15.525Z",
"deletedDate": null
},
"relationships": {
"pointLog": {
"data": {
"id": "3e5ff053422f40e3a8057fc5e8100c47",
"type": "pointLog"
},
"links": {
"related": "/core/appliances/6dbcf32a3e064a36a1db4847329cc90d/point_log"
}
}
}
}
],
"included": [],
"links": {
"self": "/core/appliances"
}
}
I've also tried using the self property instead of related. And also related as an object with an href property inside being the url.
Ok so I figured it out. The problem is the data property. It seems that if it is present, Ember will make individual calls without using the url given in the links object.
Before
"relationships": {
"pointLog": {
"data": {
"id": "3e5ff053422f40e3a8057fc5e8100c47",
"type": "pointLog"
},
"links": {
"related": "/core/appliances/6dbcf32a3e064a36a1db4847329cc90d/point_log"
}
}
}
After
"relationships": {
"pointLog": {
"links": {
"related": "/core/appliances/6dbcf32a3e064a36a1db4847329cc90d/point_log"
}
}
}
I have this error when try call rest api : Encountered "companies" in payload, but no model was found for model name "company"
http://localhost:9000/api/insurance/travel/companies
Call example :
var companies = this.store.find("insurance/travel/company");
Model :
export default DS.Model.extend({
name: DS.attr('string'),
url: DS.attr('string'),
logoUrl: DS.attr('string'),
contactUrl: DS.attr('string'),
moreUrl: DS.attr('string'),
about: DS.attr('string')
})
JSON Example :
{
"companies": [
{
"id": "id1",
"name": "test",
"url": "http://google.com",
"logoUrl": "http://google.com",
"contactUrl": "http://google.com",
"moreUrl": "http://google.com",
"about": "about text"
},
{
"id": "id2",
"name": "test 2",
"url": "http://google.com",
"logoUrl": "http://google.com",
"contactUrl": "http://google.com",
"moreUrl": "http://google.com",
"about": "about text"
}
]
}
Create an adapter and inject a inflector
import DS from 'ember-data';
import Ember from 'ember';
var inflector = Ember.Inflector.inflector;
inflector.uncountable('company');
export default DS.RESTAdapter.extend({});
It worked for me
I've recently started learning Ember, and am now trying to understand how to use Ember Data to make an ajax call for data. I would like to get data and log the response to the console when I navigate to the pages'route.
Here is the code I have but cannot get it to log the data:
Handlebars and JS:
<script type="text/x-handlebars">
<nav>
{{#link-to 'home'}}Home{{/link-to}}
{{#link-to 'pages'}}Pages{{/link-to}}
</nav>
<div class="container">
{{outlet}}
</div>
</script>
<script type="text/x-handlebars" id="pages">
Info here
{{log}}
</script>
App = Ember.Application.create();
App.Router.map(function() {
this.route('home', {path: '/'});
this.route('home', {path: 'home'});
this.route('pages', {path: 'pages'});
});
App.Pages = DS.Model.extend({
status: DS.attr('string'),
startDate: DS.attr('string'),
lastModifiedDate: DS.attr('string'),
author: DS.attr('string'),
lastModifiedBy: DS.attr('string'),
title: DS.attr('string'),
items: {}
});
App.PagesAdapter = App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'https://api.mongolab.com/api/1/databases/emberdatatest/collections/itemdata?apiKey=somekey'
});
App.PagesRoute = Ember.Route.extend({
model: function() {
this.store.find('pages');
}
});
Here is the data I cam getting back as a response:
{
"_id": {
"$oid": "5460cc6be4b0933065794003"
},
"start": 0,
"count": 5,
"total": 1549,
"pages": [
{
"status": "published",
"startDate": "2014-09-24 12:56",
"lastModifiedDate": "2014-10-02 12:01",
"author": "Luke Skywalker",
"lastModifiedBy": "Darth Vader",
"title": "home page",
"items": {}
},
{
"status": "published",
"startDate": "2014-10-13 08:03",
"lastModifiedDate": "2014-10-02 12:01",
"author": "Sauran",
"lastModifiedBy": "Gandolf",
"title": "Blog page",
"items": {}
},
{
"status": "review",
"startDate": "2014-11-22 13:03",
"lastModifiedDate": "2014-11-14 14:01",
"author": "Jean-Luc Piccard",
"lastModifiedBy": "Worf",
"title": "Vacuum Cleaners page",
"items": {}
}
]
}
Instead of
this.store.find('pages');
you should call
this.store.find('page');
it's going to make request to
https://api.mongolab.com/api/1/databases/emberdatatest/collections/itemdata?apiKey=somekey/pages
Then the api should give you back response in following format (in case of using Rest adapter)
"posts": [{
"id": 1,
"status": "foo",
"startDate": "foo",
"lastModifiedDate": "foo",
"author": "foo",
"lastModifiedBy": "foo",
"title": "foo"
}]
Inside your router model method you should return the results from anonymous function
App.PagesRoute = Ember.Route.extend({
model: function() {
return this.store.find('pages');
}
});
Then you can check in ember inspector whether everything was all right.
If you can't force your api to return expected format you can do it without ember data
App.PagesRoute = Ember.Route.extend({
model: function() {
return jQuery.getJSON('https://api.mongolab.com/api/1/databases/emberdatatest/collections/itemdata?apiKey=somekey/', function(json) {
return json
});
}
});
If you want to use ember data I advise you to look here:
http://emberjs.com/guides/models/finding-records/
http://emberjs.com/guides/models/connecting-to-an-http-server/
http://andycrum.github.io/ember-data-model-maker/
I'm using ember rc3 and ember-data 12 (sha e324f0e) (basically the files recommended in the guides). I have 2 models set up as follows:
App.User = DS.Model.extend({
username: DS.attr('string'),
playerType: DS.attr('string'),
cars: DS.hasMany('App.Car')
})
App.Car = DS.Model.extend({
name: DS.attr('string'),
thumb: DS.attr('string'),
user: DS.belongsTo('App.User')
})
The json returned is
{
"cars": [
{
"id": "50ace47234fa7557403e7f02",
"name": "Dodge Charger SRT8",
"thumb": "/static/images/carthumbs/18331.png",
"user_id": "502a754b34fa75280c000a7e"
},
{
"id": "508668cc34fa753b78784ca2",
"name": "BMW M3 Coup\u00e9",
"thumb": "/static/images/carthumbs/23250.png",
"user_id": "502a754b34fa75280c000a7e"
},
{
"id": "50c7545334fa750ab8cb3ac2",
"name": "BMW Z4 M Coup\u00e9",
"thumb": "/static/images/carthumbs/7618.png",
"user_id": "502a754b34fa75280c000a7e"
},
{
"id": "50adf64c34fa750bb036121e",
"name": "2013 Ford Shelby GT500\u2122",
"thumb": "/static/images/carthumbs/24824.png",
"user_id": "502a754b34fa75280c000a7e"
}
],
"user": {
"id": "502a754b34fa75280c000a7e",
"car_ids": [
"50ace47234fa7557403e7f02",
"508668cc34fa753b78784ca2",
"50c7545334fa750ab8cb3ac2",
"50adf64c34fa750bb036121e"
],
"player_type": "Standard Player",
"username": "WillMckenzie"
}
}
Everything seems to load fine if I call App.User.find("502a754b34fa75280c000a7e"), but when I try and access the cars property on the user it triggers a second http request to the cars api route. It was my understanding that this shouldn't be necessary, and if I change the ids to basic ints, it doesn't. As I'm using Mongo as my DB my ids have to be in this string format.
Any suggestions as to what I'm doing wrong?
Cheers
Will
Here's the answer so people don't have to dig through the comments:
"I had one car id listed that wasn't in the list of cars returned. They're grabbed slightly differently and I obviously had a bad record in there. This meant it always thought it needed to reload that record so would keep requesting. Obviously when I was faking the integer ids it was masking this." - OiNutter