Recently, I started to learn Ember-data and I'm writting an application with very custom API.
Response from Back-end is in bad format, so i normalize it to JsonApi via method 'normalizeResponse' and everything works good.
The problem appears in moment when I want to see content from response. When I was reading about Ember-data, I learned that if got data (InnerObjects), I will be able to take property from it via.
InnerObject.get('some_property'), but it doesnt work for me.
If I want 'some_property' I have to write InnerObject.data.someproperty what looks bad in longer path. I used Ember.debug() to see this path and my browser shows me that property '_data' is EmptyObject what is not true. When i click on it, it shows a list of properly content ( look attachment ).
Am I doing something wrong ? Am I forget about something or misunderstood Ember-Data?
I will be grateful for any help.
IMAGES:
Browser with _data -> EmptyObject
Browser with errors
export default DS.Model.extend({
facebook: DS.attr(),
www: DS.attr(),
name: DS.attr(),
street: DS.attr(),
house_number: DS.attr(),
postal_code: DS.attr(),
city: DS.attr(),
province: DS.attr(),
picture: DS.attr(),
x: DS.attr(),
y: DS.attr()
});
//json api serializer
export default ApplicationSerializer.extend({
normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) {
return this._super(store, primaryModelClass, this._normalizeSearch(payload), id, requestType);
},
_normalizeSearch(shops) {
let data = shops.map((obj) => {
return {
type: "search",
id: obj.id_sklep,
attributes: {
facebook: obj.facebook,
www: obj.www,
name: obj.nazwa_sklep,
street: obj.adres_ulica,
house_number: obj.adres_nr_domu,
postal_code: obj.adres_kod,
city: obj.adres_miasto,
province: obj.adres_woj,
picture: obj.zdjecie_sklep,
x: obj.lat,
y: obj.lng
}
};
});
return { data: data } ;
}
});
export default Ember.Service.extend({
getShopsAndServices(pattern) {
return this.get('store').query('search', {
fraza: pattern,
cena_min: 0,
cena_max: 100,
id_kat: 1,
lat: 53,
lng: 18
});
}
}
//Controller action:
searchRequest(pattern) {
return pattern.length > this.MIN_CHARS_FOR_SEARCH ? this.get('search').getShopsAndServices(pattern).then((results) => {
let content = results.get('content').length ? results.get('content') : [];
if (content) {
let foo = content[0];
Ember.Logger.debug(foo)
Ember.Logger.debug(foo._data.name)
Ember.Logger.debug(foo.get('name'))
}
return this.set('content', results.get('content').length ? results.get('content') : []);
}) : this.set('content', []);
},
InnerObject.data.someproperty => This is wrong.
InnerObject.get('some_property') => this is right.
Update your question with not working code. so that we can identify the issue.
1.query method returns Promise and this will be resolved to DS.RecordArray which extends Ember.ArrayProxy so you can use all the methods available.
2.results.get('content') - Don't access content property,
3.You can convert that special array to normal array by results.toArray()
4.You can even use each helper to iterate it like normal array template which is returned by this this.get('search').getShopsAndServices(pattern).
Related
I'm using Papaparse with Typescript to parse a local file and it works well.
I'm doing it like this:
parse(file, {
header: true,
dynamicTyping: true,
complete: (results) =>
console.log(results)
});
But I want to strongly type the result. I have an interface and the result of the parse will always return me an array of objects with the following properties:
export interface Person {
name: string;
age: number;
location: string;
}
How can we type the result ?
I found this Reddit thread and I tried their solution but it doesn't work:
parse<Person[]>(file, {
header: true,
dynamicTyping: true,
complete: (results: Person) =>
console.log(JSON.stringify(results.age))
});
The complete callback doesn't return a Person object.
The function should look like:
import {ParseResult} from 'papaparse';
function(results: ParseResult<Person>){ ...}
See https://www.papaparse.com/docs#results and line 255 in the typings file
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: []
}
})
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
I'm requesting data from an api that isn't configured properly.
It serves as text/html, but when I run JSON.parse(data) I get an parse error. and I do data.trade it says undefined.
If I just echo the data it looks like this (sample, not the full object):
"{\"buyOrder\":[{\"price\":\"5080.000000\"}]}"
Here is the url in question:
http://www.btc38.com/trade/getTradeList.php?coinname=BTC
I'm using request module to fetch the data.
How would I convert this string into a JSON object?
Here is the request:
var url = 'http://www.btc38.com/trade/getTradeList.php?coinname=BTC'
, json = true;
request.get({ url: url, json: json, strictSSL: false, headers: { 'User-Agent' : 'request x.y' } }, function (err, resp, data) {
c.log(data.trade); //undefined
});
Trimming the string got everything working well for me:
var request = require('request');
options = {
url: 'http://www.btc38.com/trade/getTradeList.php?coinname=BTC',
headers: {
'User-Agent': 'request x.y'
}
};
request(options, function(error, response, body) {
var cleaned = body.trim();
var json = JSON.parse(cleaned);
console.log(json.trade);
});
Output (truncated):
[ { price: '5069.000000',
volume: '0.494900',
time: '2013-12-15 16:05:44',
type: '2' },
{ price: '5069.000000',
volume: '0.230497',
time: '2013-12-15 16:02:37',
type: '2' },
{ price: '5100.000000',
volume: '0.058963',
time: '2013-12-15 15:58:27',
type: '1' },
{ price: '5100.000000',
volume: '0.099900',
time: '2013-12-15 15:58:27',
type: '1' },
{ price: '5099.000000',
volume: '0.344058',
time: '2013-12-15 15:56:58',
type: '1' },
{ price: '5069.000000',
volume: '0.027464',
time: '2013-12-15 15:55:35',
type: '2' } ... ]
Without seeing more of your code I won't be able to tell what's wrong, but I would suggest you use the request-json (npm install request-json) package for something like this.
I just ran the following in Node and got a response:
var request = require('request-json');
var client = request.newClient('http://www.btc38.com');
client.get('/trade/getTradeList.php?coinname=BTC', function(err,res,body) {
// body is a JSON object
return console.log(body);
});
I'm new to emberJS, and try to find out how to put data from JSON API to my model. So I have:
App.Store = DS.Store.extend({
revision: 12,
adapter : DS.RESTAdapter.create({
url : 'http://eu.battle.net/api/d3/profile/Alucard-2129/?callback=?'
}) });
App.Profiles = DS.Model.extend({
name: DS.attr("string"),
lastUpdated: DS.attr("string")});
App.ProfilesRoute = Ember.Route.extend({
model: function () {
return App.Profiles.find();
}});
But it does't work at all... I get error:
Uncaught TypeError: Object function () { ... } has no method 'find'
Please help...
As Ember is kind of new, it changes a lot. If you download the latest Ember build and a copy of the Ember Data beta plugin, you can do it this way:
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://eu.battle.net/api/d3/profile/Alucard-2129/?callback=?'
});
App.Profiles = DS.Model.extend({
name: DS.attr("string"),
lastUpdated: DS.attr("string")});
App.ProfilesRoute = Ember.Route.extend({
model: function() {
return this.store.find("profile");
}
});
All help you find online, is already outdated. At least, that's what I experienced. This page lists a lot of changes that probably will come in handy: https://github.com/emberjs/data/blob/master/TRANSITION.md#host-and-namespace-configuration
Good luck ;)
Probally you are using the ember-data version 1.0.0-beta. And your code is from the older version. Now instead of App.Profiles.find(); you need to use store.find('profiles');.
The full description of the transition from the older version to the 1.0.0-beta is described here
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://eu.battle.net/api/d3/profile/Alucard-2129/?callback=?'
});
App.Profiles = DS.Model.extend({
name: DS.attr("string"),
lastUpdated: DS.attr("string")
});
App.ProfilesRoute = Ember.Route.extend({
model: function() {
return this.store.find("profiles");
}
});
I hope it helps