I am attempting to build a front end web system that will run off of a third party sites database and administration console. The API seems to be entirely reliant upon GET calls, either requesting or altering information by targeting specific URLs.
The API returns XML, example:
<responseITEMs xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ITEMs>
<ITEM libraryid="e3712df592253fcb4" featured="false" releasedate="2017-24-01 00:00:00" code="ABC001" detail="Some text" name="Dummy One" displaytitle="Dummy One" keywords="" id="1fef760bc1d61c8c" status="active" lastupdated="2016-24-01 04:53:28"/>
<ITEM libraryid="e3712df592253fcb4" featured="false" releasedate="2017-24-01 00:00:00" code="ABC003" detail="Some text" name="Dummy Three" displaytitle="Dummy Three" keywords="" id="3e35wba1d9b32a45" status="active" lastupdated="2016-24-01 04:53:15"/>
<ITEM libraryid="e3712df592253fcb4" featured="false" releasedate="2017-24-01 00:00:00" code="ABC002" detail="Some text" name="Dummy Two" displaytitle="Dummy Two" keywords="" id="cca6f0cab9defe80" status="active" lastupdated="2017-24-01 01:57:37"/>
</ITEMs>
</responseITEMs>
I have not used EmberJs before, but it was suggested to me. I'm not sure it's possible to use with XML, so I currently have a PHP script running on a different local server that's calling a fixed API URL endpoint and converting the response to JSON:
$Json = json_encode(simplexml_load_string($data));
echo $Json;
The JSON I end up with looks like this:
ITEMs: {
ITEM: [
{
#attributes: {
libraryid: "e3712df592253fcb4",
featured: "false",
releasedate: "2017-24-01 00:00:00",
code: "ABC001",
detail: "Some text",
name: "Dummy One",
displaytitle: "Dummy One",
keywords: "",
id: "1fef760bc1d61c8c",
status: "active",
trackcount: "0",
lastupdated: "2016-24-01 04:53:28"
}
},
{
#attributes: {..... etc
I am trying to write an Ember normalizer that will mean that I can run a simple loop through the items (real term is not ITEM) on an Ember template. Currently it is:
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
payload = {
ITEM: {
id: payload.ITEMs.ITEM[0]["#attributes"].id,
type: requestType.modelName,
name: payload.ITEMs.ITEM[0]["#attributes"].name
}
};
return this._super(store, primaryModelClass, payload, id, requestType);
}
});
At this point Ember inspector shows that I am getting the ID and name under the Data tab, but I can't get them onto my template, or obviously retrieve more than the first item due to the hardcoded ITEM[0].
Route:
export default Ember.Route.extend({
model() {
return this.store.findAll('ITEM');
}
});
Model:
export default DS.Model.extend({
name: DS.attr('string'),
});
Template:
<strong>{{ model.name }}</strong>
<ul>
{{#each model.ITEM as |one|}}
<li>{{one.name}}</li>
{{/each}}
</ul>
Obviously I am not that far along and do not know Ember well at all. I am open to and would appreciate better solutions to tackling this as well as technical input towards my current code.
If you are using php-script, you can convert XML to correct response on PHP side, why use serializer for this?
Other solution would be creating new adapter and serializer to work with XML-responses without php-convertion.
You may find this link useful: https://bendyworks.com/blog/old-new-soap-ember-js
Related
I'm pulling some JSON with ajax (because I can't get past CORS with my limited understanding/lack of useful examples/ of the RESTAdapter...) - and I'm getting these nested objects. I need an array of the 'book' - but I'm unsure of how to format this JSON so that it's readable.
routes/books.js
import Ember from 'ember';
import ajax from 'ic-ajax';
var libraryThingApi = "http://www.librarything.com/api_getdata.php?";
export default Ember.Route.extend({
model: function() {
var libraryData = ajax({
url: libraryThingApi + "userid=F.L.O.W.",
type: 'GET',
dataType: 'jsonp'
});
console.log(libraryData);
return libraryData;
}
});
what is returned
Promise
_id: 47
_label: "ic-ajax: unwrap raw ajax response"
...
_result: Object
books: Object
111333266: Object
title: "a book title"
111730480: Object
title: "a book title"
settings: Object
theuser: "F.L.O.W."
more_things: "etc"
{
books: {
111601539: {
book_id: "111601539",
title: "Rosie Revere, Engineer",
author_fl: "Andrea Beaty",
copies: 1
},
121604536: {
book_id: "121604536",
title: "Ember for people who aren't core.",
author_fl: "No author",
copies: "This book does not exist"
}
},
settings: {
theuser: "sheriffderek"
}
}
Since you're just grabbing raw json, you can use it really however you'd like. It then just becomes a matter of how you want to access the data in your route's template.
Ember's model hook is promise aware so you are correct to simply return the promise. The fulfilled value of that promise is then what you'll be interested in for your template. Thus if the fulfilled value is something like this:
{
books: [
{id: 1, title: "some title", nested_data: {author: "some author"}},
{id: 2, title: "another title", nested_data: {author: "another author"}},
]
}
Then inside the template for your route you can access the fulfilled value from your returned promise inside the model hook. Pre Ember 1.10 (deprecated later in the 1.x series) you can each through your books in your route's template like this:
<ul>
{{#each book in model.books}}
<li>{{book.title}} by {{book.nested_data.author}}</li>
{{/each}}
</ul>
Ember 1.10 and beyond introduces block params for each statements:
<ul>
{{#each model.books as |book|}}
<li>{{book.title}} by {{book.nested_data.author}}</li>
{{/each}}
</ul>
Read the information about Ember 1.10 release here.
I am trying to do something that sounds simple but I can't find the solution.
My application needs to edit documents which contains pages.
Here is my model :
MyApplication.Document = DS.Model.extend({
title: DS.attr('string'),
pages: DS.hasMany('page', {async: true})
});
MyApplication.Page = DS.Model.extend({
document: DS.belongsTo('document', {async: true}),
title: DS.attr('string'),
params: DS.attr(),
objects: DS.attr()
});
And the routes :
MyApplication.Router.map(function () {
this.resource('document', {path: '/document/:document_id'});
});
MyApplication.Document = Ember.Route.extend({
model: function (params) {
return this.store.find('document', params.document_id);
}
});
When I load the document 1, the application call http://www.myserver.com/api/document/1.
The problem is that when I want to find a page of the document, it calls
http://www.myserver.com/api/pages/ID
instead of
http://www.myserver.com/api/document/1/pages/ID
Theses nested URL are important in my application.
I found different things on the subject like adding links in the JSON response :
{
"document": {
"id": "1",
"title": "Titre du document",
"pages": ["1", "2", "3"],
"links": {"pages" : "pages"}
},
But when I call for the pages, it requests http://www.myserver.com/api/document/1/pages without the id.
I also try specify the document when I ask for the page :
this.store.find("page", 1, {document:1});
Can't find a complete documentation on this subject, so if someone can explain me what's wrong, I'll be happy.
Thank.
Depends : EMBER DATA >= V1.0.0-BETA.9
The way to handle nested routes is hidden under release notes
Need to send back the links with response like this
{
"document": {
"id": 1,
"title": "Titre du document",
"links": {"pages" : "/documents/1/pages"}
}
You'll need to customize the adapter:page's buldUrl method like
MyApplication.PageAdapter = DS.RestAdapter.extend({
// NOTE: this is just a simple example, but you might actually need more customization when necessary
buildURL: function(type, id, snapshot) {
return '/documents/' + snapshot.record.get('document.id') + '/pages/' + id;
}
});
#code-jaff answer adapted to Ember 2.1.0:
// app/adapters/page.js
import DS from './application'; // I suppose you have your adapter/application.js
export default DS.RESTAdapter.extend({
buildURL: function(type, id, snapshot) {
return this.host + '/' + this.namespace + '/documents/' + snapshot.record.get('document.id') + '/pages/' + id;
}
});
Your problem likely stems from the quotes that are surrounding the IDs in your JSON. If you modify your serializer so that there are no quotes for the the IDs both around the document ID and the pages IDs, you should get the behavior that you expect. Also, you need to modify the formatting of your links to point to the relative path:
The resulting JSON should look like:
{
"document": {
"id": 1,
"title": "Titre du document",
"pages": [1, 2, 3],
"links": {"pages" : "/documents/1/pages"}
}
Please see this answer for a description of why adherence to Ember's expectations with regard to the JSON format is important and for an overview of the expected format.
While using the RESTAdapter, I have an Organization model which is to be embedded in the response. It appears that the default implementation of the Ember.RESTAddapter sends the id, using the same model name, but not as an object (this currently 'works'):
POST/PUT api/v1/item/{id}
{
"item" {
id: "1029383829"
...
organization: "26044097612186763401268824297"
}
}
I have consulted the documentation, and found that the mixin DS.EmbeddedRecordsMixin should do this, coupled with declaring embedded: "always" on the attrs or the Serializer:
models/item.js
var Item = DS.Model.extend({
...,
organization: DS.belongsTo("organization", {embedded: "always"})
});
serializers/item.js:
var ItemSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
organisation: {serialize: "id", embedded: "always"}
}
}
);
However, when deserializing records, Ember Data complains, saying that it expected an object, but just got a string:
Assertion Failed: Expected an object as data in a call to push for
app#model:organization: , but was 26044097612186763401268824297
Ultimately, I would prefer a system, likened to sideloading, wherein an additional attribute, post-fixed "_id", describes the corresponding id of an embedded record:
{
"item": {
id: 1,
name: "name",
organization_id: "26044097612186763401268824297"
...
}
}
How can I allow serializing and deserializing embedded id sideloading for my Organization model?
You aren't actually embedding the record, you're just specifying the id, in that case you should mark it as async.
var Item = DS.Model.extend({
...,
organization: DS.belongsTo("organization", {async: true})
});
And remove your embedded records implementation.
I created model Consultation:
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr('string'),
records: DS.hasMany('record', { async: true }),
currentUser: DS.belongsTo('user'),
remoteUser: DS.belongsTo('user')
});
And also I created model Record:
import DS from 'ember-data';
export default DS.Model.extend({
record_text: DS.attr('string'),
record_poster_id: DS.attr('string'),
record_consultation_id : DS.attr('number'),
consultation: DS.belongsTo('consultation'),
isMine: DS.attr('boolean')
});
At first all consultations load during opening page. And I don't want to load all records of each consultation during opening page. To do this I added async: true but all records loaded simultaneously sending many requests like /consultations/:id/records. After that consultation and records still non-joined. I have next json response for consultation:
{
"consultations":[
{
"id":140721,
"title":"ExpertId: 41217, clientId: 0",
"links":{
"records":"records"
},
"currentUser":41217,
"remoteUser":159984
},
......
]
}
And for records:
{
"records":[
{
"record_id":681952,
"record_consultation_id":140721,
"record_poster_id":0,
"record_text":"1",
},
........
]
}
I think I need to override default serializer. I tried to create serializer for Record:
import DS from 'ember-data';
export default DS.RESTSerializer.extend({
primaryKey: 'record_id',
keyForRelationship: function(key, kind) {
return 'record_consultation_id';
}
});
But it still doesn't work.
Please advise how to join models using links?
UPDATE:
Template
{{#each item in model.records}}
<div class="table message">
{{item.record_text}}
</div>
{{/each}}
I am doing Async (lazy loading) of data using RESTAdaptor and Ember-Data too.
For my links area, i put in the full request URL as follows:
"links":{
"records":"/consultations/140721/records"
},
And using firebug to look at when the request gets sent off, its only when I request for the async content that the AJAX gets fired off.
model.get('records');
Can you provide your Controllers & Template code so I can see how your accessing the Records?
Using Ember-data and Ember.js, I'm trying to load two models with one JSON request. The models have a relationship analogous to this:
App.Person = DS.Model.extend({
name: DS.attr('string'),
dogs: DS.hasMany('App.Dog'),
});
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person'),
});
My server is sending JSON like this:
{
"dog": {
"id": 1,
"name": "Fido",
"owner": {
"id": 1,
"name": "John Smith",
"dogs": [1]
}
}
}
…And yet, Ember-data still sends a request (using findQuery) to my server trying to get the owner JSON.
I have a jsFiddle set up that demonstrates it here. To watch the problem happen, you'll need to go to this link to activate the route/template:
http://fiddle.jshell.net/6kQ8s/2/show/#/dog/1
I haven't defined findQuery() in my adapter on purpose because I shouldn't need that to get data that I have already sent… Right?
Does anyone know what I'm doing wrong here?
I'm doing the following (using ember-data revision 8)
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person', { embedded: true }),
});
Additionally, I have to tell the serializer to load a mapping for this relation.
Though it's not required, I'm using my own DS.Serializer subclass. At initialisation
time the serializer loads a mapping for the Person class, which specifies that
embedded relationships should be loaded.
App.WOSerializer = DS.Serializer.extend({
init: function(){
this._super();
this.map(App.Dog, {
person: {
embedded: 'load'
}
});
});
Edit by question asker:
The serializer needed to be initialized in the adapter.
App.adapter = DS.Adapter.create({
// ...
serializer: App.WOSerializer.create()
});
Try use embedded property.
App.Dog = DS.Model.extend({
name: DS.attr('string'),
owner: DS.belongsTo('App.Person', { embedded: true }),
});