I receive the following error when I use Ember Data to create records from a JSON response. What gives? I am following what the docs state.
Uncaught Error: Assertion Failed: Ember Data expected a number or string to represent the record(s) in the `user` relationship instead it found an object. If this is a polymorphic relationship please specify a `type` key. If this is an embedded relationship please include the `DS.EmbeddedRecordsMixin` and specify the `user` property in your serializer's attrs object.
JSON being parsed:
[
{
"id": 76,
"title": "Title",
"shipped": 0,
"date": "2015-05-21T05:00:00.000Z",
"user": {
"firstName": "First Name",
"lastName": "Last Name",
"email": "hellothere#gmail.com",
"id": 1
}
}
]
Shipment Model:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
user: DS.belongsTo('user', { async: false })
});
Route:
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel: function() {
if(!localStorage.accessToken) {
this.transitionTo('login');
}
},
model: function() {
var shipmentObjects = [];
var App = this;
Ember.$.getJSON('http://localhost:1337/subscription/1/shipments/upcoming', function(shipments) {
shipments.forEach(function(data) {
var shipment = App.store.push('shipment', data);
shipmentObjects.pushObject(shipment);
});
});
return shipmentObjects;
}
});
You can create a custom serializer, if you can't modify your json response and manage to arrange data in other way
App.MODELNAMESerializer = DS.ActiveModelSerializer.extend({
extract: function(store, type, payload, id, requestType){
var shipments = [];
//CREATE A NEW PAYLOAD THAT EMBER CAN READ
var _payload = { };
return this._super(store, type, _payload, id, requestType);
}
});
Your json should look something like this
{
shipments: [
{
"id": 76,
"title": "Title",
"shipped": 0,
"date": "2015-05-21T05:00:00.000Z",
"user_id": 1,
}
],
"users": [
{
"firstName": "First Name",
"lastName": "Last Name",
"email": "hellothere#gmail.com",
"id": 1
}
]
}
Read the error message. It could hardly be clearer. By default, Ember Data expects an association to be represented by an ID. If the association is instead embedded, you must tell Ember Data that. You'll need something like:
// serializers/shipment.js
export default ApplicationSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
user: { embedded: 'always' }
}
});
And remove the {async: false}, since the data is embedded right there.
See http://emberjs.com/api/data/classes/DS.EmbeddedRecordsMixin.html.
Related
I have a json file that contains content for several different pages that are under a "service" category. I use dynamic routes in nextJS by having a file as "[serviceId].tsx", this routing works. However I have a json file where I want to use the [serviceId] provided in the route to access information.
I have the following code in my [serviceId].tsx file:
const json = jsonFile.services
const router = useRouter()
const serviceId = router.query.serviceId
return (
<div>
<ArticleWithPicture title={content.title} description={content.description}/>
</div>
)
}
My json file looks similar to this (ive edited it to be more clear for this example):
{
"serviceId":
[
{
"service1": {
"id": "xx",
"title": "xxx",
"description": "xx",
"featuredCompany":
[
{ "id": "1",
"name": "xxx",
"companyPageURL": "/",
"imagePath": "xxx",
"description": "xxx",
"additionalServices": {
"service1": "xxx",
"service2": "xxx"
},
"instagramURL":"/",
"twitterURL": "/"
}
]
}
},
{
"service2": {
"id": "xxx",
"title": "xxx",
"description": "xxx",
"featuredCompany":
[
{ "id": "1",
"name": "xxx",
"companyPageURL": "/",
"imagePath": "xxx",
"description": "xxx",
"additionalServices": {
"service1": "xxx",
"service2": "xx"
},
"instagramURL":"/",
"twitterURL": "/"
}
]
}
}
]
}
Basically, each Service has the content for each indiviual page. So I want to dynamically set for instance the title of my component "ArticleWithPicture" based on the corresponding title in my json file based on the serviceId that I get from router.query.serviceId. However when I try the following code:
<ArticleWithPicture title={json.{serviceId}.title}/>
I get error (this is due to how I use "{}" within a "{}", is there a way to do this better?
But I also cannot access it if I do eg:
const title = json.serviceId.title
or (what is what I actually want to do ie: query the json file based on my serviceId provided by "router.query.serviceId")
const title = json.{serviceId}.title
I guess something might be wrong with either my json file structure or how I try to access it. Any advice would be appreciated.
Thanks!
I'm assuming the JSON you provided is your entire jsonFile.
If the JSON you provided is just jsonFile.services, then change any uses of jsonFile to jsonFile.services
and update the type.
The format of the JSON isn't great for your use case because there's a lot of unnecessary wrapping.
With your current JSON
Assuming you cannot modify the format of the JSON, you would have to find the service from the serviceId array:
function getService(json, serviceId) {
return json.serviceId
.find((serviceWrapper) => serviceWrapper[serviceId] !== undefined)
?.service;
}
A fully typed example:
type Service = {
id: string
title: string
description: string
// ...
};
// you don't have to use this, I just included it for clarity
type JsonFile = {
serviceId: {
[serviceId: string]: Service
}[]
};
function getService(json: JsonFile, serviceId: string): Service | undefined {
return json.serviceId
.find((serviceWrapper) => serviceWrapper[serviceId] !== undefined)
?.service;
}
// declare const jsonFile: JsonFile;
export default function ServicePage() {
const router = useRouter();
const serviceId = router.query.serviceId as string;
const content = getService(jsonFile, serviceId);
if (!content) return (
<div>
{'Article doesn\'t exist.'}
</div>
);
return (
<div>
<ArticleWithPicture title={content.title} description={content.description} />
</div>
);
}
With better JSON
An example JSON that would need less unwrapping is:
{
"service1": {
"id": "xx",
"title": "xxx",
// ...
},
"service2": {
"id": "xxx",
"title": "xxx",
// ...
}
}
type JsonFile = {
[serviceId: string]: Service
}
Then you would be able to just do jsonFile[serviceId] or jsonFile.services[serviceId] to get a service.
I have formData node that has dynamic jsonObject value in mongodb
{
"_id": {
"$oid": "5a71fea0f36d2848ae4f8f0a"
},
"formData": {
"pages": [
{
"name": "page1",
"questions": [
{
"type": "comment",
"name": "about",
"title": "Please tell us about your main requirements "
}
]
}
]
},
"editorId": "59678e58f36d2842f777bc48",
"datetimeSubmit": "2018/01/15"
}
I write a node API to fetch the data from mongodb, it only display ids, editorI and datetimesubmit nodes, but it ignores the formData(jsondata) field.
const Model = require('./myModel');
module.exports = {
getData: function (callback) {
Model.find({}, (err, jsonObj) => {
callback(null, {
data: jsonObj
})
})
}
}
looks like the model.find() doesn't return jsonObject value?
thanks
got my own question fixed, basically, i should also define the data type as JSON in schema, otherwise, will be ignored.
I'm using express.js and sequelize.js to build an API. Once I retrieved an object from the DB using sequelize, I want to
filter out object attributes (e.g. retrieve the user, but don't render the User's password hash to the returned JSON)
add new object attributes
before I return it from the API as JSON.
Similar to what these Rails libraries do:
Roar
Grape Entity
What's the most common framework to do that in node? Or do sequelize.js / express.js contain functionality to do that?
UPDATE
Ok, there is a basic example, passport.js gets the authenticated user's object from the DB and attaches it to req.user;
router.get('/me/data',
passport.authenticate('bearer', { session: false }),
function(req, res) {
res.status(200).send(req.user);
}
);
That would return the following JSON response:
{
"id": 24,
"first_name": "John",
"last_name": "Doe",
"email": "mymail#example.com",
"password": "8d23cb9c4827bc06bb30ac47c06af0efbdbeb575001ab7de5387da4085f7184a381335c0f04b45f4a40e5a7042d47ae1e2d29d28fd5be1d534f09ba3db04e8ca",
"updatedAt": "2016-01-25T09:19:07.422Z",
"createdAt": "2016-01-25T09:19:07.422Z",
"data": null
}
But I want to return something like this:
{
"id": 24,
"full_name": "John Doe",
"email": "mymail#example.com",
"data": null
}
And not just for this one case, but in any case a user object is rendered.
The simplest solution would be to edit req.user before sending it:
function
render (user) {
callback({
"id": user.id,
"full_name": user.first_name + ' ' + user.last_name,
"email": user.email,
"data": null
});
}
router.get('/me/data',
passport.authenticate('bearer', { session: false }),
function(req, res) {
render(req.user, function(user) {
res.status(200).send(user);
});
}
);
This module will helper you with Grape Entity manner:
https://github.com/baijijs/entity
For example:
const Entity = require('baiji-entity');
const userEntity = new Entity({
id: true,
fullname: true,
email: true,
data: true
});
userEntity.parse(req.user);
// And the parsed result will be as below
{
"id": 24,
"full_name": "John Doe",
"email": "mymail#example.com",
"data": null
}
Hope this can help you.
Ok so you would like to chop of some of the fields? You could chop it of before the server is sending the response. For example with a module like this https://www.npmjs.com/package/json-chop or if you use mongo db you can hide the fields like described here https://docs.mongodb.org/manual/tutorial/project-fields-from-query-results/
Without knowing more details about the setup, the following (untested) code has the potential to work. You could specify the attributes field in your query against the model in order to filter out results (based on certain conditions):
router.get('me/data',function(req, res) {
db.UserModel.find({
attributes:['id','first_name','last_name','email','data'],
where: { id: req.user.id}, //put in here whatever you are using to get the info from the database
})
.then (function(user) {
var formatUser = {
id: user.id,
full_name: user.first_name + ' ' + user.last_name,
email: user.email,
data: user.data,
};
res.json(formatUser);
}
});
See the following docs for more information:
http://docs.sequelizejs.com/en/latest/docs/models-usage/
http://docs.sequelizejs.com/en/latest/docs/querying/
making first attempt to master Ember.js atm. I'm trying to make a simple CMS, but for some reason I have a problem with getting any data from json displayed. I've already switched from Fixture to RESTAdapter, but I am still stuck with
Error: assertion failed: Your server returned a hash with the key timestamp but you have no mapping for it.
Here's my js code:
App.Store = DS.Store.extend(
{
revision:12,
adapter: 'DS.RESTAdapter'
}
);
App.Menucategory = DS.Model.extend({
timestamp: DS.attr('number'),
status: DS.attr('string')
});
App.MenucategoryRoute = Ember.Route.extend({
model: function() {
return App.Menucategory.find();
}
});
DS.RESTAdapter.reopen({
url: <my url>
});
DS.RESTAdapter.configure("plurals", {
menucategory: "menucategory"
});
Trying to access it with:
<script type="text/x-handlebars" id="menucategory">
{{#each model}}
{{data}}
{{/each}}
</script>
My json structure:
{
"timestamp": 1366106783,
"status": "OK",
"data": [
{
"name": "starters",
"id": 1
},
{
"name": "main dishes",
"id": 2
}]}
Thank you in advance for any help you can provide.
By default the RESTAdapter expects your API to be in a specific format, which your API doesn't conform to, if you can modify your API, you need to get it to return in this format, otherwise you'll need to customize the adapter.
Expected Format (based on your JSON):
{
"menucategory": [
{
"name": "starters",
"id": 1
},
{
"name": "main dishes",
"id": 2
}
],
"meta": {
"timestamp": 1366106783,
"status": "OK",
}
}
I'm having trouble parsing a json to a model.
Here is the JSON:
[
{
"name": "Douglas Crockford",
"email": "example#gmail.com",
"_id": "50f5f5d4014e045f000002",
"__v": 0,
"items": [
{
"cena1": "Cena1",
"cena2": "Cena2",
"cena3": Cena3,
"cena4": "Cena4",
"cena5": "Cena5",
"cena6": Cena6,
"_id": "50ee3e782a3d30fe020001"
}
]
}
]
And i need a model to have the 'items' attributes like this:
cena = new Model({
cena1: "Cena1",
cena2: "Cena2",
...
});
What I've tried:
var cenaCollection = new Backbone.Collection.extend({
model: Cenas,
url: '/orders',
parse: function (response) {
return this.model = response.items;
}
});
then I create new instance of the collection and fetch, but i get "response.items" always "undefined" :|
Thanks in advance!
The parse function should return the attributes hash to be set on the model (see the documentation here). So you'll need simply:
parse: function (response) {
return response[0].items;
}