ember cli pagination not working - ember.js

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.

Related

Ember: accessing sideloaded data in setupController with ember-data

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.

Ember Data and each loop and nested objects in Handlebars

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/

EmberJS 2.7 passing data down into a component is failing

I have read many questions on this problem, but none of them seem to have the issue I have here.
I am using a third-party component (ember-highcharts) which works great except for this snag.
I have a route called dashboard. For now this route is just using dummydata, it is not using the store. This serves to illustrate the problem.
templates/dashboard.hbs
<div>
{{log model}} <-- NOTE this logs the object to the console as expected
{{summary-chart chartData=model}} <-- my component, see below
</div>
routes/dashboard.js
import Ember from 'ember';
export default Ember.Route.extend({
// this is for testing, normally we get the data from the store
model: function() {
return this.get('modelTestData');
},
setupController: function(controller, models) {
this._super(controller, models);
controller.set('model', models);
},
modelTestData: [{
name: 'gear',
colorByPoint: true,
data: [
{y: 10, name: 'Test1'},
{y: 12, name: 'Test2'},
{y: 40, name: 'Test3'}
]
}],
});
templates/components/summary-chart.hbs
{{log model}} <-- NOTE this logs '**undefined**' to the console NOT expected
{{high-charts chartOptions=summaryOptions content=model}}
components/summary-chart.js
import Ember from 'ember';
export default Ember.Component.extend({
summaryOptions: {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Total weight of gear in each category'
},
tooltip: {
pointFormat: '<b>{point.percentage:.1f}%</b> of {series.name}'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: false
},
showInLegend: true
}
}
}
});
Why is the model undefined and not being passed into summary-chart component? The chart renders (you can see the title) but of course without the data being plotted, because the model is undefined.
If I change the component to this, so the data is 'local' to the component, then the chart is rendered with the data points:
templates/components/summary-chart.hbs
{{high-charts chartOptions=summaryOptions content=summaryData}}
components/summary-chart.js
import Ember from 'ember';
export default Ember.Component.extend({
summaryOptions: {
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
type: 'pie'
},
title: {
text: 'Total weight of gear in each category'
},
tooltip: {
pointFormat: '<b>{point.percentage:.1f}%</b> of {series.name}'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: false
},
showInLegend: true
}
}
},
summaryData: [{
name: 'gear',
colorByPoint: true,
data: [
{y: 10, name: 'Test1'},
{y: 12, name: 'Test2'},
{y: 40, name: 'Test3'}
]
}]
});
Note that 'summaryData' is an identical data structure to 'modelTestData', so the chart understands how to plot it.
What I do not understand is why the route/controller combination is NOT passing the model down to the child component.
Why is it always 2 minutes after posting on SO you realise the answer to your own question?
The key line is "{summary-chart chartData..."
The model is being 'handed down' to the "chartData" property of the child component (summary-chart), so of course the data structure is bound to 'chartData' property now at this level, no longer to the model property at the dashboard/route level.
So the solution is to fix the template binding here:
templates/components/summary-chart.hbs
{{log chartData}} <-- NOTE this logs the object now as expected
{{high-charts chartOptions=summaryOptions content=chartData}}
chartData is now passed into the 'content' property of the 'high-charts' child component, and the chart renders, yay!

#ember-power-select, How to set a preselected value in ember-power-select custom-search action

There must be a value set in "selected" attribute before typing in search parameter".Can anyone help me to sort this out ? #ember-power-select
It is working with normal action-handling by setting a value for "destination" in js and assigning "destination" to "selected". Have a look at here for such examples http://www.ember-power-select.com/docs/action-handling.
But can't assign a value for custom-serach-action http://www.ember-power-select.com/docs/custom-search-action.
My Code:
Models:
hpqualifcation.js
import DS from 'ember-data';
export default DS.Model.extend({
type_name: DS.attr('string'),
hoprofile: DS.belongsTo('hoprofile')
});
hoprofile.js
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
hpqualification: DS.hasMany('hpqualification')
});
Route:
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params){
return Ember.RSVP.hash({
hpqualifications: this.store.query('hpqualification', {username: params.username}),
…
});
}
})
Data from API side:
{
"hpqualification": [
{
"id": 1,
"type_name": "UG",
"hoprofile": 1
},
{
"id": 1,
"type_name": "PG",
"hoprofile": 2
}
],
"hoprofile": [
{
"id": 1,
"name": "silicon guy",
"hpqualifications": [1]
},
{
"id": 2,
"name": "power star",
"hpqualifications": [2]
}
]
}
Templates:
Used ember-power-select custom-search-action, where the request will be sent to API side for typing each letter and the data returned will be displayed in select box options http://www.ember-power-select.com/docs/custom-search-action
{{#each model.hpqualifications as |hpqualification|}}
{{#power-select
selected=hpqualification.hoprofile.name
search=(action "hoProfile")
onchange=(action (mut hpqualification.hoprofile.name))
as |repo|
}}
{{repo.name}}
{{/power-select}}
{{/each}}
{{/power-select}}
{{/each}}
Components:
import Ember from 'ember';
export default Ember.Component.extend({
actions: {
hoProfile(term){
if (Ember.isBlank(term)) { return []; }
const url = `//localhost:3000/betaweb/filters/hoprofiles? name=${term}`;
return Ember.$.ajax({ url }).then(json => json.hoprofiles);
}
}
});
Data returned for power-select action:
{
"hoprofiles": [{
"id": 7,
"name": "silicon guy"
}, {
"id": 14,
"name": "power star"
}]
}
Everything is working fine. But in ember-power-select the preselected value is not getting selected. The select box is blank before typing in search parameter .
normally using the below code the value is visible
{{#each model.hpqualifications as |hpqualification|}}
<label>HO Profile Name<label>
<li> {{input value=hpqualification.hoprofile.name}} </li>
{{/each}}
It displays all the data that is returned from API side.
HO Profile Name
- silicon guy
- power star
But when i use ember-power-select the data is not getting preselected in select box. I have tried many ways but it didn’t sort me out. Can anyone please get me a solution or an alternate way to do this using power-select ?
For prefilled data, you need to specify the options property as well.
From the docs:
"You can provide both options and a search action. Those options will be the initial set of options, but as soon as the user performs a search, the results of that search will be displayed instead."
Just make sure that the list you pass to the options is also formatted the same as the search results, in other words array of objects with a "name" attribute.
As for pre-selected object, your selected property needs to also be an object with a "name" attribute. In your case selected=hpqualification.hoprofile.

Why can't I display the second association in a nested each loop?

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');
})
);
}
});