This has been asked a couple times, but the examples didn't help a whole lot.
I want to post 'posts' to my server, so I have a 'posts' model and then a 'single' model. The 'posts' model represents all the posts, and then my 'single' model represents what each post needs... I am new to Ember.js, and really could use a hand here/direction.
So when I submit the form (for creating a new post):
// When the form is submitted, post it!
actions: {
// createNew begin
createNew() {
var title = this.controller.get('title');
var content = this.controller.get('content');
const data = {
"posts": [
{
"title": title,
"content": content
}
]
};
return this.store.createRecord('posts', data).save().
then(function(post) {
console.log(post);
}, function(error) {
console.log(error);
});
} // end of createNew
}
'posts' model:
import DS from 'ember-data';
export default DS.Model.extend({
posts: DS.hasMany('single'),
});
'single' model:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
content: DS.attr('string'),
});
And then my serializer to hook the two together...
import DS from 'ember-data';
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
posts: { embedded: 'always' }
}
});
Currently, this is the error that outputs:
"Assertion Failed: All elements of a hasMany relationship must be instances of DS.Model, you passed [[object Object]]"
In Short: I need to create data models that can represent the following JSON structure:
{
"posts": [
{ "title": "Title", "content": "Content" }
]
}
Thanks!
The error is actually saying exactly what's wrong.
"Assertion Failed: All elements of a hasMany relationship must be instances of DS.Model, you passed [[object Object]]"
The model posts has a hasMany relationship to the model single.
What your code is doing is passing a plain JS object instead of the model.
const data = {
"posts": [
{ // <-
"title": title, // <-
"content": content // <- this is a POJO
} // <-
]
};
One way to solve this actually is to create the two objects separately.
// create 'posts' and 'single' separately
const posts = this.store.createRecord('posts');
const single = this.store.createRecord('single', {
title,
content
});
// link them up
posts.get('posts').addObject(single);
Related
As I'm going deeper and deeper into Ember.js application building process I hit another wall.
Before I was using mirage with great success - I just copy output from API that I wanted build around to mirage fixtures and it was working great.
Now I have problem with making it work with real API.
I first disabled mirage in config/environment.js
ember g adapter filter
import ApplicationAdapter from './application';
export default ApplicationAdapter.extend({
findAll: function(store, type, label) {
var url = `${this.host}/${this.namespace}/${type.modelName}`;
console.log(`${url}`);
return this.ajax(url, 'GET');
},
});
The application adapter looks like this
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
host: 'http://127.0.0.1:1234',
namespace: 'api',
headers: Ember.computed(function(){
return {"secret": "1234"};
})
});
And that way when I enter /filter
app/routes/filter/index
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('filter');
}
});
I can see that url is build ok as http://127.0.0.1:1234/api/filter and there is no 404 but I get error
Error while processing route: filter.index The adapter operation was aborted EmberError#http://127.0.0.1:4200/assets/vendor.js:29616:15
and as I don't fully grasp the know-how of Ember Inspector im trying to figure this out somehow
My filter model that worked before (with mirage) looks like this:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr(),
url: DS.attr()
});
The api returns list [{"id":1, "name": "namex", "url": "http://"},{"id":2, "name": "namey", "url": "http://"}]
I'm sure If its the way api return data as its not "filters" as I had problem with plurals before.
Edit: THIS RESOLVE THe problem
import DS from 'ember-data';
export default DS.RESTSerializer.extend(
{
normalizeFindAllResponse(store, type, payload)
{
var data = [];
payload.forEach(
function(item, index, enumerable)
{
var ob = {};
Ember.set(ob, 'id', item.id);
Ember.set(ob, 'type', 'filter');
Ember.set(ob, 'attributes', item);
data[index]=ob;
console.log(data[index]);
}
);
return {
data: data
};
}
});
This is almost the fix but not quite.
I can access some of model attributes like name, but there is a array object with 3 arrays inside, 2 of them are array the one in middle is Getter (what ever is that) and I cannot access it as its not array anymore. So im not sure if it binding to object correctly this way. Also I wasn't able to do anything with "data" because no matter what RESTAdapter here and there I put it would ignore it and ask for object with data/meta/errors attribute... dunno if its a bug or not.
As you are using JSONAPIAdapter your api response should follow the JSON format specification.
{
"data": [{
"type": "filter",
"id": "1",
"attributes": {
"name": "namex",
"url": "http://"
}
}, {
"type": "filter",
"id": "2",
"attributes": {
"name": "namey",
"url": "http://"
}
}]
}
or if you are not following JSONAPI in that case either you can change application adapter to extend RESTAdapter.
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
host: 'http://127.0.0.1:1234',
namespace: 'api',
headers: Ember.computed(function(){
return {"secret": "1234"};
})
});
RESTAdapter response would be like,
{"filters":[{"id":1, "name": "namex", "url": "http://"},{"id":2, "name": "namey", "url": "http://"}]}
either you need to send it like above format or manipulate it to produce the required format.(override normalizeFindAllResponse)
I'm attempting to grab some data from my Rails API and display it in the template.
I'm very new to ember so the simpler the explanation the better and forgive me if this is a very stupid question.
The problem seems to be that the api data is not reaching the model correctly, it works when I have static fixture data but not for server data. The Get request to the server is going through and I'm getting a good response, so there must be something wrong with how the json is moved to the model.
My route in routes/external/jobs.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('job');
}
});
My job model in models/job.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
id: DS.attr('string')
});
I am expecting my API to return data in the form
{
"jobs": [
{
id: "jfdsa132113",
title: "developer",
type: "Job",
body: "lorem ipsum",
published_at: date,
tags: [
"some stuff",
"more stuff"
]
},
{
id: "fdsafd3432",
title: "designer",
type: "Job",
body: "lorem ipsum",
published_at: date,
tags: [
"some stuff",
"more stuff"
]
}
]
}
My router
Router.map(function () {
//index route
this.route('external', function () {
this.route('jobs');
this.route('support');
});
export default Router;
I think you have a misunderstanding with regards to DS.Models, you should take a look at the guides, specifically http://guides.emberjs.com/v2.1.0/models/finding-records/.
Assuming the route and template are properly set up, and that ApplicationAdapter is extending RESTAdapter/ActiveModel Adapter — see expected JSON formats here —, I believe the problem is in models/jobs.js, which is unnecessary.
Ember Data should make the right request to /jobs when you do store.findAll('job'). Try removing models/jobs.js and adapters/jobs.js, and doing the following:
// routes/external/jobs.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('job');
}
});
If this doesn't help try also posting your router and any errors you get.
Assuming your models are loading up fine from your API I think the issue is in your template.
According to the docs, you need to access the attributes of each model explicitly. http://guides.emberjs.com/v2.1.0/templates/displaying-a-list-of-items/
So rather than {{title}} use {{job.title}}.
{{#each model as |job|}}
<a href={{job.title}}>
<div class="info">
<div class="title">{{job.body}}</div>
</div>
</a>
{{/each}}
This fixed it
adding the file
serializers/job.js
with the lines
import DS from 'ember-data';
export default DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
});
I did not build the api and I noticed that some of the id's were underscored convention. More can be read about it here http://ember-doc.com/classes/DS.ActiveModelSerializer.html
I'm having a problem with my sibling hasMany relationships disappearing. Working with Ember data canary.
I have the following data model:
import DS from 'ember-data';
export default DS.Model.extend({
// More here, discarded for brevity...
app: DS.hasMany('app', { async: true }),
paymentMethod: DS.hasMany('paymentMethod', { async: true })
});
When user is updated after deleting a paymentMethod in the following way:
var paymentMethod = this.get('content'),
currentUser = this.session.get('currentUser.content');
currentUser.get('paymentMethod').then(function ( paymentMethods ) {
paymentMethods.removeObject(paymentMethod.get('id'));
paymentMethod.destroyRecord();
currentUser.save();
}, handleError);
or saving in the following way:
var paymentMethod = self.store.createRecord('payment-method', paymentMethodData);
paymentMethod.save().then(function ( PaymentMethod ) {
currentUser.get('paymentMethod').addObject(PaymentMethod);
currentUser.save().then(function ( /* record */ ) {...
The apps array is set to an empty []. It happens the opposite way as well, deleteing or adding an app with a paymentMethod will unset the paymentMethod array.
I have the following serializer in place, but it appears as the relationship is set as an empty array before the record gets to the serializer:
var json = {
_id: user.get('id'),
name: {
first: user.get('firstName'),
last: user.get('lastName'),
company: user.get('companyName')
},
login: {
email: user.get('email'),
password: user.get('password')
},
app: user.get('app').mapProperty('id'),
paymentMethod: user.get('paymentMethod').mapProperty('id'),
time_stamp: user.get('time_stamp')
};
return json;
Sorry for the overload. Hope you can help.
You are naming your hasMany associations in singular, which isn't really following the convention. That being said, you have no 'apps' array. I don't think that should cause you any problems, I am just pointing out because you maybe searching for the wrong thing.
I suppose your backend somehow restricts you to this payload?
I'm trying a simple post retrieval app with ember. Everything works fine as long as i'm not passing arguments to the find() function:
This works great:
return this.store.find('post')
But not these:
return this.store.find('post', { title: 'title1' })
return this.store.find('post', {})
The server returns exactly the same JSON regardless of the parameters, but Ember doesn't seem to process it when there's arguments. The store stays empty.
Here's the complete code:
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
App.Router.map(function(){
this.resource('myPosts');
});
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api/v1/'
});
App.Post = DS.Model.extend({
title : DS.attr( 'string' ),
body : DS.attr( 'string' )
});
App.MyPostsRoute = Ember.Route.extend({
model : function() {
return this.store.find('post', { title: 'title1' })
},
setupController: function (controller, model) {
controller.set('model', model);
}
});
In all cases I never get any error, the queries are processed just fine and I've checked in chrome DevTools that the JSON returned by the server is the same.
Here's the JSON returned.
{"post": [{"body": "a body", "title": "title1"}]}
The handelbars template is simply:
<script type="text/x-handlebars" id="myPosts">
<div class = "postsWrapper">
{{#each}}
<div>{{this}}</div>
<div>title: {{title}}</div>
{{/each}}
</div>
</script>
Here's the output i get with this.store.find('post', { title: 'title1' }):
<App.Post:ember382:null>
title:
Here's the one with i get with no parameters:
<App.Post:ember294:null>
title: title1
Thanks for you help.
Ok after further conversation, I think your issue is linked to the API.
When you call this.store.find('post') Ember should make a GET request to the api/v1/posts endpoint, which should return the following JSON:
Note: this is a plural posts object with an array of post objects.
{
"posts": [
{ "body": "body1", "title": "title1" },
{ "body": "body2", "title": "title2" }
]
}
When you call this.store.find('post', { title: 'title1' }) Ember should make a GET request to the api/v1/posts?title=title1 endpoint, which should return this JSON:
Note: this is a singular post object with a single post object.
{
"post": { "body": "body1", "title": "title1" }
}
The format the JSON comes back from the server is really important, the API needs to do the filtering, Ember doesn't filter unless you ask it to.
//Setup:
Ember: 1.3.2
Handlebars: 1.3.0
jQuery: 2.0.0
-----------------
MongoDB (_id's, embedded data)
I have been attempting to get a self many to many relationship like this:
//Model:
App.Post = DS.Model.extend({
title: DS.attr('string'),
content: DS.attr('string'),
links: DS.hasMany('App.Post'),
});
Links should be embedded as id's for (hopefully) obvious reasons.
After a couple of days digging around I have managed to get the app to serialise and submit the data correctly via RESTAdapter, the code I am using looks like this:
//Controller:
App.PostController = Ember.ObjectController.extend({
actions: {
addRelated: function(related) {
var links = this.content.get('links').pushObject(related);
this.content.save();
}
}
});
//Store:
App.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.extend({
url: '/admin/api',
serializer: DS.RESTSerializer.extend({
primaryKey: function(type) {
return '_id';
},
addHasMany: function(hash, record, key, relationship) {
if (/_ids$/.test(key)) {
hash[key] = [];
record.get(this.pluralize(key.replace(/_ids$/, ''))).forEach(function(post) {
hash[key].push(post.get('id'));
});
}
return hash;
}
})
});
});
From what I can gather the serializer is expecting data in the form
{post: {...}, links: [{...},{...}]}
But since the link is of type post, I would rather not create an entire App.Links model if possible.
So can I map links to posts? As in
{post: {...}, posts: [{...},{...}]}
I tried adding a deserializeHasMany but it didn't get called when using App.Post.find()
I am guessing I would need to write a custom extract function that takes link_ids and extracts the posts into the record from it?
pI haven't test this but would say:
You should change your model to look like this:
App.Post = DS.Model.extend({
title: DS.attr('string'),
content: DS.attr('string'),
links: DS.hasMany('post'), //changed
});
Your JSON should be in the format:
{"posts": [{ "id":3 ... post item .... "links":[3,10]} { "id":4... post item .... "links":[4,11]}]}
All links must be included in the JSON unless already loaded.
My understanding is that you should not have to override the RESTAdapter and RESTSerializer as this should work out of the box - if it doesn't I'd first check ajax and capitalization.