I'm building an adapter to wrap the Keen.io API, so far I've been able to successfully load the projects resource, however the returned object looks like this:
{
partners_url: "/3.0/projects/<ID>/partners",
name: "Project Name",
url: "/3.0/projects/<ID>",
saved_queries: [ ],
events_url: "/3.0/projects/<ID>/events",
id: "<ID>",
organization_id: "<ORG ID>",
queries_url: "/3.0/projects/<ID>/queries",
api_key: "<API KEY>",
events: [
{
url: "/3.0/projects/<ID>/events/user_signup",
name: "user_signup"
},
{
url: "/3.0/projects/<ID>/events/user_converted",
name: "user_converted"
},
{
url: "/3.0/projects/<ID>/events/user_created_project",
name: "user_created_project"
}
]
}
Excluding a lot of cruft, Ember has no trouble mapping the name and id attributes using the RESTSerializer, but if I add an events relation to my Project model it blows up with:
Error while loading route: TypeError: Cannot set property 'store' of undefined
at Ember.Object.extend.modelFor (http://localhost:3000/assets/ember-data.js?body=1:9813:23)
at Ember.Object.extend.recordForId (http://localhost:3000/assets/ember-data.js?body=1:9266:21)
at deserializeRecordId (http://localhost:3000/assets/ember-data.js?body=1:10197:27)
at deserializeRecordIds (http://localhost:3000/assets/ember-data.js?body=1:10211:9)
at http://localhost:3000/assets/ember-data.js?body=1:10177:11
at http://localhost:3000/assets/ember-data.js?body=1:8518:20
at http://localhost:3000/assets/ember.js?body=1:3404:16
at Object.OrderedSet.forEach (http://localhost:3000/assets/ember.js?body=1:3247:10)
at Object.Map.forEach (http://localhost:3000/assets/ember.js?body=1:3402:10)
at Function.Model.reopenClass.eachRelationship (http://localhost:3000/assets/ember-data.js?body=1:8517:42)
From my investigation this seems to be because it can't find the inverse relation to map an Event back to a Project because there's no parent ID.
Is it possible to create a relation in Ember Data to support this? Or is it possible to modify the Serializer to append a projectId to each event before loading?
I'm using Ember 1.5.0-beta.4 and Ember Data 1.0.0-beta.7+canary.f482da04.
Assuming your Project model is setup the following way:
App.Project = DS.Model.extend({
events: DS.hasMany('event');
});
You need to make sure that the JSON from your API is in a certain shape that Ember-Data expects.
{
"project": {
"id": 1,
"events": ["1", "2"],
},
"events": [{
"id": "1",
"name": "foo"
}, {
"id": "2",
"name": "bar"
}]
}
You can, however, implement extractArrayin your model's serializer to transform the JSON from the server into something similar like the above example.
There's a working example and an explanation in the Ember docs.
Related
I'm having some trouble accessing some nested data in one of my Ember models. Below is the JSON response...
{
"fields": {
"header": {
"name": "header",
"type": "text",
"localize": false,
"options": []
},
"body": {
"name": "body",
"type": "textarea",
"localize": false,
"options": []
}
},
"entries": [
{
"header": "header test",
"body": "body test body test body test",
"_mby": "5a395e8430c2ddoc683600766",
"_by": "5a395e8430c2ddoc683600766",
"_modified": 1513709605,
"_created": 1513709605,
"_id": "5a3960253d578doc735798511"
}
],
"total": 1
}
Here is my current serializer
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType)
{
payload = {
entries: payload.entries
};
return this._super(store, primaryModelClass, payload, id, requestType);
}
});
I'm trying to get the Entries in my template. Perhaps I need some help better serializing this with NormalizeResponse? I'm very stumped so please share if you have any ideas!
Thanks!!
The ember-data RESTAdapter expects the JSON to contain keys that correspond to model names and values to be arrays of JSON objects with key-value pairs according to the respective models' attribute-name--attribute-value-type definitions*.
So if you have a model named entry containing header: attr('string'), body: attr('string'), the following JSON should lead to an entry in the store:
{
"entries": [{
"header": "header test",
"body": "body test body test body test",
"id": "5a3960253d578doc735798511"
}],
}
There is a way to convince ember-data to customize the id field name (to accept _id instead of id). The build-in pluralization rules should include the entry->entries pluralization (but I cannot vouch for it).
Any top-level key that does not correspond to a model name should be ignored anyway, so no need to filter it out manually (unless you have models named field or total).
If you then return this.store.findAll('entry') (or query('entry',{/*something*/}) in your route's model hook, you should be able to look over {{#each model as |entry|}} in your controller's template.
(It is good practice to not use model as the model name, but if you want it to be entries, you have to specify that explicitly somewhere, i.e. the controller or the route's setupController method.)
*The point of repeating the name of the model as top-level key in the json response (presumably to an API endpoint of the same name) is that you can include other models if you whish and they will be added to the store as well.
I created a new RestAdapter like so.
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
namespace: 'api',
host: 'http://localhost:8081'
});
if i look at the network calls the api gets called properly and returns this data.
{
"environments": [
{
"Id": 1,
"LocalePath": "C:\\XML_DEPOT",
"Name": "Acceptation 1",
"RemotePath": "D:\\XML_DEPOT",
"DatabaseServerName": "somedata",
"DatabaseName": "somedata",
"Port": 60903
},
{
"Id": 2,
"LocalePath": "bob",
"Name": "Acceptation 2",
"RemotePath": "bob",
"DatabaseServerName": "somedata\\somedata",
"DatabaseName": "somedata",
"Port": 60904
}
]
}
and then i get an error saying that
Assertion Failed: normalizeResponse must return a valid JSON API
document
but from what i understand the adapter is not a JSONAPIAdapter so it shouldnt try to serialize it has a jsonapi ??
Ember Data is using JSON Api spec internally since version 1.13 as described in release notes. normalizeResponse method of DS.RestAdapter "is used to normalize a payload from the server to a JSON-API Document."
Your payload does not look like what ember data RestAdapater excepts as default. RestAdapter expects camelCase keys but yours are CamelCase. You have to customize your serializer keyForAttribute method to get it working.
I'm using Ember.js with ember-data to make a GUI for a self made API.
I've been following this tutorial to handle authentication but I want to use ember-data instead of custom jQuery requests.
One thing I have to do is to call the API to create a new session, by sending email and password, and the API sends me back an API Key object.
Here is my LoginController handling the loginUser action (it's CoffeeScript) :
App.LoginController = Ember.ObjectController.extend
actions:
loginUser: ->
session = #store.createRecord 'session',
email: #get 'email'
password: #get 'password'
session.save()
Here is the result I get when creating a session:
{
"users": [
{
"id": "525fa0286c696c0b14040000",
"email": "john.doe#mydomain.com",
"first_name": "John",
"surname": "Doe"
}
],
"api_key": {
"id": "526e464c6c696c07d2000000",
"type": "session",
"key": "6b824d6a-a065-4b6f-bb28-5c19389760f8",
"expires_at": "2013-10-28T11:41:08+00:00",
"user_id": "525fa0286c696c0b14040000"
}
}
I have Session, ApiKey and User models. I can create the session, but the thing I don't understand is how to access the return value of the save() method.
I know that my ApiKey and User are loaded somewhere because I get an error after save() if their respective Ember model don't exist but I don't know how to access them.
I've tried to use save() callbacks like then() or didCreate event but there's a lack of documentation about arguments passed to these callbacks and how to use them.
Ember.js 1.1.2
Ember Data 1.0.0.beta.3
EDIT:
I've tried to create an actuel Session model on my API, resulting in this JSON output:
{
"api_keys": [
{
"id": "526f69526c696c07d2110000",
"type": "session",
"key": "4c26af37-2b21-49c2-aef5-5850a396da0b",
"expires_at": "2013-10-29T08:22:50+00:00",
"user_id": "525fa0286c696c0b14040000"
}
],
"users": [
{
"id": "525fa0286c696c0b14040000",
"email": "john.doe#coreye.fr",
"first_name": "John",
"surname": "Doe"
}
],
"session": {
"id": "526f6e666c696c18c0010000",
"api_key_id": "526f69526c696c07d2110000"
}
}
(note the root element is now session)
It doesn't work better because now my save action leads to the following error (not in the console but then points to error callback):
Object function () { [...] } has no method 'eachTransformedAttribute'
I get this error, the relation between Session and ApiKey being declared in Ember Data models or not...
Your second example JSON looks better: since you are saving a Session, I would expect a session node in the response and other models to be side loaded. You can access the saved session after it's saved by using a promise callback:
session.save().then (savedSession) =>
console.log savedSession.api_key.key
Since you have _id relationship keys in your JSON, I assume you are using the ActiveModel adapter and its default serializer:
App.ApplicationAdapter = DS.ActiveModelAdapter.extend()
Here,I am trying to implement CRUD operations using ember-model.
I am totally new to ember environment,actually i don't have much understanding of ember-model.
Here,i am trying to add new product and delete existing one.I am using inner node of fixture
i.e. cart_items.My this fixture contains two node i.e. logged_in and cart_items and this what my fixture structure :
Astcart.Application.adapter = Ember.FixtureAdapter.create();
Astcart.Application.FIXTURES = [
{
"logged_in": {
"logged": true,
"username": "sachin",
"account_id": "4214"
},
"cart_items": [
{
"id": "1",
"name": "Samsung Galaxy Tab 2",
"qty": "1",
"price": "100",
"subtotal": "100"
},
{
"id": "2",
"name": "Samsung Galaxy Tab 2",
"qty": "1",
"price": "100",
"subtotal": "100"
},
{
"id": "3",
"name": "Samsung Galaxy Tab 2",
"qty": "1",
"price": "100",
"subtotal": "100"
}
]
}
];
I want to this fixture struture only to get data in one service call from server.
Now,here is my code which i am using to add and delete product from cart_items
Astcart.IndexRoute = Ember.Route.extend({
model: function() {
return Astcart.Application.find();
}
});
Astcart.IndexController = Ember.ArrayController.extend({
save: function(){
this.get('model').map(function(application) {
var new_cart_item = application.get('cart_items').create({name: this.get('newProductDesc'),qty: this.get('newProductQty'),price: this.get('newProductPrice'),subtotal: this.get('newProductSubtotal')});
new_cart_item.save();
});
},
deleteproduct: function(product){
if (window.confirm("Are you sure you want to delete this record?")) {
this.get('model').map(function(application) {
application.get('cart_items').deleteRecord(product);
});
}
}
});
But when i am trying to save product i am getting an exception
Uncaught TypeError: Object [object global] has no method 'get'
And when i am trying to delete product i am getting an exception
Uncaught TypeError: Object [object Object] has no method 'deleteRecord'
Here,i also want to implement one functionality i.e. on every save i need to check if that product is already present or not.
If product is not present then only save new product other wise update existing product.
But i don't have any idea how to do this?
I have posted my complete code here.
Can anyone help me to make this jsfiddle work?
Update
I have updated my code here with debugs.
Here, i am not getting any exception but record is also not getting delete.
I am not getting what is happening here?
Can anyone help me to make this jsfiddle work?
'this' context changes within your save method. You need to use the 'this' of the controller and not the map functions. Try this:
save: function(){
var self = this;
self.get('model').map(function(application) {
var new_cart_item = application.get('cart_items').create({
name: self.get('newProductDesc'),
qty: self.get('newProductQty'),
price: self.get('newProductPrice'),
subtotal: self.get('newProductSubtotal')
});
new_cart_item.save();
});
}
Working on an App with Ember RC6 and Ember-Data v0.13-54 along a custom server PHP API am running into some problems with Models relations (mainly many-to-many) and side loading.
The models in questions are:
App.Member = DS.Model.extend({
apiToken: DS.attr('string'),
apiTokenExpire: DS.attr('string'),
favourites: DS.hasMany('App.Presentation')
});
App.Presentation = DS.Model.extend(
{
title: DS.attr('string'),
description: DS.attr('string'),
date: DS.attr('date'),
category: DS.belongsTo('App.Category'),
tags: DS.hasMany('App.Tag'),
employees: DS.hasMany('App.Member'),
presentation: DS.belongsTo('App.File'),
presenterNotes: DS.belongsTo('App.File'),
cover: DS.belongsTo('App.Image')
});
To get the RESTAdapater to send the relation with the Member model I have:
App.APIRESTAdapter.map('App.Member', {
favourites: {embedded: 'always'}
});
When loading /members/1 the server returns:
{
"member": {
"api_token": "9fa236ad58726584d7b61bcf94b4dda37cbd3a24",
"api_token_expire": "1383832335",
"id": 1,
"favourite_ids": [ 3 ],
"group_ids": [ 2 ]
},
"presentations": [
{
"title": "That's a new one!",
"category_id": 2,
"id": 3,
"tag_ids": [ 1 ],
"employee_ids": [ 1 ]
}
]
}
But if I log get('member.favourites').mapProperty('id') I get an empty array and none of the favourites are actually added to the Member model.
If I disable the embedding of favourites on the RESTAdapter, all works fine. I am just wondering if there is something that I am missing or if there is some issues with how the JSON response is formatted?
EDIT:
Done some digging and seems that the issue comes from the fact that the relation names (favourites, employees) is different from the model names (Member, Presentation) which are used when sideloading data. Weird, since rev. 12 https://github.com/emberjs/data/blob/master/BREAKING_CHANGES.md models should be Sideload by Type.
Doing some tests I've added a new many-to-many relation for those 2 models:
App.Member gets presentations: DS.hasMany('App.Presentation')
App.Presentation get members: DS.hasMany('App.Member')
The JSON returned by the server is exactly the same, and when logging get('member.presentations') I now get the list of presentations as it should.
At this point this looks like a bug to me, but maybe am missing something? I've tried mappings on the RESTAdapater for favourites and employees but that didn't help... Maybe there is some other Adapter config that could help?
This isn't a sideloading issue but more a misunderstanding on my side about embedded data and what the configuration meant.
Since the Adapter was configured with:
App.APIRESTAdapter.map('App.Member', {
favourites: {embedded: 'always'}
});
The expected JSON response from the server is:
{
"member": {
"api_token": "b84fd204b37d3fa3cee8a2b2cac8bd4fd02635a1",
"api_token_expire": "1384027367",
"id": 1,
"favourites": [
{
"title": "Some kind of title",
"category_id": 1,
"id": 2,
"tag_ids": [ 1 , 2 ],
"employee_ids": [ 1 ]
}
]
}
}
So "favourite_ids": [ X, X, X ] should have been "favourites": [ HASH, HASH, HASH ] when records are flagged as embedded.