Ember.js linking application to real api after mirage success - javascript

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)

Related

Objects in Ember Data

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);

Ember data not populating model

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

Ember.js find() does not work with parameters

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.

Ember data 1.0 beta how map embedded data

It seems that ember data have many changing up to version 1.0 beta. All works great with version 0.13. now I want update to higher version. In my case we have an embedded model 'user'
App.Post = DS.Model.extend({
subject: DS.attr('string'),
created: DS.attr('number'),
fcreated: function(){
debugger;
var d = new Date(this.get('created'));
return d.toLocaleDateString();
}.property('created'),
reporter: DS.belongsTo('user')
}
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'restws'
});
The Json from server looks like this.
{
"posts": [
{
"id": "5226f2670364e70ae7d77266",
"subject": "Text",
"created": 1325410935048,
"reporter": {
"id": "5226f2660364e70ae7d771e2",
"firstName": "Doris",
"lastName": "Baumertr"
}
}
I get the following error code 'Uncaught TypeError: Cannot call method 'toString' of undefined'. In the ember source code I see, that in ember-data.js line 2236 the function throw the error 'buildRecord: function(type, id, data) .. ' After debugging I see that the properties type is undefined id is set with the correct id and data is undefined?
What is the mistake? How I can map the embedded data?
Here's actually the exact extractSingle method that you need to implement
App.PostSerializer = DS.RESTSerializer.extend({
extractSingle: function(store, type, payload, id, requestType) {
if(typeof payload.post.reporter !== "undefined") {
var reporter_id = payload.post.reporter.id;
payload.users = [payload.post.reporter];
payload.post.reporter = reporter_id;
}
return this._super.apply(this, arguments);
}
});
Here's a jsbin http://jsbin.com/EKItexU/1/edit?html,js,output
Note, that I had to redefine ajax method in RESTAdapter to emulate the server returning your JSON.
Also, if you are sideloading users in your JSON, than you'll have to update this method so it doesn't overwrite sideloaded users in your payload (payload.users property)
Support for embedded records is gone (for now).
You can handle embedded records yourself by implementing extractSingle and reorganizing your JSON payload.
Please read here for more info about the transition: https://github.com/emberjs/data/blob/master/TRANSITION.md#embedded-records
Hope it helps.

Ember-Data: How do "mappings" work

I'm currently trying to put something together with ember + emberdata + router + asp.net web api. Most of it seem to work, however I stuck in an error message I get when ember-data tries to findAll through the adapter for my models.
In my backend I have a model like this (C#):
public class Genre {
[Key]
public int Id { get; set; }
[Required]
[StringLength(50, MinimumLength=3)]
public string Name { get; set; }
}
Which in my app I represent it like this using ember-data:
App.Genre = DS.Model.extend({
id: DS.attr("number"),
name: DS.attr("string")
}).reopenClass({
url: 'api/genre'
});
I have also a Store defined in my App using the RESTAdapter like so:
App.store = DS.Store.create({
revision: 4,
adapter: DS.RESTAdapter.create({
bulkCommit: false
})
});
And the store is used in my controller as below:
App.GenreController = Ember.ArrayController.extend({
content: App.store.findAll(App.Genre),
selectedGenre: null
});
The router is defined as
App.router = Em.Router.create({
enableLogging: true,
location: 'hash',
root: Ember.Route.extend({
//...
genre: Em.Route.extend({
route: '/genre',
index: Ember.Route.extend({
connectOutlets: function (router, context) {
router.get('applicationController').connectOutlet('genre');
}
})
}),
//...
})
})
When I run my application, I get the following message for every object that has this same structure:
Uncaught Error: assertion failed: Your server returned a hash with the
key 0 but you have no mappings
For reference, here's the json the service is returning:
[
{
"id": 1,
"name": "Action"
},
{
"id": 2,
"name": "Drama"
},
{
"id": 3,
"name": "Comedy"
},
{
"id": 4,
"name": "Romance"
}
]
I cannot tell exactly what the problem is and since the assertion is mentioning that I need mapping, I'd like to know:
What this mapping is and how to use it.
Since the returned json is an array, should I be using a different type of controller in my app ,or is there anything I should know about when working with this type of json in ember-data? or should I change the JsonFormatter options in the server?
Any help is welcome.
I can definitely add more information if you feel this isn't enough to understand the problem.
EDIT: I've changed a few things in my backend and now my findAll() equivalent action in the server serializes the the output as the following json:
{
"genres": [
{ "id": 1, "name": "Action" },
{ "id": 2, "name": "Drama" },
{ "id": 3, "name": "Comedy" },
{ "id": 4, "name": "Romance" }
]
}
But I still can't get it to populate my models in the client and my error message has changed to this:
Uncaught Error: assertion failed: Your server returned a hash with the
key genres but you have no mappings
Not sure what else I might be doing wrong.
The method that throws this exception is sideload and checks for the mappings like this:
sideload: function (store, type, json, root) {
var sideloadedType, mappings, loaded = {};
loaded[root] = true;
for (var prop in json) {
if (!json.hasOwnProperty(prop)) { continue; }
if (prop === root) { continue; }
sideloadedType = type.typeForAssociation(prop);
if (!sideloadedType) {
mappings = get(this, 'mappings');
Ember.assert("Your server returned a hash with the key " + prop + " but you have no mappings", !!mappings);
//...
This call sideloadedType = type.typeForAssociation(prop); returns undefined and then I get the error message. The method typeForAssociation() checks for the for 'associationsByName' key which returns an empty Ember.Map.
Still no solution for this at the moment.
By the way...
My action is now like this:
// GET api/genres
public object GetGenres() {
return new { genres = context.Genres.AsQueryable() };
}
// GET api/genres
//[Queryable]
//public IQueryable<Genre> GetGenres()
//{
// return context.Genres.AsQueryable();
//}
I had to remove the original implementation which gets serialized by json.NET as I could not find config options to produce a json output as Ember-Data expects ( as in {resource_name : [json, json,...]}). Side effect of this is that I've lost built-in OData support, but I'd like to keep it. Does anyone know how could I configure it to produce different json for a collection?
The mapping can be defined in the DS.RESTAdapter. I think you could try to define something like this:
App.Store = DS.Store.extend({
adapter: DS.RESTAdapter.create({
bulkCommit: true,
mappings: {
genres: App.Genre
},
// you can also define plurals, if there is a unregular plural
// usually, RESTAdapter simply add a 's' for plurals.
// for example at work we have to define something like this
plurals: {
business_process: 'business_processes'
//else it tries to fetch business_processs
}
}),
revision: 4
});
Hope this resolves your problem.
Update:
At this time, this is not well documented, I don't remember if we found it by ourself reading the code, or perhaps Tom Dale pointed on it.
Anyway, here is the point for plurals
For the mappings, I think we were driven by the same error as you, and either we tried, either Tom teached us about this.
The RESTAdapter expects the returned JSON to be of the form:
{
"genres": [{
"id": 1,
"name": "action"
},{
"id": 2,
"name": "Drama"
}]
}
The tests are a good source of documentation, see https://github.com/emberjs/data/blob/master/packages/ember-data/tests/unit/rest_adapter_test.js#L315-329
I'm using Ember Data rev. 11 and it seems that the plurals config in DS.RESTAdapter.create never works. I looked into the codes and found a solution as following:
App.Adapter = DS.RESTAdapter.extend({
bulkCommit: false
})
App.Adapter.configure('plurals', {
series: 'series'
})

Categories