Needless to say, new to Ember and just trying to get a proof of concept done. Already had some help with EmberCLI but this a new oddness for me.
/routes/index.js
export default Ember.Route.extend({
model() {
return this.store.findAll('skill');
}});
/models/skill.js
import Model from 'ember-data/model';
export default Model.extend({
name: DS.attr('string'),
desc: DS.attr('string'),
type: DS.attr('string')
});
/adapters/application.js
import DS from "ember-data";
export default DS.JSONAPIAdapter.extend({
namespace: 'v1',
host: 'http://edu-api.app:8000',
});
/serializers/application.js
import DS from "ember-data";
export default DS.JSONAPISerializer.extend({});
/templates/index.hbs
<h2>Skills</h2>
<ul>
{{#each model as |item|}}
<li>
<div>
<li>{{item}} {{item.id}} {{item.type}} {{item.name}} {{item.desc}}</li>
</div>
</li>
{{/each}}
</ul>
It seems that the id attr is available and correct, but yet all the other attrs are not being loaded from the json. If I copy/paste the json and manually set it in the model, it works as expected, so is there some filtering going on when coming from the model store or serializer?
The JSONAPISerializer and JSONAPIAdapter are not to for simple JSON/REST Backends but for a fully JSONAPI compatible Backend.
You say it works when you copy & paste and set it on the model, so probably you mean something like this:
this.store.createRecord('skill', {
id: '1',
name: 'foo',
desc: 'bar',
type: 'baz'
});
This will indeed work for a model creation but is not a JSONAPI compatible response! In JSONAPI you would have something like this (if the request should return multiple entities:
{
data: [{
id: '1',
attributes: {
name: 'foo',
desc: 'bar',
type: 'baz'
}
}]
}
So now you have two options:
Make your API JSONAPI compatible, or
use a different adapter & serializer.
The RESTSerializer/RESTAdapter are a simple default implementation that can handle a structure like this:
{
id: '1',
name: 'foo',
desc: 'bar',
type: 'baz'
}
Also they are highly customizable.
Checkout the official API for documentation.
Related
Ember novice here. I have been following along with the tutorial on the Ember website here.
I have been R&D'ing the example to the word and everything works...until I try implementing Mirage. The data just never shows up on the index.hbs page.
Here's my model hook:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('rental');
},
});
And my model: rental.js
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
owner: DS.attr('string'),
city: DS.attr('string'),
type: DS.attr('string'),
image: DS.attr('string'),
bedrooms: DS.attr('number')
});
My index.hbs:
<h1> Welcome to Super Rentals </h1>
We hope you find exactly what you're looking for in a place to stay.
{{#each model as |rentalUnit|}}
{{rental-listing rental=rentalUnit}}
{{/each}}
{{#link-to "about"}}About{{/link-to}}
{{#link-to "contact"}}Click here to contact us.{{/link-to}}
and lastly my app/mirage/config.js:
export default function() {
this.get('/rentals', function() {
return {
data: [{
type: 'rentals',
id: 1,
attributes: {
title: 'Grand Old Mansion',
owner: 'Veruca Salt',
city: 'San Francisco',
type: 'Estate',
bedrooms: 15,
image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg'
}
}, {
type: 'rentals',
id: 2,
attributes: {
title: 'Urban Living',
owner: 'Mike Teavee',
city: 'Seattle',
type: 'Condo',
bedrooms: 1,
image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg'
}
}, {
type: 'rentals',
id: 3,
attributes: {
title: 'Downtown Charm',
owner: 'Violet Beauregarde',
city: 'Portland',
type: 'Apartment',
bedrooms: 3,
image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg'
}
}]
};
});
}
I get two messages in Chrome developer console:
Mirage: Your Ember app tried to GET 'http://localhost:4200/assets/vendor.js', but there was no route defined to handle this request. Define a route that matches this path in your mirage/config.js file. Did you forget to add your namespace?
and this warning:
WARNING: Encountered "data" in payload, but no model was found for model name "datum" (resolved model name using super-rentals#serializer:-rest:.modelNameFromPayloadKey("data"))
However it looks like the info was sucessfully retrieved as I see a:
Successful request: GET /rentals
Object {data: Array[3]}
which reflects the proper data. It just is breaking somewhere between that and the index.hbs and I am to novice to figure it out. I'm sure it's just a small misunderstanding on my part. Any help would be appreciated!
You have the wrong version of Ember Data installed. Also make sure you restart the server any time you install a dependency.
The error message that you are getting means that your application is using RESTAdapter (the default adapter for Ember Data 1.x). That is why it looks at the top-level data key and tries to singularize and find the related model.
You can update by following the instructions on the latest Ember CLI release. Or, you can npm install -g ember-cli#beta and start from scratch.
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 writing an ember-cli app. I have the following model:
// app/models/school.js
export default DS.Model.extend({
name: DS.attr('string', { defaultValue: '' })
});
It was generated using an ember generator like all my other models. It has a functioning unit test as well that tests for the default value of it's name attribute. All tests are green until another model belongs to school like this:
// app/models/professor.js
export default DS.Model.extend({
name: DS.attr('string', { defaultValue: '' }),
email: DS.attr('string', { defaultValue: '' }),
courses: DS.hasMany('course'),
posts: DS.hasMany('post'),
school: DS.belongsTo('school')
});
This test is totally green until I add the school attribute. It's even green with 'model:school' defined in the needs array of the moduleForModel helper:
// tests/unit/models/professor-test.js
// this is green w/o belongsTo('school') in the model
moduleForModel('professor', {
// Specify the other units that are required for this test.
needs: ['model:school', 'model:post', 'model:course']
});
The error I'm getting is:
Error: No model was found for 'school'
Here's the models directory
$ ls app/models/
course.js post.js professor.js school.js student.js
Why is it not finding my model?
You need to import the school model in your test:
import '[appName]/models/school';
//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.
I have two ember models with a relationship like this
App.Foo = DS.Model.extend
bar: DS.belongsTo("App.Bar", embedded: true)
App.Bar = DS.Model.extend
primaryKey: "blah"
blah: DS.attr "string
If I create and save a new record like this:
foo = App.store.createRecord App.Foo
foo.set "bar", App.Bar.createRecord(blah: "blahblah")
App.store.commit()
I see 2 post requests to the server:
URL: /foos
Payload: {"foo":{"bar":null}}
and
URL: /bars
Payload: {"bar":{"blah":"blahblah"}}
The association is embedded so I would like to see:
URL: /foos
Payload: {"foo":{"bar":{"blah":"blahblah"}}}
Can I achieve this with the ember-data REST adapter or do I need to write my own code to do this?
I am observing the same behavior in my application.
Setting 'embedded' to true only helps you get data as embedded, but while you post it separate requests will be generated.
You have write your in code if you want to achieve it in one request.
This is not a direct answer to your question, but for what it's worth I've found that "fighting" Ember's design by trying to save multiple models at once led me down a terrible path.
It is much easier and more reliable to add the required attributes for a new Bar to Foo and then create and return the new objects on the server, like so:
App.Foo = DS.Model.extend({
blah: DS.attr('string'),
barName: DS.attr('string'),
bar: DS.belongsTo('bar')
})
this.store.createRecord('foo', { blah: "stuff", barName: "foo" }).save()
# => POST /foos
Request:
{ foo: { blah: "stuff", bar_name: "boo" } }
Response:
{ foo: { id: 1, blah: "stuff", bar: { id: 1, name: "boo" } } }
Yes, you end up with an unused attribute on Foo, but you save yourself a lot of code.