I have an issue when trying to display a list of objects where each object has nested objects and there is even another level of objects in those objects. The API-respons gives me this (simplified) JSON-data where there are many freight orders:
{
"freightOrders": [{
"id": 1,
"comment": "Freight order comment",
"shipments": [{
"id": 1,
"shipment_lines": [{
"id": 1,
"description": "A description",
"package_weight": 900,
"package_length": 1200,
"package_height": 400,
"package_width": 800
}],
"pickup_address": {
"id": 1,
"address": "The pickup address",
"zip": "9000"
},
"delivery_address": {
"id": 2,
"address": "The delivery address",
"zip": "8000"
},
}],
}]
}
What I want is to display a list of all freight orders, and for the time being, access directly the first shipments-line for each order. In Handlebars I have tried
{{#each model as |order|}}
<span>
{{order.shipments.0.pickup_address.address}},
{{order.shipments.0.pickup_address.zip}}
</span>
{{/each}}
and
{{#each model as |order|}}
{{#each order.shipments as |shipment|}}
<span>
{{shipment.pickup_address.address}},
{{shipment.pickup_address.zip}}
</span>
{{/each}}
{{/each}}
Edit: Here is the order model as requested:
import DS from 'ember-data';
export default DS.Model.extend({
comment: DS.attr('string'),
shipments: DS.hasMany('shipment', { inverse: null })
});
and the shipment model for good measure:
import DS from 'ember-data';
export default DS.Model.extend({
pickup_address: DS.belongsTo('address', { inverse: null }),
delivery_address: DS.belongsTo('address', { inverse: null }),
shipment_lines: DS.hasMany('shipment-line', { inverse: null })
});
Whatever I try to do, I am not able to access shipments element or any nested objects of the order object.
I can mention that I have also tried to create the shipments part as a component and pass order.shipments to the component, but to no prevail.
Searching SO and google does not reveal any hints only some examples of how to do nested each in ember 1.x
So, how can one access nested objects in an each-loop in Handlebars and Ember Data?
I don't know if I got enough information, but let's start with an observation:
The JsonApi spec describes the use of hyphens instead of underscores. So your payload should be shipment-lines (etc). Ember uses the JsonApi as default, so you should follow this, or fix it with serializers.
For example:
export default DS.JSONAPISerializer.extend({
keyForAttribute: function(attr, method) {
return Ember.String.underscore(attr);
}
});
Note that ember 'understands' that the underscores should be capitalized in your model. Your payload could be enhanced to look look this [1]:
{
"freightOrders": [{
"id": 1,
"comment": "Freight order comment",
"shipments": [{
"id": 1,
"shipment-lines": [{
"id": 1,
"description": "A description",
"package-weight": 900,
"package-length": 1200,
"package-height": 400,
"package-width": 800
}],
"pickup-address": {
"id": 1,
"address": "The pickup address",
"zip": "9000"
},
"delivery-address": {
"id": 2,
"address": "The delivery address",
"zip": "8000"
},
}],
}]
}
And your shipment model:
import DS from 'ember-data';
export default DS.Model.extend({
pickupAddress: DS.belongsTo('address', { inverse: null }),
deliveryAddress: DS.belongsTo('address', { inverse: null }),
shipmentLines: DS.hasMany('shipment-line', { inverse: null })
});
In your template you should be able to do a simple loop:
{{#each model.shipments as |shipment|}}
<span>{{shipment.pickupAddress.address}}</span>
{{/each}}
[1] Better would be if you use attributes and relations in your payload to be full JSON API compliant, see for more info: http://jsonapi.org/
Related
I have a model named 'someModel' is as follows
import DS from 'ember-data';
export default DS.Model.extend({
type: DS.attr('string'),
email: DS.attr('string'),
days: DS.attr('number'),
isInstant: DS.attr('boolean'),
cutHours: DS.attr('number')
});
and by default I need an array of 4 records of this model with default values as follows
"someModel": [{
"type": "Booking",
"email": "sunny.wayne#some.com,mahela.jayawardane#some.com",
"isInstant": true,
"cutHours": 72,
"days": -1
},
{
"type": "Booking",
"email": "",
"isInstant": false,
"cutHours": 72,
"days": -1
},
{
"type": "Arrival",
"email": "mahela.jayawardane#some.com",
"isInstant": false,
"cutHours": 72,
"days": 1
},
{
"type": "Cancellation",
"email": "sunny.wayne#some.com",
"isInstant": false,
"cutHours": 72,
"days": -1
}
]
Whats the ideal way to do this? I looked at the createRecord, but only one record can be created at a time. My backend logic expecting the data to be in the above format and I will also have to update the 4 records that are created depending on the user action.
Wrap your "someModel" with another model in a hasMany relationship.
Create another model, lets call it "parentModel" for our understanding.
In its model definition, add the "someModel" as a property with hasMany relation.
parentModel.js
export default DS.Model.extend({
someModel: DS.attr('someModel')
});
Now create a parentModel record and add as many as someModel records to it and save the parentModel.
let pModel = this.get("store").createRecord("parentModel");
pModel.get("someModel").addObject(this.get("store").createRecord("someModel", {
type: "Booking",
email: "sunny.wayne#some.com,mahela.jayawardane#some.com",
isInstant: true,
cutHours: 72,
days: -1
}));
pModel.save();
Looking at the JSON structure that you had provided, you need to use JSONSerializer and EmbeddedRecordsMixin to get the someModel json in your response.
Also make sure that "id" are retured for record of someModel in the response.
The request formed by ember would be like this
{
"someModel": [{
"type": "Booking",
"email": "sunny.wayne#some.com,mahela.jayawardane#some.com",
"isInstant": true,
"cutHours": 72,
"days": -1
}]
}
I'm using RESTAdapter and trying to figure out how to access sideloaded data.
A sample of the payload is:
{
"category": {
"categoryName": "test category",
"id": 6,
"products": [
4419,
502,
3992
]
},
"products": [{
"description": "Whatevs",
"id": 4419,
"name": "Product 1",
"price": 114.95,
"skuid": "S21046"
}, {
"description": "Whatevs",
"id": 502,
"name": "Product 2",
"price": 114.95,
"skuid": "SOLS2594"
}, {
"description": "Whatevs",
"id": 3992,
"name": "Product 3",
"price": 114.95,
"skuid": "S21015"
}]
}
I can see 'category' and 'product' data models (and data) in the ember inspector, so I know they are being loaded.
I can even access the products in the template model.products. BUT I can't access model.products in the route's setupController. The error I get is:
TypeError: Cannot read property '_relationships' of undefined
This is really perplexing me! My route model hook is:
model(params) {
return this.get('store').queryRecord('category', {
id: params.id
})
}
The setupController hook (that causes the error) is:
setupController(controller, model) {
controller.set('results', model.products);
}
The 'category' model:
export default DS.Model.extend({
products: hasMany('product'),
categoryName: attr('string')
});
The 'product' model:
export default DS.Model.extend({
name: attr('string'),
skuid: attr('string'),
price: attr('number'),
description: attr('string')
});
My template (which works, if I remove the 'setupController' hook from the route):
{{#each model.products as |product|}}
{{product.name}} {{product.skuid}}<br />
{{/each}}
I'd like to be able to access model.products from the route's setupController so I can call it something else. Any help appreciated.
Relationship returns Promises. so to get the result you need to use then. but accessing it in template will work because template is by default promise aware.
setupController(controller, model) {
//controller.set('results', model.products);
model.get('products').then((result) => {
controller.set('results',result);
});
}
Please give read on relationships as promises guide.
Hi I want to create a pagination for my application for that i am using spring-boot Pagination and ember-cli-pagination. Before using spring-boot pagination, rest controller returns data in the following format,
[{
"id" : 56789,
"name" : "sample book",
"status": "available",
},{
"id" : 56789,
"name" : "sample book",
"status": "available",
}]
after using spring-pagination my rest controller returns data in the format,
{
"content":
[{
"id" : 56789,
"name" : "sample book",
"status": "available",
},{
"id" : 56789,
"name" : "sample book",
"status": "available",
}],
"last": true,
"totalElements": 5,
"totalPages": 2,
"size": 3,
"number": 1,
"sort": null,
"first": false,
"numberOfElements": 2
}
Now the structure of the data completely changes so i need to modify my ember model so that ember data will properly serialize my data.
Is there any other way to apply spring-boot-pagination to ember data with minimal changes?Any standard or guided way to do that?
Do i really need to change my ember model?
Ember Model :
export default DS.Model.extend({
id: DS.attr(),
name: DS.attr(),
status: DS.attr()
});
Route.js
import Ember from 'ember';
import RouteMixin from 'ember-cli-pagination/remote/route-mixin';
export default Ember.Route.extend(RouteMixin, {
model: function(params) {
params.paramMapping = {
perPage: "size"};
return this.findPaged('book',params);
},
resetController: function (controller) {
var queryParams = controller.get('queryParams');
queryParams.forEach(function (param) {
controller.set(param, null);
});
},
});
Controller.js
import Ember from 'ember';
import pagedArray from 'ember-cli-pagination/computed/paged-array';
export default Ember.Controller.extend({
queryParams: ["page", "perPage",'uid'],
pagedContent: pagedArray('content', {pageBinding: "page", perPageBinding: "perPage"}),
totalPagesBinding: "pagedContent.totalPages",
page: 0,
perPage: 2,
//some actions
});
In the browser network tab i can able to see the response but in browser console i am getting the following message,
Error while processing route: book.index data is undefined _pushInternalModel
In Chrome getting following exception:
ember.debug.js:30610 Error while processing route: books.index Cannot read property 'type' of undefined TypeError: Cannot read property 'type' of undefined
and my teomplate is not loading.
index.hbs
<div class="row">
<div class="col-md-2">
<h3>
Books
</h3>
</div>
</div>
<br/>
{{#each pagedContent}}
<h2>entered paged content</h2>
{{/each}}
{{page-numbers content=pagedContent}}
Any help should be appreciable.
I have 2 models deliverHour and day. In an .hbs file, I have two nested each loops.
I am trying to display the delivery hours for each day. Apparently, once it reaches the second each loop, it doesnt render anything. Not even this line does not render!
Looking in Ember Inspector. I do not see any failed promises. The network tab doesn't even show Ember trying to attempt calling the delivery hour end point.
The Data tab shows delivery-hour (0), which means no delivery hours in the store, which is strange.
Since the relationship for the day model has DS.hasMany('deliveryHour', { async: true }) I would have expected Ember to do an asynchronous call to the delivery hour api resource. No?
What am I missing here?
// Route
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
var addressId = this.checkoutCart.get('addressId');
return Ember.RSVP.hash({
address: this.store.find('address', addressId),
delivery: this.store.createRecord('delivery'),
days: this.store.find('day')
});
},
// You can use model.delivery to get delivery, etc
// Since the model is a plain object you can just use setProperties
// http://stackoverflow.com/questions/16463958/how-to-use-multiple-models-with-a-single-route-in-emberjs-ember-data
setupController: function(controller, model) {
controller.setProperties(model);
},
});
// app/models/delivery-hour.js
import DS from 'ember-data';
export default DS.Model.extend({
day: DS.belongsTo('day', { async: true }),
from: DS.belongsTo('hour', { async: true }),
to: DS.belongsTo('hour', { async: true }),
status: DS.attr('string')
});
// app/models/day.js
import DS from 'ember-data';
export default DS.Model.extend({
deliveryHours: DS.hasMany('deliveryHour', { async: true }),
name: DS.attr('string'),
dayOfTheWeek: DS.attr('number')
});
// hbs
{{#each day in days}}
<li>
{{day.name}} // Returns something like 'Monday'
{{day.deliveryHours}} // Returns something like <(subclass of DS.PromiseArray):ember770>
{{day.deliveryHours.length}} // Returns 0
{{#each deliveryHour in day.deliveryHours}}
this line does not render!
{{deliveryHour.hour}} <----- ideally, this is what i want to see
{{/each}}
</li>
{{/each}}
{{day.deliveryHours.length}} returns 0. I wanted to verify this. I am using Rails as my API. In Rails console, when I do Day.first.delivery_hours.count, it returns 4. Strange that Ember returns 0. The Network tab, in Ember Inspector, does not show Ember trying to call the delivery hours API endpoint.
On the top right corner of the screenshot, I click on the > $E. Then I go to the console tab of Ember inspector and paste: $E.get('deliveryHours'). I get Object { content: Getter, toString: makeToString/<(), isFulfilled: true, 4 moreā¦ }.
In the console of Ember Inspector, I also tried:
>> $E.get('deliveryHours').then(function(hours) { console.log(hours.get('length'));});
Object { _id: 156, _label: undefined, _state: undefined, _result: undefined, _subscribers: Array[0] }
Update: My API response for days returns as:
{
"days":
[
{
"id": 1,
"name": "Monday",
"day_of_the_week": 1
},
{
"id": 2,
"name": "Tuesday",
"day_of_the_week": 2
},
{
"id": 3,
"name": "Wednesday",
"day_of_the_week": 3
},
{
"id": 4,
"name": "Thursday",
"day_of_the_week": 4
},
{
"id": 5,
"name": "Friday",
"day_of_the_week": 5
},
{
"id": 6,
"name": "Saturday",
"day_of_the_week": 6
},
{
"id": 7,
"name": "Sunday",
"day_of_the_week": 7
}
]
}
http://emberjs.com/guides/models/the-rest-adapter/#toc_relationships indicates that I should probably load the delivery_hours as an array of ID's. I've tried this too, same results..
That's because deliveryHours is an async property in your model, which is why {{day.deliverHours}} returns an unresolved promise, instead of the record array you were expecting.
You'll have to explicitly resolve it in an afterModel hook, like so:
// Route
import Ember from 'ember';
var RSVP = Ember.RSVP;
export default Ember.Route.extend({
// ...
afterModel: function(model) {
return RSVP.all(
model.days.map(function(day) {
return day.get('deliveryHours');
})
);
}
});
With ember data relationship can be {async: true} or {async: false}. How to create a model FIXTURES that mimic the behavior of an synced relashionship as described in the documentation :
var attr = DS.attr,
hasMany = DS.hasMany,
belongsTo = DS.belongsTo;
App.Post = DS.Model.extend({
title: attr(),
comments: hasMany('comment'),
user: belongsTo('user')
});
App.Comment = DS.Model.extend({
body: attr()
});
Ember Data expects that a GET request to /posts/1 would return the JSON in the following format:
{
"post": {
"id": 1,
"title": "Rails is omakase",
"comments": ["1", "2"],
"user" : "dhh"
},
"comments": [{
"id": "1",
"body": "Rails is unagi"
}, {
"id": "2",
"body": "Omakase O_o"
}]
}
You'll want to override the find method of the adapter. Here is the current implementation of the find method in the FixtureAdapter. You can see that it simply finds the record with the given ID, then returns it. You're going to want to modify that so that it side-loads the proper records. Something like this:
var json = {};
json[type.typeKey] = requestedRecord;
type.eachRelationship(function(name, meta) {
if (!meta.async) {
json[meta.type.typeKey.pluralize()] = [ /* put related records here */ ];
}
});
The syntax may not be perfect, but you should get the idea.