I searched for a clear explanation on how this works but was not able to find one yet. I'm looking for a clear and detailed explanation with the assumption that I'm a total newbie with all these frameworks.
So here's my problem, I'm writing an app with frontend using Ember.js (with Ember-cli) and backend under Play-framework in Java. I'm trying to get my frontend to digest some json coming out from my API. Here's the json :
{
"buildings": [
{
"id": 0,
"createdDateTime": "2015-03-27T06:39:19.913Z",
"address": {
"id": 1,
"city": "City",
"civicNumber": 1287,
"country": "Canada",
"postalZipCode": "G1Q1Q9",
"provinceOrState": "QC",
"streetName": "A Street Name"
},
"cost": 1000000,
"earnings": 2300,
"mainPicturePath": "http://dummyimage.com/200x200/999/fff.png&text=Building",
"type": "DUPLEX",
"yearOfBuilt": 1998
},
{
"id": 1,
"createdDateTime": "2015-03-27T06:39:19.935Z",
"address": {
"id": 2,
"city": "City",
"civicNumber": 1289,
"country": "Canada",
"postalZipCode": "G1Q1Q9",
"provinceOrState": "QC",
"streetName": "A Street Name"
},
"cost": 980000,
"earnings": 670,
"mainPicturePath": "http://dummyimage.com/200x200/999/fff.png&text=Building",
"type": "TRIPLEX",
"yearOfBuilt": 1980
}]
}
And here's my emberjs code :
//models/building.js
export default DS.Model.extend({
type: DS.attr('string'),
address: DS.belongsTo('address', {embedded: 'always'}),
cost: DS.attr('number'),
yearOfBuilt: DS.attr('number'),
earnings: DS.attr('number'),
createdDateTime: DS.attr('date'),
mainPicturePath: DS.attr('string')
});
//models/address.js
export default DS.Model.extend({
civicNumber: DS.attr('number'),
streetName: DS.attr('string'),
city: DS.attr('string'),
provinceOrState: DS.attr('string'),
country: DS.attr('string'),
postalZipCode: DS.attr('string'),
building: DS.belongsTo('building', {embedded: 'always'})
});
//serializers/building.js
export default DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
address: {embedded: 'always'}
}
});
This doesn't throw any errors and works fine. The problem is, I don't fully understand the code for the embedded object address in building (I do understand how the DS models, the export from ember-cli and the belongsTo work). Could someone explains me in details how does it works (the RESTSerializer with EmbeddedRecordsMixin, the {embedded: 'always'} attribute and the other available options)? Any clarifications will be more than appreciated.
Lets also bring this to the next level, say I do not want to have an id for each address since they should never be used more than once (cannot have 2 buildings at the same address). How would I achieve that? Maybe I will store the address in the same record of my building object in the db and don't want an extra table for addresses. Based on these solutions, what would be the best approach (feel free to propose better solution if you have)?
Please be advised that I have already read the following links :
Ember-data: deserialize embedded model
http://mozmonkey.com/2013/12/loading-json-with-embedded-records-into-ember-data-1-0-0-beta/ (which doesn't say anything about EmbeddedRecordsMixin)
How to access nested object in json with Ember data (talking about the App.Adapter.Map which I don't understand either)
https://github.com/lytics/ember-data.model-fragments (Very interesting project that may quit be exactly what I'm looking for)
Thank you!
The answers to most of your questions can be found by closely reading the EmbeddedRecordsMixin Docs
Specifically:
using { embedded: 'always' } as an option to DS.attr is not a valid
way to setup embedded records.
(meaning as long as you've defined your belongsTo relationship you're good)
and
The attrs option for a resource { embedded: 'always' } is shorthand
for:
1 {
2 serialize: 'records',
3 deserialize: 'records'
4 }
But the real answer is, it's all in the code! Go read here and a little bit further down here, it's written pretty well and you should be able to trace what's going on.
Basically what happens is once you tell the serializer that there are embedded records (via {key: {embedded: 'always' } }), it will find that key in your JSON and deserialize it into an Ember object (as long as it finds a defined Ember object with that key, in your case 'address').
As far as your next level questions, I'd respond with my own question: Do your buildings have tenants? If so, they will probably have addresses, and you'll likely want to access it through tenant.address, rather than tenant.building.address, so go ahead and make address it's own table. It will likely save you some headaches in the near future.
Related
I'm using the latest version of EmberJS and Ember Data.
I have the next JSON data:
[{
"id": 6,
"name": "First object",
"vol": 40,
"description": "bla bla bla",
"category": "first"
}, {
"id": 7,
"name": "Second object",
"vol": 17,
"description": "Some description",
"category": "second"
}]
And the next model:
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
vol: DS.attr('number'),
description: DS.attr('string'),
category: DS.attr('string')
});
And I don't understand how to make Ember data works. I have suspicious that ember data expects something like next:
[{
model-name {
"id": 6,
"name": "First object",
"vol": 40,
"description": "bla bla bla",
"category": "first"
}
}, {
model-name {
"id": 7,
"name": "Second object",
"vol": 17,
"description": "Some description",
"category": "second"
}
}]
In console I have a bunch of warnings:
WARNING: Encountered "0" in payload, but no model was found for model
name "0" (resolved model name using emdber-drink-
it#serializer:application:.modelNameFromPayloadKey("0"))
WARNING: Encountered "1" in payload, but no model was found for model
name "1" (resolved model name using emdber-drink-
it#serializer:application:.modelNameFromPayloadKey("1"))
And more than hundred similar records (that is how many records server returns on request).
And no data in store.
How can I fix this problem without changing JSON that I receive from server?
From http://emberjs.com/api/data/classes/DS.JSONSerializer.html
In Ember Data a Serializer is used to serialize and deserialize
records when they are transferred in and out of an external source.
This process involves normalizing property names, transforming
attribute values and serializing relationships.
At this time there are 4 types of serializers:
DS.Serializer
DS.JSONSerializer
DS.RESTSerializer
DS.JSONAPISerializer
The JSON data you have is plain json.
JSONSerializer is useful for simpler or legacy backends that may not
support the http://jsonapi.org/ spec
So you need JSONSerializer which is the most simple solution to consume plain json data.
With ember-cli is pretty easy create a new serializer, for example for a model book:
ember generate serializer book
which will produce something like this:
version: 1.13.8
installing serializer
create app/serializers/book.js
installing serializer-test
create tests/unit/serializers/book-test.js
The previous command will create a serializer of type RESTSerializer. (If you dont use ember -cli just create this file at hand)
// app/serializers/book.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
});
and then change RESTSerializer by JSONSerializer:
import DS from 'ember-data';
export default DS.JSONSerializer.extend({
primaryKey: '_id', // useful for mongodb-like data
});
I hope you enjoy learning ember as much as I do.
You should be able to add a custom serializer to add a root key to your JSON data. I haven't tested this but you could try something like:
(using a "book" model as an example)
// app/serializers/book.js
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
extractArray: function(store, type, payload) {
var newPayload = {};
newPayload[type.modelName] = payload;
return this._super(store, type, newPayload);
}
});
If this is a problem with the entire API, then you might need to make a custom serializer for the entire application and use the passed-in type argument to build the root keys.
Also as a side note, I probably wouldn't use Ember Data if I didn't have control over the API. Unless it follows strict guidelines like jsonapi.org, you're bound to have a few headaches with it.
EDIT - changed answer to the suggestion above (using 'type' to build root key).
I took another approach and overloaded the 'modelNameFromPayloadKey' method.
If the payload key is of type integer, it will be mapped to the name of the model instead.
// app/serializers/<model>.js
import DS from 'ember-data';
import normalizeModelName from "ember-data/-private/system/normalize-model-name";
import {singularize} from "ember-inflector";
export default DS.RESTSerializer.extend({
model: '<model>',
modelNameFromPayloadKey(key) {
// Check whether the key is of type integer
if (!isNaN(key)) {
return normalizeModelName(this.get('model'));
}
return singularize(normalizeModelName(key));
},
});
Where can i find the list of all possible variable that is possible to set in play application.conf ?
I can't find this information on playframework website.
Thank you
If you use IDE such as eclipse or IntelliJ, you can inspect Play.application().configuration() at runtime while debugging and it will contain all possble configuration key/value pairs. It briefly looks as follows:
{
"akka":{ },
"application":{ },
"applyEvolutions":{ },
"awt":{ },
"db":{ },
"dbplugin":"disabled",
"evolutionplugin":"enabled",
"file":{ },
"java":{ },
"jline":{ },
"line":{ },
"logger":{ },
"os":{ },
"path":{ },
"play":{ },
"promise":{ },
"report":{ },
"sbt":{ },
"sun":{ },
"user":{ }
}
There is no such list of all possible variables, since the application.conf is arbitrarily extensible by all sorts of tools and components, most of them third party, and can contain any config the user wants.
For example: the configuration detailing Play's thread pools is really just Akka configuration.
The key things (DB config, languages, evolutions) are in the template, either with default values or commented out, when you initialise a new Play application.
The config page on the site discusses some additional configuration you might need, but this mostly relates to concerns external to the application, like launching and logging.
I'm writing an external app that loads it's data from a drupal site, the site implemented "services" module which returns JSON representation of a node and (almost) all of it's data.
the one thing the service does not return is the url of the requested node,
so I'm calling this:
[domain]/rest/mynode/ffad3c26-e684-4cca-9c4b-16334f9f3dc1.json
and getting this:
{
vid: "2087",
uid: "67133de7-9755-424d-b45b-c4bbf41cb00e",
title: "PD300",
log: "Updated by FeedsNodeProcessor",
status: "1",
comment: "0",
promote: "0",
sticky: "0",
vuuid: "097f43ef-e22c-4f35-b74d-02b8e8ac5895",
nid: "2087",
type: "product",
language: "en"... and some more
I also need the url for the current node so I can link back to it from the app, any idea?
I think you just need to concatenate in your application the string something like that:
URL= "[domain]"."/node/"."{nid}"
I've created translation file, validated it at jsonlint, ensured the translation file was located at /locales/translation-en.json.
I consistently get the error,
There is a typo in: locales/translation-en.json
I'm stumped ... here's the translation json I have.
{
"tab":{
"legionella":"LEGIONELLA",
"logbook":"LOGBOOK"
},
"representative":{
"tag":"Representative: __rep__ — Phone: __phone__ — ",
"email":"Click here to email your rep"
},
"portlet":{
"contacts":{
"title":"Contacts",
"type":"Contact<br>Type",
"name":"Contact<br>Name",
"phone":"Phone<br>Number",
"type_context_1":"Owner",
"type_context_2":"Maintenance",
"type_context_3":"Other"
},
"samples":{
"title":"Legionella Samples",
"sampleDate":"Sample<br>Date",
"transmitForm":"Transmittal<br>Form",
"certOfAnalysis":"Certificate<br>of Analysis",
"concentration":"Concentration<br>(UFC/L)",
"correctAction":"Corrective<br>Action",
"range_context_1":"Interference",
"range_context_2":"Less than 10,000 UFC/L",
"range_context_3":"Between 10,000 to 1,000,000 UFC/L",
"range_context_4":"Greater than 1,000,000 UFC/L"
},
"serviceReports":{
"title":"Service Reports",
"date":"Report<br>Date"
},
"maintenance":{
"title":"Maintenance Programs",
"popup":"Create New Maintenance Program",
"type":"Program<br>Type",
"date":"Effective<br>Date",
"document":"Program<br>Document",
"type_context_1":"Water Treatment",
"type_context_2":"Mechanical",
"type_context_3":"Schematic",
"type_context_4":"O&M Manual",
"popup_type":"Type",
"popup_date":"Effective Date",
"popup_document":"Document",
"popup_save":"Save Maintenance Program"
},
"history":{
"title":"System History",
"popup":"Create New System History Entry",
"date":"Event<br>Date",
"type":"Event<br>Type",
"details":"Event<br>Details",
"type_context_1":"Breakage",
"type_context_2":"Repair",
"type_context_3":"Decontamination",
"type_context_4":"Replacement"
},
"reminders":{
"title":"Reminders",
"date":"Date",
"description":"Description"
},
"emails":{
"title":"Emails",
"date":"Date",
"subject":"Subject",
"recipient":"Recipient"
}
},
"common":{
"view":"View",
"registryList":"Registry: ",
"signout":"Sign Out"
}
}
So, I found the issue. I didn't give ALL the details required in the original question. What I failed to let everyone know was that the json file resided in Netsuite. Netsuite doesn't like serving up .json files. When I converted it to .json.txt, all was well in the world. Thanks!
Just in the event someone missed the obvious..
JSON, unlike Javascipt, requires the keys be quoted.
Valid JSON:
{ "foo": "bar" }
Invalid JSON:
{ foo: "bar" }
Since this question pops high in Google when search for "i18next there is a typo in", here's what you can do.
Use a JSON validation tool. If it's not valid, correct your mistakes and try again.
I have truly searched and I have not found a decent example of using the serializer to get objects from a differently formatted JSON response. My reason for not changing the format of the JSON response is outlined here http://flask.pocoo.org/docs/security/#json-security.
I'm not very good with javascript yet so I had a hard time understanding the hooks in the serialize_json.js or maybe I should be using mapping (I just don't know). So here is an example of my JSON response for many objects:
{
"total_pages": 1,
"objects": [
{
"is_completed": true,
"id": 1,
"title": "I need to eat"
},
{
"is_completed": false,
"id": 2,
"title": "Hey does this work"
},
{
"is_completed": false,
"id": 3,
"title": "Go to sleep"
},
],
"num_results": 3,
"page": 1
}
When ember-data tries to use this I get the following error:
DEBUG: -------------------------------
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.1
DEBUG: -------------------------------
Uncaught Error: assertion failed: Your server returned a hash with the key total_pages but you have no mapping for it
Which totally makes when you look at my code for the data store:
Todos.Store = DS.Store.extend({
revision: 12,
adapter: DS.RESTAdapter.create({
mappings: {objects: "Todos.Todo"},
namespace: 'api'
})
});
My question is how do I deal with total_pages, num_results and page? And by deal, I mean ignore so I can just map the objects array.
All root properties you return in your JSON result are mapped to a DS.Model in Ember Data. You should not return properties that are not modelled or you should model them.
If you want to get rid of the error you should make an empty model for the properties you don't use.
Read more here
Why are you returning properties you don't want to use? Or is it out of your control?
The way to accomplish this is with a custom serializer. If all your data is returned from the server in this format you could simply create ApplicationSerializer like this:
DS.RESTSerilizer.extend({
normalizePayload: function(type, payload) {
delete payload.total_pages;
delete payload.num_results;
delete payload.page;
return payload;
}
});
That should allow Ember Data to consume your API seamlessly.
Ember is fairly opinionated about how things are done. Ember data is no exception. The Ember team works towards certain standards that it thinks is best, which is, in my opinion, a good thing.
Check out this post on where ember is going. TL;DR because there are so many varying implementations of api calls, they're setting their efforts towards supporting the JSON API.
From my understanding, there is no easy way to do what you're asking. Your best bet is to write your own custom adapter and serialized. This shouldn't be too hard to do, and has been done before. I recommend you having a look at the Tastypie adapter used for Python's Django Tastypie