Updated 03/17/2016 to better reflect current best practices for EmberJS v1.13.0 and up.
Problem
I am rendering Highcharts into a component and i almost have it working, but the binding of a property into the component is getting lost somewhere.
This is how I call the component:
//templates/index.hbs
{{pie-chart data=pieData}}
This is what the data property looks like (currently set in a controller):
//controllers/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
init() {
this._super(...arguments);
this.pieData = [
['0 - 30', 2.5],
['31 - 60', 7.5],
['61 - 90', 12.5],
['91 - 120', 77.5]
];
}
});
And here is the component logic:
//components/pie-chart.js
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['chart'],
renderChart() {
return this.$().highcharts({
chart: {
height: 275
},
title: null,
plotOptions: {
pie: {
dataLabels: {
enabled: false
}
}
},
series: {
type: 'pie',
data: this.get('data')
},
colors: ['#777777', '#888888', '#999999', '#aaaaaa', '#bbbbbb', '#cccccc', '#dddddd', '#eeeeee'],
credits: {
enabled: true
}
});
},
didUpdateAttrs() {
let chart = this.$().highcharts();
let series = this.get('data');
chart.series[0].setData(series);
},
didInsertElement() {
this._super(...arguments);
this.renderChart();
},
willDestroyElement() {
this.$().highcharts().destroy();
}
});
i got the idea from this blog and i am trying to adapt it to make more charts.
the chart renders onto the screen, but it is blank... there are no errors to report... the only thing i can think is the data property is not being handled correctly in order to plot the graph?
I am not sure if this is my wrong use of Ember code, or wrong use of Highcharts code?
Solution
the series property inside the highchart was supposed to be an array of objects.. but I mistakenly defined just an object:
the fix:
series: [{
type: 'pie',
data: this.get('data')
}],
So, that is how you render a Highchart.js into a component :)
Related
So, I'm trying to access my model properties in controller.
Controller:
dashobards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
];
In route I have model named dashboards
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
}).then((hash) => {
return Ember.RSVP.hash({
dashboards: hash.dashboards
});
}, self);
I wanna have result in controller like this:
dashboards: [
{ id: 12, name: 'test' },
{ id: 17, name: 'test2' },
{ id: 17, name: 'test1' },
{ id: 20, name: 'test20' },
];
In controller I am trying to access this model like this:
this.dashborads = this.get(model.dashobards)
And it's not working, is there any other way of doing that?
Another update How to access complex object which we get it from server in ember data model attibute,
Created twiddle to demonstrate
define attribute with DS.attr(),
export default Model.extend({
permissions:DS.attr()
});
route file,
model(){
return this.store.findAll('dashboard');
}
Your server response should be like,
data: [{
type: 'dashboard',
id: 1,
attributes: {
permissions: {'name':'role1','desc':'description'}
}
}]
hbs file,
{{#each model as |row| }}
Name: {{row.permissions.name}} <br/>
Desc: {{row.permissions.desc}} <br />
{{/each}}
Update:
Still I am not sure about the requirement, Your twiddle should be minimalized working twiddle for better understanding..anyway I will provide my observation,
1.
model(params) {
this.set('id', params.userID);
const self = this;
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
}).then((hash) => {
return Ember.RSVP.hash({
user: hash.user,
dashboards: hash.dashboards
});
}, self);
}
The above code can be simply written like
model(params) {
this.set('id', params.userID);
return Ember.RSVP.hash({
dashboards: this.store.findAll('dashboard'),
user: this.store.findRecord('user', params.userID)
});
}
Its good to always initialize array properties inside init method. refer https://guides.emberjs.com/v2.13.0/object-model/classes-and-instances/
For removing entry from array,
this.dashboard.pushObject({ 'identifier': '', 'role': '' }); try this this.get('dashboard').pushObject({ 'identifier': '', 'role': '' });.
if possible instead of plain object you can use Ember.Object like
this.get('dashboard').pushObject(Ember.Object.create({ 'identifier': '', 'role': '' }));
For removing entry.
removeDashboard(i) {
let dashboard = Ember.get(this, 'dashboard');
Ember.set(this, 'dashboard', dashboard.removeObject(dashboard[i]));
}
The above code can be written like, since i is an index
removeDashboard(i) {
this.get('dashboard').removeAt(i)
}
Just do return this.store.findAll('dashboard'); in route model hook, and dont override setupController hook, then in hbs you should be able to access model that will represent RecordArray. you can have a look at this answer for how to work with this.
i have two components in my template:
{{ property-pie-chart
models=model.hosts
defaultProp=""
filterByDate=filterByDate
chartData=[]
}}
{{ paged-filtered-list
data=model.hosts
dates=model.dates
page=page
pageSize=pageSize
filterByDate=filterByDate
pagerView=pagerView
initRouteAction=( action 'dateInit' )
dateFilterAction=( action 'filterByDate' )
termFilterAction=(action 'filterByTerm')
sortOrder=sortOrder
sortField=sortField
}}
I send action from paged-filtered-list component to controller, which triggers route transition with filterByDate as parameter:
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams: [
'page',
'pageSize',
'sortField',
'sortOrder',
'filterByDate',
'filterByTerm'
],
filterByDate: "",
filterByTerm: "",
page: 1,
pageSize: 10,
pagerView: 4,
sortField: "",
sortOrder: 'asc',
lala: "",
actions: {
dateInit: function(sortedDates) {
if (!this.get('filterByDate')) {
let params = {
filterByDate: sortedDates.get('firstObject').get('key'),
page: this.get('page'),
pageSize: this.get('pageSize'),
pagerView: this.get('pagerView')
};
this.transitionToRoute('hosts', { queryParams: params});
}
},
filterByDate: function(value) {
if (value) {
let params = {
filterByDate: value,
page: 1,
pageSize: this.get('pageSize')
};
this.transitionToRoute('hosts', { queryParams: params});
}
},
filterByTerm: function(value) {
let params = {
filterByDate: this.get('filterByDate'),
page: 1,
pageSize: this.get('pageSize')
};
if (value) {
params['filterByTerm'] = value;
} else {
params['filterByTerm'] = "";
}
this.transitionToRoute('hosts', { queryParams: params});
}
}
});
Problem is that URL is updated and contains filterByDate, but first component property-pie-chart does not detect that filterByDate property is changed, altough i checked attributes in init/didUpdate methods and parameter is changed, can somebody help and explain what i am doing wrong?
Currently you are not setting filterByDate property in controller.
I would suggest the following approach,
You please declare the below property in corresponding route.js,
queryParams: { page: { refreshModel: true }, pageSize: { refreshModel: true },sortOrder: { refreshModel: true },filterByDate: { refreshModel: true },filterByTerm: { refreshModel: true }}
refreshModel denotes is whenever this property changed,then it will force to refresh the page.
and in controller.js, You don't need to call this.transitionToRoute('hosts', { queryParams: params}); instead you just set required queryParams participating property alone then transition will automatically taken care.
SideNote: It's good if you can change function name filterByTerm filterByDate by the way this is not related to problem
Update:
I am glad you sorted out the problem. but then I want to emphasize what are Computed Properties from ember guides.
In a nutshell, computed properties let you declare functions as properties. You create one by defining a computed property as a function, which Ember will automatically call when you ask for the property. You can then use it the same way you would any normal, static property.
https://guides.emberjs.com/v2.8.0/object-model/computed-properties/#toc_what-are-computed-properties
I am working with Ember and Ember-data. But the JSON which i receive is not par with the Ember side-loading standards. The JSON does'nt have a root model. Also the models are embedded and some times haves Ids and sometimes does not have Id.
I have seen couple of links on how to add root model using extract hook and also how to play with embedded model using
App.ColorSerializer = DS.RestSerializer.extend(DS.EmbeddedRecordsMixin, {
attrs: {
foos: {embedded: 'always'}
}
});
This code is taken from this link.
This is the JSON used there
{
colors:[
{
id: 1,
color: "red",
foos:[
{
id:1,
name:'something 1'
},
{
id:2,
name:'something 2'
}
]
},
...
Now the problem that i am facing is that my JSON could also look like below(no root model "color")
{
id: 1,
color: "red",
foos:[
{
id:1,
name:'something 1'
},
{
id:2,
name:'something 2'
}
]
},
...
or even like this(without Ids for foo objects)
{
id: 1,
color: "red",
foos:[
{
name:'something 1'
},
{
name:'something 2'
}
]
},
...
Is there any way i can handle this? How do i add Ids to the embedded model foo? Also is there some solution/plugin which would accept any kind of embedded JSON and convert it into side loaded JSON and added Ids if needed.
I have seen this solution. Does it really work? Because it does not use the latest EmbeddedRecordsMixin
I used a generic transform for arrays:
// /transforms/array.js
import DS from "ember-data";
import Ember from "ember";
export default DS.Transform.extend({
deserialize: function (value) {
if (Ember.isArray(value)) {
return Ember.A(value);
} else {
return Ember.A();
}
},
serialize: function (value) {
if (Ember.isArray(value)) {
return Ember.A(value);
} else {
return Ember.A();
}
}
});
Then in my model, I simply use:
foos: DS.attr("array")
I am struggeling to get a gridster layout values saved in my model: normal saving isn't that big deal.. just the grid layout.. how to fix this part?
Component: Serialized data availible after resized grid
export default Ember.Component.extend({
tagname: "",
Setupgridster: function(){
Ember.$(".gridster ul").gridster({
widget_base_dimensions: [359, 232],
widget_margins: [5, 5],
testvalue: 213213,
helper: 'clone',
resize: {
enabled: true,
max_size: [3, 3],
min_size: [1, 1],
stop: function (e, ui, $widget) {
var widget_base_dimensions = this.serialize($widget)[0];
}
},
serialize_params: function($w, wgd) {
return {
col: wgd.col,
row: wgd.row,
size_x: wgd.size_x,
size_y: wgd.size_y
};
}
}).data('gridster');
}.on("didInsertElement")
});
Save controller
export default Ember.Controller.extend({
model: 'layout',
actions: {
save: function(){
var newName= this.store.createRecord('layout', {
title: this.get('title'),
/** Don't know how to get in here serialized gridster data **/
layout: this.get('gridsterdata')
});
newName.save();
alert('saved');
this.transitionToRoute('index');
}
});
Someone told me to intergrate the save option into the component itself as an action, but I haven't got a clue how to make this work. Could somebody fix this or make a suggestion. I should be really pleased! (looking allready 1 week on this issue)
so you found the gridster resize stop callback, all you should need to do is:
export default Ember.Component.extend({
tagname: "",
Setupgridster: function(){
var _this = this;
Ember.$(".gridster ul").gridster({
...
resize: {
stop: function (e, ui, $widget) {
_this.sendAction('save', your_params_here);
var widget_base_dimensions = this.serialize($widget)[0];
}
}
}).data('gridster');
}.on("didInsertElement")
});
and then in your template where you are using your component, bind the action:
{{gridster-component save='save'}}
and modify your controller save action to take whatever arguments you send from the component.
I have a ContainerView which contains a CollectionView. After this CollectionView renders on the screen I need to perform a jquery function which essentially looks through the content of the rendered template and performs some display modifications.
If I perform that jquery within the didInsertElement of CollectionView it works but it gets executed for every single element in the CollectionView where as I really just need it to be done once at the end. How do I specify that?
http://jsfiddle.net/JFqNr/ (note doesn't render on jsfiddle or some reason but just to show you structure)
App = Ember.Application.create();
App.FooContainerView = Ember.ContainerView.extend({
childViews: ['elementList'],
elementList: Ember.CollectionView.extend({
content: function() {
return [
{ Title: "Dashboard", ID: "dashboard" },
{ Title: "Invoices", ID: "invoices" },
{ Title: "Expenses", ID: "expenses" },
{ Title: "People", ID: "people" },
{ Title: "Reports", ID: "reports" },
{ Title: "Settings", ID: "settings" }
];
}.property(),
template: Ember.Handlebars.compile( '{{view.content.title}}' ),
didInsertElement: function() {
// perform jquery function
}
}),
didInsertElement: function() {
// does not work if perforemed here
}
});
App.initialize();
The functionality to do this has only very recently been added to the master branch, so you will need to be compile your own version of Ember.
You can now schedule into an afterRender queue to run after all the individual views have been rendered.
App.FooContainerView = Ember.ContainerView.extend({
// Existing code
didInsertElement: function() {
Ember.run.scheduleOnce('afterRender', this, function(){
// perform jQuery function here;
});
}
See https://github.com/emberjs/ember.js/pull/1528 for code details.