I want to update my chart every seconds
Here is my code :
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartData: {
// my charData
}
}
setInterval(function(){
this.props.changeHandler();
}, 500);
}
changeHandler() {
this.state.update();
}
// my render
}
But I have this error : this.props is undefined (On the setInterval)
Anyone can help me please ?
function has its own this context so you either have to store the this context in a local variable before the setInterval or use an arrow function instead of function
setInterval(() => {
this.props.changeHandler();
}, 500);
First you need to obtain a reference to the Chart.js instance.
constructor() {
...
this.chartReference = React.createRef();
...
}
render() {
return (
<Doughnut ref={this.chartReference} data={this.state.data} />
)
}
From this reference, you can obtain the chart instance, update its data and finally invoke chart.update().
setInterval(() => {
const chart = this.chartReference.current.chartInstance;
chart.data.datasets[0].data = [<the new data>];
chart.update();
}, 2000);
Please take a look at this StackBlitz and see how it works.
Related
I have embedded ember-cli-chart in my hbs file as
<div class="chart">
{{ember-chart type='line' data=data options=options}}
</div>
In my component file I have created an options property as
options: computed('metric', function() {
let opts = defaultOptions;
if (this.metric === 'height') {
opts.scales.yAxes = [{
ticks: {
callback: function(value, index, values) {
// code to return labels
}
}
}]
} else {
opts.scales.yAxes = [{
ticks: {
callback: function(item, index, items) {
// code to return labels
}
}
}]
}
return opts;
});
I want to display Y-Axis labels based on the current selected metric.
When first time chart loads it renders correct labels on y-Axis and if I change the metric then the same callback is getting used instead of the other one (in else part) and renders same labels but with updated data values.
Can anyone help on this?
Hmmm I don't know the addon or chart.js for the matter, but when looking at the source code for the ember-chart component, I see
didUpdateAttrs() {
this._super(...arguments);
this.updateChart();
},
updateChart() {
let chart = this.get('chart');
let data = this.get('data');
let options = this.get('options');
let animate = this.get('animate');
if (chart) {
chart.config.data = data;
chart.config.options = options;
if (animate) {
chart.update();
} else {
chart.update(0);
}
}
}
So, in order for chart.js to update, you need didUpdateAttrs to fire, which means in your case here that options itself needs to change. I don't know how you're creating defaultOptions, but assuming this reference never changes, there's no reason that didUpdateAttrs would fire since you aren't changing the reference to options (you're only changing child props of defaultOptions in the computed). I would suppose that:
import { assign } from '#ember/polyfills';
...
options: computed('metric', function() {
let opts = assign({}, defaultOptions);
if (this.metric === 'height') {
opts.scales.yAxes = [{
ticks: {
callback: function(value, index, values) {
// code to return labels
}
}
}]
} else {
opts.scales.yAxes = [{
ticks: {
callback: function(item, index, items) {
// code to return labels
}
}
}]
}
return opts;
})
would be enough to trigger the behavior you want since we always return a new object when a recomputation of options occurs.
I'm trying to remove an item from my state array when clicked. At the moment I have an onclick listener which calls a function passed into the props. However I get a warning: bind(): React component methods may only be bound to the component instance. See App... and it does not remove the item.
Thanks for any help regarding this issue! It has pretty much ground my progress to a halt.
(function (React) {
var data = [
'Go to work',
'Play Albion Online',
'Keep learning React'
]
var App = React.createClass({
getInitialState: function () {
return {data: []}
},
componentWillMount: function () {
this.state.data = data;
},
removeItem: function (i) {
console.log(i);
},
render: function () {
return (
<ToDoList onRemoveItem={this.removeItem} tasks={this.state.data} />
)
}
});
var ToDoList = React.createClass({
render: function () {
var scope = this;
var tasks = this.props.tasks.map(function (task, i) {
return <ToDo onClick={scope.props.onRemoveItem.bind(this, i)} key={task} task={task} />
});
return (
<ul>
{tasks}
</ul>
)
}
});
var ToDo = React.createClass({
render: function () {
return (
<li>{this.props.task}</li>
)
}
});
React.render(<App />, document.getElementById('example'));
})(React);
React actually auto-binds methods to the current component:
http://facebook.github.io/react/blog/2013/07/02/react-v0-4-autobind-by-default.html
In the TodoList component, rather than:
scope.props.onRemoveItem.bind(this, i)
Try:
scope.props.onRemoveItem.bind(null, i)
By providing null instead of this you'll allow React to do its own thing. Also you need to actually use the onClick handler:
<li onClick={this.props.onClick}>{this.props.task}</li>
After adding angular-ui-bootstrap and running grunt serve on my yeoman app, it runs perfectly and the modal I want to show is displayed correctly, but once I do a grunt build, I get an unknown provider error in my console.
<!-- This is what I added in my index.html -->
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
// In app.js I have
angular.module('yeomanApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute',
'ui.bootstrap'
])
and in the controller,
.controller('myCntrl', function ($modal) {
$scope.items = ['item1', 'item2', 'item3'];
$scope.showDeleteWarning = function () {
var modalInstance = $modal.open({
templateUrl: 'deleteWarning.html',
controller: ModalInstanceCtrl,
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {});
};
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
var ModalInstanceCtrl = function ($scope, $modalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
deleteVent();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
};
Likely that you need to inject your controller dependency...
https://docs.angularjs.org/tutorial/step_05#a-note-on-minfication
.controller('myCntrl', ['$modal', function ($modal) {
/* Controller Code Here... */
}]);
I know this is an old question, but I'll post my answer here for people who come across this problem in the future.
I came across this exact problem before. The cause of your errors during minification is most likely your 'var ModalInstanceCtrl'.
Here's how I got my code to work:
var modalInstance = $modal.open({
templateUrl: 'deleteWarning.html',
controller: 'ModalInstanceCtrl', //change this to a string
resolve: {
items: function () {
return $scope.items;
}
}
});
and this line:
var ModalInstanceCtrl = function ($scope, $modalInstance, items) {
to:
angular.module('myModule').controller('ModalInstanceCtrl', function ($scope, $modalInstance, items) {
For anyone who just encountered this problem, maybe this will help.
We use customModalDefaults and customModalOptions, so we had to turn the whole return $modal.open(tempModalDefaults).result; in the show function to the following:
this.show = function (customModalDefaults, customModalOptions) {
//Create temp objects to work with since we're in a singleton service
var tempModalDefaults = {};
var tempModalOptions = {};
//Map angular-ui modal custom defaults to modal defaults defined in service
angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);
//Map modal.html $scope custom properties to defaults defined in service
angular.extend(tempModalOptions, modalOptions, customModalOptions);
return $modal.open({
backdrop: customModalDefaults.backdrop,
keyboard: customModalDefaults.keyboard,
modalFade: customModalDefaults.modalFade,
templateUrl: customModalDefaults.templateUrl,
size: customModalDefaults.size,
controller: ['$scope', '$modalInstance', function ($scope, $modalInstance) {
$scope.modalOptions = tempModalOptions;
$scope.modalOptions.ok = function (result) {
$modalInstance.close(result);
};
$scope.modalOptions.close = function (result) {
$modalInstance.dismiss('cancel');
};
} ]
}).result;
};
I just ran into this problem on only one of many modals used throughout my application, and it turned out my problem was not using explicit function annotation in the resolve block of the modal configuration.
var modalInstance = $uibModal.open({
templateUrl: 'preferences.html',
controller: 'preferencesCtrl as ctrl', // this external controller was using explicit function annotation...
resolve: {
parent: [function() {
return ctrl;
}],
sectorList: ['preferencesService', function(preferencesService) { // but this was not!
return preferencesService.getSectors();
}]
}
});
Hope this saves someone else a gray hair or two...
I am creating a component to wrap the select2 select box. The code is below:
App.FixedSelectComponent = Ember.Component.extend({
actions: {
change: function(value) {
this.set('selectedValue',value);
}
},
didInsertElement : function(){
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
// send to change
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
// send to change
}
});
},
willDestroyElement : function() {
this.$("#select1").select2('destroy');
},
});
however, what I am stuck at is how to send the data that I've got in the on("change") event to the action:change that I've defined , or if I can set the selectedValue property itself in the on("change") event
"this" isn't the component at the "// send to change" lines - how / where do I get the reference to the component itself at this point ?
basically what I am trying to achieve is to get the data passed to the "change" event of select2 into my selectedValue property
thanks
You can use Component.send('actionName').
I found it in Ember's documentation.
this context will not refer to FixedSelectComponent context in $.each, and also use send method which will call FixedSelectComponent change method..
refer : http://emberjs.com/api/classes/Ember.Component.html#method_send
didInsertElement : function(){
var _this = this;
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
_this.send('change',value.split('>')[2].split('<')[0]); // send to change
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
_this.send('change',e.val.split('>')[2].split('<')[0]); // send to change
}
});
}
this.get('actions').change.call(this, value);
Check http://emberjs.com/api/classes/Ember.Component.html#property_actions -- 'actions' is simply another property on your Component.
Try this:
App.FixedSelectComponent = Ember.Component.extend({
change: function(value) {
this.set('selectedValue',value);
}
didInsertElement : function(){
var self = this;
this.$("#select1").select2().on("change", function(e) {
if ($.isArray(e.val)) {
$.each(e.val, function(index,value) {
console.log("multiple:",value.split('>')[2].split('<')[0]);
// send to change
self.change(value); // substitute value by whatever you want to pass
});
} else {
console.log("single:",e.val.split('>')[2].split('<')[0]);
// send to change
self.change(value); // substitute value by whatever you want to pass
}
});
},
willDestroyElement : function() {
this.$("#select1").select2('destroy');
},
});
this._actions['change'].apply(this, value);
I'm currently applying anonymous functions to a hover event, that call global functions inside:
GridInterface.HexGrid.TileGlobal = {
hoverIn: function(obj) {
var self = obj;
self.tile.attr({ fill: '#c7c7c7', stroke : '#000' });
self.tile.toFront();
self.label.toFront();
self.label.attr({ fill : '#000' });
},
hoverOut: function(obj) {
var self = obj;
self.tile.attr({ fill : '#d0d1ff', stroke : '#aaa' });
self.label.attr({ fill: '#999' });
}
}
Then i simply call:
.hover(function() {
GridInterface.HexGrid.TileGlobal.hoverIn(self);
}, function() {
GridInterface.HexGrid.TileGlobal.hoverOut(self);
}
)
on the object. However, when i switch that to unhover, the event is not removed. I think it's because it's a different anonymous function. Any ideas on how to fix?
GridInterface.HexGrid.Tile = function(coords) {
var self = this;
this.hoverIn = function() {
this.attr({ fill: '#c7c7c7', stroke : '#000' });
};
this.hoverOut = function() {
this.attr({ fill: '#d0d1ff', stroke: '#aaa' });
};
this.attachEvents = function()
{
self.el[0].hover(self.hoverIn, self.hoverOut);
};
};
Then elsewhere i call:
tile.el[0].unhover(tile.hoverIn, tile.hoverOut);
Tile being an object instance of that above function.
Maybe solve the problem not with the raphäel event handling but with a library of your choice, ie jQuery: $(circle.node).click(…)
like that you can addEvent / removeEvent on demand.