Ember data - Cannot read property 'typeKey' of undefined - json

Trying to load a plan model, embedded, into my app model.
I keep getting the following error when loading (it saves just fine):
Cannot read property 'typeKey' of undefined TypeError: Cannot read property 'typeKey' of undefined
at Ember.Object.extend.modelFor (http://localhost:4200/assets/vendor.js:71051:22)
at Ember.Object.extend.recordForId (http://localhost:4200/assets/vendor.js:70496:21)
at deserializeRecordId (http://localhost:4200/assets/vendor.js:71500:27)
at http://localhost:4200/assets/vendor.js:71477:11
at http://localhost:4200/assets/vendor.js:69701:20
at http://localhost:4200/assets/vendor.js:17687:20
at Object.OrderedSet.forEach (http://localhost:4200/assets/vendor.js:17530:14)
at Object.Map.forEach (http://localhost:4200/assets/vendor.js:17685:14)
at Function.Model.reopenClass.eachRelationship (http://localhost:4200/assets/vendor.js:69700:42)
at normalizeRelationships (http://localhost:4200/assets/vendor.js:71463:12) vendor.js:17062logToConsole
With that said I have the following models,
app/models/app.js
export default DS.Model.extend({
name: attribute('string'),
domain: attribute('string'),
plan: DS.belongsTo('plan', { embedded: 'load' }),
creator: DS.belongsTo('user', { async: true }),
time_stamp: attribute('string', {
defaultValue: function () {
return moment().format("YYYY/MM/DD HH:mm:ss");
}
})
});
app/models/plan.js
export default DS.Model.extend({
price: attribute('number'),
description: attribute('string'),
tagline: attribute('string'),
title: attribute('string'),
features: attribute('array') // Array is defined in a transform, don't worry.
});
Plan being kind of a static document.
Here's my server response when calling store.get('creator.apps');
{
"apps":[
{
"_id":"53da9994b2878d0000a2e68f",
"name":"Myapp",
"domain":"http://myapp.com",
"creator":"53d9598bb25244e9b1a72e53",
"plan":{
"_id":"53d93c44b760612f9d07c921",
"price":0,
"description":"Free plan",
"tagline":"Great for testing",
"title":"Developer",
"features":["5,000 Requests","API/Plugin Access"],
"__v":0
},
"time_stamp":"2014/07/31 13:31:32",
"__v":0
}
]
}
I realize that the typeKey error is due to Ember not finding a model for the response. I can confirm that it finds the app type, firing a hook under normalizeHash.apps.
Sorry this is such a long post, just can't wrap my head around the cause of the issue!

App.Thing = DS.Model.extend(
{
name: attr('string'),
children: DS.hasMany('child', {inverse:null})
}
);
App.ThingSerializer = DS.RESTSerializer.extend(
DS.EmbeddedRecordsMixin, {
attrs: {
children: { embedded: 'always' }
}
}
);
DS.EmbeddedRecordsMixin must be in your model and you must have `embedded:'always' for the correct attribute.
If you have a Thing model then you can make Ember Data load the embedded children (here and array of nested objects) by using the model specific serializer.
Resources:
http://emberjs.com/guides/models/customizing-adapters/
http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html

Ember doesn't want to have the record embedded in the JSON of the parent record.
Do what you need to, to get your json like this. With just the plan id.
{
"apps":[
{
"_id":"53da9994b2878d0000a2e68f",
"name":"Myapp",
"domain":"http://myapp.com",
"creator":"53d9598bb25244e9b1a72e53",
"plan_id":"53d93c44b760612f9d07c921", // just output id only not embedded record
"time_stamp":"2014/07/31 13:31:32",
"__v":0
}
]
}
This then lets ember look up the related model itself, using the async: true
export default DS.Model.extend({
name: attribute('string'),
domain: attribute('string'),
plan: DS.belongsTo('plan', { async: true }), //changed
creator: DS.belongsTo('user', { async: true }),
time_stamp: attribute('string', {
defaultValue: function () {
return moment().format("YYYY/MM/DD HH:mm:ss");
}
})
});
I've just gone through the pain of this myself and with some help found an answer.
For anyone else who's come here and still is has issues, read my answer to my own question for a detailed rundown of what the typeKey error means and further steps I used to resolve the issue myself.
Deploying ember-rails to Heroku - TypeError: Cannot read property 'typeKey' of undefined

Related

How do I restrict the result of Sequelize Model.create to the properties of the model?

I'm experimenting with Sequelize for the data layer in an API. For better or for worse, I have a table with over 30 columns but API users only need to know (to simplify this question) only 5 of them.
I built a Sequelize model that "exposes" just the fields API users need to know about (the rest are all nullable):
const Widget = sequelize.define("widget",
{
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: Sequelize.STRING },
active: { type: Sequelize.BOOLEAN },
created_at: { type: Sequelize.DATE },
last_modified_at: { type: Sequelize.DATE },
}, {
timestamps: false,
freezeTableName: true,
}
);
I can use this model just fine in my service layer; in fact, my API endpoints to get a widget by id:
export default async function(widgetId: string): Promise<any> {
return Widget.findOne({ where: { id: widgetId } });
}
and to search widgets:
export default async function(name: string): Promise<any> {
const where: any = {};
if (name) {
where.name = name;
}
// More stuff here omitted for clarity, e.g., order and limit...
return Widget.findAll({ where, order, limit });
}
both work lovely. Each returns a promise and when I jsonify the value from the resolved promise, the resulting widget contains ONLY the fields in the Sequelize model, exactly as I hoped for.
const widget = await getWidgetById(widgetId); // works great (5 fields shown)
const widgets = await getWidgets(name); // works great (5 fields in each search result)
HOWEVER, when creating widgets, the resulting widget JSON contains all 30 fields. This was not expected. The service layer invocation is just:
export default async function(payload: {}): Promise<any> {
return Widget.create(payload);
}
and I called it from the API handler like this:
const widget = await createWidget(payload);
The JSONified widget has all 30 fields. I would like to restrict it to the desired 5 fields.
Now I did look into this but printing the value of widget after the await and I did see that Sequelize gave back:
widget {
dataValues:
{ id: 472304,
name: 'ABC',
active: true,
created_at: 2018-02-04T04:58:31.812Z,
last_modified_at: 2018-02-04T04:58:31.812Z,
======>>>>>> ZILLIONS OF OTHER FIELDS ALL NULL <<<<<<====== },
_previousDataValues:
{ name: 'ABC',
active: true,
created_at: 2018-02-04T04:58:31.812Z,
last_modified_at: 2018-02-04T04:58:31.812Z,
id: 472304 }, <<<<<<====== THIS IS PROMISING
_changed:
{ name: false,
active: false,
created_at: false,
last_modified_at: false,
id: false },
_modelOptions:
{ ... },
_options:
{ ... },
__eagerlyLoadedAssociations: [],
isNewRecord: false }
so there is some indication in there that Sequelize knows that what is in the model is only part of the table. When I pass that very object to JSON.stringify I get only the part in the dataValues section, so what I am seeing is some interesting magic with Sequelize's toString.
Digging deep into that object feels wrong to me.
Is there a (proper) way to restrict the "returned" (I know it is in a promise) object from Model.create to be restricted to just those properties in the model?
I am not sure if I understood your problem correctly. If you are looking to exclude some of the attributes when returning. You can exclude some of your attributes in attributes.exclude as an array.
Widget.findAll({
where,
order,
limit,
attributes: {
include: [],
exclude: []
}
})

How to store an array of nested objects using Sails.js with MongoDB?

I'm trying to save an nested object inside a sails.js model.
This is how it looks like:
module.exports = {
schema: true,
attributes: {
label: {
type: 'string',
required: true,
},
consumption: [{
timestamp: {
type: 'string',
required: true,
},
value: {
type: 'float',
required: true,
},
}],
}
};
To include the values inside the array I'm doing the following (inside the controller):
if(!plug.consumption)
plug.consumption = [];
plug.consumption.push({
timestamp: req.param('timestamp'), /* Format: '2016-04-14T16:18:24.972Z' */
value: req.param('value'), /* Format: '6.5' */
});
plug.save(function (err){
if(err){
res.send("Error");
return next(err);
}
});
But when the plug.save is executed, sails breaks and says Error: Unknown rule: 0
I've searched how to store arrays of objects on sails.js but didn't find anything that would work.
Can anyone help?
Thanks
You syntax used in consumption is wrong or at least not documented. Waterline supports attribute types json and array as documented but you can't define a schema for them. To define a schema you have to use a One-to-Many relationship between your model and a consumption model.

Error while processing route: index Assertion Failed: You must include an 'id'

I'm making an End-of-degree project of a city dashboard. I'm usin:
- PostgreSQL as DB
- Node.js + Express.js + Massive.js as server
- Ember.js as client app
Actually, I'm testing if I can get the data from DB to Ember, but I'm getting the next error (I've tried almost every solution I found here):
Error while processing route: index Assertion Failed: You must include an 'id' for poblacio in an object passed to 'push' Error: Assertion Failed: You must include an 'id' for poblacio in an object passed to 'push'
These are my files:
server.js (just a piece)
router.route('/poblacios')
.get(function(request, response) {
db.poblacio.find({}, function(err, res){
response.json(res);
});
});
app.use('/api/v1/', router);
adapters/application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api/v1'
});
route/application.js
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.store.findAll('poblacio');
}
});
models/poblacio.js
import DS from 'ember-data';
export default DS.Model.extend({
anny: DS.attr('number'),
districte: DS.attr('number'),
barri: DS.attr('string')
});
serializers/poblacio.js
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
primaryKey: 'id',
normalizeFindAllResponse(store, type, payload) {
return {
data: {
id: payload.id,
type: type.modelName,
attributes: {
anny: payload.anny,
districte: payload.districte,
barri: payload.barri,
}
}
};
}
});
localhost:3000/api/v1/poblacios returns the number of women and men from 0 to 95+ years by district, neighbourhood and year, with an id for each row:
[{"id":1,"anny":2015,"districte":1,"barri":"1. el Raval","donesanys0":206,"donesanys1":212,"donesanys2":206,"donesanys3":247....
{"id":2,"anny":2015,"districte":1,"barri":"2. el Barri Gotic","donesanys0":48,"donesanys1":53...
....
{"id":657,"anny":2007,"districte":10,"barri":"73. la Verneda i la Pau","donesanys0":103,"donesanys1":118,"donesanys2":123,"donesanys3":107...
Thanks for your help!
If you use the JSONAPIAdapter you need to follow the JSON API spec.
The JSON above, according to the spec, should be something similar to:
{
data: [
... collection of resource objects
]
}
And that is the problem, your payload is an array, therefore a valid solution would be on the line of:
import DS from 'ember-data';
export default DS.JSONAPISerializer.extend({
primaryKey: 'id',
normalizeFindAllResponse(store, type, payload) {
return {
data: payload.map((el) => {
return {
id: el.id,
type: type.modelName,
attributes: { /* the attributes you need from el */ }
}
})
};
}
});
Hope this helps.

ExtJs5: How to read server response in Model.save

I use Model.save to save data from the ExtJs form. Sometimes server returns operation status in following format:
{"success": false, "error": {"name": "Invalid Name"}}
The following code sends data from form to server:
var model = formPanel.getRecord();
model.save({
callback: function(record, operation, success) {
// operation.response is null,
// and success === true
// how to read server response here?
}
})
Server response is treated as successful because HTTP status is 200. So I I have to read server response to check operation status. But operation.response is null in callback function.
Here is my Model:
Ext.define('My.Model', {
extend: 'Ext.data.Model',
idProperty: 'id',
fields: [
{name: 'id', type: 'auto'},
{name: 'name', type: 'auto'}
],
proxy: {
type: 'rest',
url: 'api/v1/models',
appendId: true,
reader: {
type: 'json',
},
writer: {
type: 'json'
}
}
});
Question: how can I access server response after Model.save's call?
More generic question: is it semantically correct to use Model.load or Model.save to populate/save the ExtJs form?
I'm using ExJs 5.0.1.1255.
I created some simple test code for this:
var Clazz = Ext.define('MyModel', {
extend: 'Ext.data.Model',
proxy: {
type: 'rest',
url: 'api/v1/models'
}
});
var instance = Ext.create('MyModel', {
name: 'MyName'
});
instance.save({
callback: function(record, operation) {
}
});
The server responds with:
{
success: true,
something: 'else'
}
You can see this in a fiddle here: https://fiddle.sencha.com/#fiddle/fhi
With this code, the callback has a record argument, and record.data contains the the original record merged with the server response. In addition, you can do operation.getResponse() (rather than just operation.response) to get full access to the server's response.
In regard to your question on load vs save, if you use view models and bind the model that way, it kind of becomes moot as your form should always reflect the state of the model.
Using model.save() and Model.load() is definitely the correct thing to do.
In addition to providing a custom callback, you should investigate configuring a custom proxy. In a custom proxy, you can provide your own implementation of the extractResponseData method. This would let you centralise your need to examine the server response.

Accessing nested objects in JSON feed - Sencha Touch

I'll begin with the usual disclaimer: new to Sencha Touch/working with JSON, floundering in the dark. Any help or prodding in the right direction is appreciated more than you know!
I'm trying to get my app to fetch data from a public Google Spreadsheet JSON feed. From what I've managed to figure out, my current model is based on JSON arrays, NOT nested objects. How do I access and return a nested object?
Ext.regModel('Count', {
fields: [{name:'$t'}]
});
this.list = new Ext.List({
itemTpl: new Ext.XTemplate('<div>{$t}</div>'),
loadingText: false,
store: new Ext.data.Store({
model: 'Count',
proxy: {
type: 'scripttag',
url : 'http://spreadsheets.google.com/feeds/cells/0AuYDRk91MX8-dHhkV29ZVkRGNjlvZjV4QVBIVmJubVE/odb/public/basic?range=A1&alt=json',
reader: {
type: 'json',
root: 'feed'
}
}
})
});
The JSON data (extra stuff removed, above link will show all of it if need be, contains an email address I'd rather not post and have indexed):
{
"feed":{
"entry":[{
"content":{
"type":"text",
"$t":"11"
}
}]
}
}
If I plop in another JSON feed that uses arrays I can work with it just fine, but just can't figure out what I need to do to access that integer in the object that corresponds to $t. If I put "entry" as the root instead of "feed," I get an error that reads, "Uncaught TypeError: Cannot read property 'length' of undefined."
The solution! Turns out Sencha didn't like the $ in my template variable.
Ext.regModel('Count', {
fields: [{name:'count', mapping:'content.$t'}]
});
this.list = new Ext.List({
itemTpl: new Ext.XTemplate('<div>{count}</div>'),
loadingText: false,
store: new Ext.data.Store({
model: 'Count',
proxy: {
type: 'scripttag',
url : 'http://spreadsheets.google.com/feeds/cells/0AuYDRk91MX8-dHhkV29ZVkRGNjlvZjV4QVBIVmJubVE/odb/public/basic?range=A1&alt=json',
reader: {
type: 'json',
root: 'feed.entry'
}
}
})
});