I have a problem with model and serializer. I've looked at the documentation and can't find a solution. My app is connected to a django rest api.
Django response:
{
"id": 1,
"url": "http://localhost:8000/users/1/",
"username": "username",
"email": "xxx#email.com",
"is_staff": true
}
The model:
export default DS.Model.extend({
url : DS.attr('string'),
username : DS.attr('string'),
email : DS.attr('string'),
});
I've modified the response with a serializer to include "data":
export default DS.JSONAPISerializer.extend({
primaryKey: 'id',
normalizeFindRecordResponse(store, type, payload, id) {
console.log('payload',payload)
return {
data: {
'id': id,
'url': payload.url,
'username': payload.username,
'email': payload.email,
}
} ;
}
});
The route:
export default Ember.Route.extend({
model() {
return this.store.findRecord('users', 1);
}
});
Another version of the route same error:
export default Ember.Route.extend({
model() {
this.store.findRecord('users', '1').then(function(user){
console.log('user', user);
}).catch(function(e){
console.log('e', e);
});
}
});
Finally Ember inspector has the model but all values as undefined and "str is undefined" on console.
Image of ember inspector
1.normalizeFindRecordResponse is not returning valid JSONAPI format,
The below is the valid format,
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
// ... this article's attributes
}
}
}
As you can see, you are missing type, attributes key.
2.Your model file name should be user.js in singular.
3.this.store.findRecord('users', 1) - here you need to use singluar form of the model so this.store.findRecord('user', 1)
4.In your another attempt you are missing the return statement in model hook.
Related
I have some problem with get data from storage.
Isset model orders:
import DS from 'ember-data';
export default DS.Model.extend({
test: DS.hasMany('dealer-status'),
});
adapter (its is test data). I havn't normolize responce and i add params to data for normalize. It is implementation will be in serialize :
export default DS.JSONAPIAdapter.extend({
service: Ember.inject.service('admin.auth'),
query(store, type, query) {
var service = this.get('service');
return new Ember.RSVP.Promise(function (resolve, reject) {
service.fetch(query).subscribe(
res => {
var data = {
"data": [{
"type": "user",
"id": "1",
"attributes": {
test: 'test'
}
}]
};
resolve(data);
},
err => {
reject(err);
});
});
}
});
The router:
model() {
return this.get('store').query('orders', {}).then(res => {
console.log(res.get('test')); //undefined
console.log(res.objectsAt(0)); //undefined
})
In router I find by query, but i can't get param test. In Promise res.get('test') return undefined.
Why i get undefined in promise? How fix it?
Because in your order model you don't have attribute test. It's a hasMany relationship. Look into json API documentation about relationships in resource objects for more details.
So, I'm trying to access my model properties in controller.
Controller:
dashobards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
];
In route I have model named dashboards
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
}).then((hash) => {
return Ember.RSVP.hash({
dashboards: hash.dashboards
});
}, self);
I wanna have result in controller like this:
dashboards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
{ id: 17, name: 'test1' },
{ id: 20, name: 'test20' },
];
In controller I am trying to access this model like this:
this.dashborads = this.get(model.dashobards)
And it's not working, is there any other way of doing that?
Another update How to access complex object which we get it from server in ember data model attibute,
Created twiddle to demonstrate
define attribute with DS.attr(),
export default Model.extend({
permissions:DS.attr()
});
route file,
model(){
return this.store.findAll('dashboard');
}
Your server response should be like,
data: [{
type: 'dashboard',
id: 1,
attributes: {
permissions: {'name':'role1','desc':'description'}
}
}]
hbs file,
{{#each model as |row| }}
Name: {{row.permissions.name}} <br/>
Desc: {{row.permissions.desc}} <br />
{{/each}}
Update:
Still I am not sure about the requirement, Your twiddle should be minimalized working twiddle for better understanding..anyway I will provide my observation,
1.
model(params) {
this.set('id', params.userID);
const self = this;
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
}).then((hash) => {
return Ember.RSVP.hash({
user: hash.user,
dashboards: hash.dashboards
});
}, self);
}
The above code can be simply written like
model(params) {
this.set('id', params.userID);
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
});
}
Its good to always initialize array properties inside init method. refer https://guides.emberjs.com/v2.13.0/object-model/classes-and-instances/
For removing entry from array,
this.dashboard.pushObject({ 'identifier': '', 'role': '' }); try this this.get('dashboard').pushObject({ 'identifier': '', 'role': '' });.
if possible instead of plain object you can use Ember.Object like
this.get('dashboard').pushObject(Ember.Object.create({ 'identifier': '', 'role': '' }));
For removing entry.
removeDashboard(i) {
let dashboard = Ember.get(this, 'dashboard');
Ember.set(this, 'dashboard', dashboard.removeObject(dashboard[i]));
}
The above code can be written like, since i is an index
removeDashboard(i) {
this.get('dashboard').removeAt(i)
}
Just do return this.store.findAll('dashboard'); in route model hook, and dont override setupController hook, then in hbs you should be able to access model that will represent RecordArray. you can have a look at this answer for how to work with this.
Is this possible? I know I can do:
this.store.find('model', 1)
but that's not what I want. I would like to retrieve the json in this format: (working if I retrieve it in the route like this ):
App.OptionsRoute = Ember.Route.extend({
model: function () {
return {
"options":{
"id": "1",
"headline": "A Headline",
"results": [
{
"id": "1",
"title": 'Option 1',
},
{
"id": "2",
"title": "Option 2"
}
]
}
};
}
});
options model:
App.Options = DS.Model.extend({
headline: DS.attr(),
results: DS.attr()
});
options.hbs
<h5>{{options.headline}}</h5>
{{#each item in options.results}}
<h5>{{item.title}}</h5>
{{/each}}
I am using the RESTAdapter. And that is the only model that will be retrieved on that route. I would like to be able to use ember-data, but store.find expects an array.
You're missing a point here. First of all you're using bad format for your response. You need custom serializer. You can also use a bit more dirty workaround like this(but it works). Route:
App.OptionsRoute = Ember.Route.extend({
model: function() {
that = this;
return new Promise(function (resolve, reject) {
url = that.store.adapterFor('option').buildURL('option');
Ember.$.getJSON(url).then(function (json) {
body = json.options;
correct = {
options: [
body
]
};
that.store.pushPayload('option', correct);
resolve(that.store.all('option').get('firstObject'));
});
});
}
});
Template:
<h5>{{model.headline}}</h5>
{{#each item in model.results}}
<h5>{{item.title}}</h5>
{{/each}}
Application outputs:
A Headline
Option 1
Option 2
Working demo - please notice that I'm using $.mockjax to recreate your response from server, but it matches format you provided.
Edited as I narrowed down issues....
I'm working on an app that is taking in an API - data as JSON. It is an array of groups, with "name", "id", and other such elements.
{
"status": "success",
"data": {
"groups": [
{
"id": 7100,
"name": "Test 12345",
"kind": "floor",
"parent_group_id": 7000,
"controlled_device_type_count": {},
"is_top_level": true
}
]
}}
I also have a livestream websocket - data as JSON stream. It should update the elements referenced in the first API. The two only share "id".
Livestream:
{
"group":{
"usage":{
"10":1,
"20":0,
"30":2,
"40":2
},
"last_change":"2014-03-24T05:56:10Z",
"id":7954
}}
**Updated...**My IndexRoute:
App.ApplicationAdapter = DS.RESTAdapter.extend({
extractArray: function(store, type, payload, id, requestType) {
payload = payload.data;
return this._super(store, type, payload, id, requestType);
}
});
App.IndexRoute = Ember.Route.extend({
sortProperties: ['id'],
sortAscending: true,
beforeModel: function() {
var socket = window.io.connect('http://localhost:8887');
var self = this;
socket.on('group_live_stream', function(data){
var dataObj = JSON.parse(data);
self.store.push('group',dataObj.group);
});
},
actions: {
toggleMenu: function() {
this.controller.toggleProperty('menuVisible');
this.controller.pushBody();
} },
activate: function() {
var self = this;
$.getJSON('http://localhost:3000/api/groups/top?subscribe=true').then(function(data) {
self.store.pushMany('group', data.data.groups);
});
},
model: function() {
return this.store.all('group');
}
});
Updated:
So now I see the livestream coming through - and for a brief, immediate second - I see the API data (group name) and then it disappears. I'm thinking it's because I'm just "pushMany"-ing records and deleting old ones instead of updating. I've heard/read that pushPayload might be my solution....but I can't figure it out. At all (and when I put it in, I just get an error: "Uncaught Error: No model was found for '0' " Help?!
Any thoughts?
Thanks so much!
ptep
Your API's payload is not in the format that ember-data expects (it looks for your payload in the root of your JSON by default). You'll need to override extractArray (and likely extractSingle) on your ApplicationAdapter something like this:
ApplicationAdapter = DS.RESTAdapter.extend({
extractArray: function(store, type, payload, id, requestType) {
payload = payload.data;
return this._super(store, type, payload, id, requestType);
}
});
I have these two resources:
App.Users = DS.Model.extend({
first_name: DS.attr('string'),
last_name: DS.attr('string'),
email: DS.attr('string'),
userprofile: DS.belongsTo('App.Userprofiles', {embedded:true}),
fullName: function() {
return this.get('first_name') + ' ' + this.get('last_name');
}.property('first_name', 'last_name'),
didLoad: function() {
console.log('Developer model loaded', this);
}
});
App.Userprofiles = DS.Model.extend({
company: DS.attr('string'),
user: DS.belongsTo('App.Developers'),
didLoad: function() {
console.log('Developer Profile model loaded', this);
}
})
These are my view and controller:
App.UserInfoController = Ember.ObjectController.extend({
content: App.store.find(App.Users, 1),
}).create();
App.UserInfoView = Ember.View.extend({
controller: App.UserInfoController,
contentBinding: 'controller.content'
});
This a sample response for a user from my API
{
"email": "foo#gmail.com",
"first_name": "George",
"id": "1",
"last_name": "Eracleous",
"resource_uri": "/api/v1/users/1/",
"userprofile": {
"company": "bar",
"id": "1",
"resource_uri": "/api/v1/userprofiles/1/",
"user": "/api/v1/users/1/"
}
}
The user object is loaded correctly but when I try to do get("userprofile") I get null. Does anybody know what I am doing wrong?
In order to load embedded related objects you have to configure the serializer used by the adapter, by calling its 'map' function. The only way I know to do this is by subclassing the serializer and add an 'init' function to it, where you make the necessary calls to map. For every embedded relationship of every model class you will have to do a call to 'map'. This applies to to-one and to-many relationships. Make sure to configure your adapter to use this serializer.
For an example see my answer to a previous question.
You can also check out this example online.
As mentioned in the comment, instead of subclassing the serializer and calling its map() function in the initialiser you can directly call map() on the adapter class. As an example, here is an excerpt of my own code doing this.
WO.RESTAdapter.map(App.Category, {
resourceTypes: {
embedded: 'load'
}
});
WO.RESTAdapter.map(App.Resource, {
resourceType: {
embedded: 'load'
}
});
WO.RESTAdapter.map(App.Reservation, {
resource: {
embedded: 'load'
},
user: {
embedded: 'load'
}
});
App.serializer = App.WOSerializer.create();
App.store = DS.Store.create({
revision: 10,
adapter: WO.RESTAdapter.create({
namespace: "cgi-bin/WebObjects/Reserve.woa/ra",
serializer: App.serializer
}),
serializer: App.serializer,
adapterForType: function(type){
return this.get('adapter');
}
});