how to link custom ng2-charts plugin to one chart only? - angularjs

We created a plugin for a barchart using ng2-charts.
Component.ts:
barChartPlugins: ChartComponentLike[] = [
{
id: 'SOME_ID',
afterDatasetDraw: (chart, args, options) => {
// do some stuff using component property this.params
}
},
];
and template:
<canvas
#canvas
baseChart
[options]="barChartOptions"
[data]="barChartData"
[legend]="barChartLegend"
[plugins]="barChartPlugins"
></canvas>
The issue is that anytime another barchart is instanciated in the application, the plugin is applied AND the the values in this.params come from the first barchart.
How can we link the plugin to some chart only and/or pass some parameters?
So far, the only ugly solution we found is to pass parameters through the canvas id and we really don't want to do that.

Related

Open highcharts tooltip at a specific point - React typescript highcharts

I am using React with typescript and I need to open the Highchart tooltip as soon as the chart opens up. I need to open it at a specific point.
I know that I need a load function but I am getting error in using this keyword in typescript.
I found a solution. In Highcharts.Options object, inside chart object use the following code:
events: {
load: function (this: Highcharts.Chart) {
if (this) {
const data = this.series.map(s => s.points[s.points.length - 1]);
this.tooltip.refresh(data);
}
}
},
The tooltip will appear as soon as the charts load.

React variable component name - all lower case?

I am trying to render icons that are set in the back end for each service on the page. The data comes from an api and includes the correct names for the icons. I am importing the needed icons from react-icons before hand...
I tried it this way:
//Services and icons
var serviceICON = this.state.services.map(service => {
let Icon = service.Icon
return <tr><td><Icon /></td><td> {service.serviceName}</td></tr>
});
This almost works - the only problem is that the icons are not rendered. Instead the html looks like this:
<fabed></fabed>
I don't understand why this happens. The api delivers the correct name (=> FaBed), so why is this rendering as all lower case?
Thanks a lot for your help in advance!
Edit:
The complete array for one of the services would look like this:
id: 2
serviceName: "Hotel"
trip: 6
Icon: "FaBed"
created_at: "2020-07-08T06:45:02.239Z"
updated_at: "2020-07-22T07:52:05.066Z"
I am mapping through each of these and try to output the code above. As you can see "Icon" comes with the correct spelling. So I don't understand why it is rendered in all lower case...
Basically what is happening here is Icon is just a string so the react framework treats it differently.
html tags vs react components
<Icon />
is converted to
React.createElement(Icon, {});
Choosing the Type at Runtime
You'll notice here the key is using a map to match a string value with an actual React Component. For your case you'll need to map service.Icon to the relevant (imported) Icon Component.
import FaBed from '...';
...
const icons = {
...
"FaBed": FaBed,
...
};
...
var serviceICON = this.state.services.map(service => {
let Icon = icons[service.Icon];
return <tr><td><Icon /></td><td> {service.serviceName}</td></tr>
});

HandsOnTable editor custom function

I'm using the autocomplete editor of HOT, but needed to have my own template of the option-list. I've been able to accomplish that, by removing the default display and replacing it with my own while doing a lazy load of its content. But I need to perform specific tasks on each of the options being clicked.
The issue is that I cannot find a way to have my <a ng-click='doSomething()'> or <a onclick = 'doSomething()'> tags to find my "doSomething" function.
I've tried the extend prototype of the autocomplete instance, have put my function out there on my controller to no avail. Is there any way I can insert a delegate function inside this editor that could be triggered from inside my custom-made template? (Using angularjs, HOT version 0.34)
Dropdown options cannot interpret HTML instead of Headers.
To perform action when an option is selected you can use Handsontable callback : AfterChange or BeforeChange
Here you can find all HOT callbacks https://docs.handsontable.com/0.34.0/tutorial-using-callbacks.html
This JSFiddle can help you http://jsfiddle.net/fsvakoLa/
beforeChange: function(source, changes){
console.log(source, changes)
},
afterChange: function(source, changes){
console.log(source, changes);
if(!source) return;
if(source[0][1] == 0){//if ocurs on col 0
let newsource = optionsWBS[source[0][3]];
cols[1] = {
type : 'dropdown',
source: newsource,
strict: false
};
hot.updateSettings({columns: cols});
hot.render();
};
}
Thanks, I actually needed actions specific to each area being clicked. What I did to make it work was this: while inserting the items for the list, I created the element and bound it to the function right away: liElement = document.createElement('li') .... liElement.onclick = doSomething(){} .... got it working this way ..

Highcharts export stops working after first export

I've an AngularJS application using HighCharts (but not highcharts-ng though). I implemented export functionality similar to this JSFiddle which came from this GitHub discussion.
The issue is, after loading, the export works exactly once. For subsequent exports, it throws an error saying TypeError: Cannot read property 'exporting' of undefined.
Interestingly, the same behavior can be observed both in the JSFiddle and my implementation.
I believe it is referring to exporting property of chart options. Why would options become undefined after one export?
exportChart() method losses its context which should be a chart but in fact after the first exporting the context is a deleted chart with no properties - so it does not have any options.
When the chart is loaded the callback is invoked
func: function (chart) {
$scope.chartExport = $.proxy(chart.exportChart, chart);
}
In the above callback chartExport()'s context is bound with the rendered chart - which is fine for the first exporting.
When the first exporting is being proceed there is created a new temporary chart with new options merged with the options from the original chart. The callback from the chartConfig is invoked once more time and now $scope.chartExport() is bound with the temporary chart which will be destroyed just after the exporting is finished.
Move the callback to load events and in in the export options overwrites it so it will not change the chartExport() context.
options: {
chart: {
type: 'bar',
events: {
load: function () {
$scope.chartExport = $.proxy(this.exportChart, this);
}
}
}
},
...
$scope.svgExport = function() {
$scope.chartExport({type: 'image/svg+xml', filename: 'my-svg'}, {
chart: {events: {load: function () {} }},
subtitle: {text:''}});
}
example: http://jsfiddle.net/a9cse5pt/18/

Unable to render a Ext.form.TextField into the output of an XTemplate

I want to render some Ext components into the output of an XTemplate. We want to have the flexibility of using an XTemplate to render the HTML but retain the styling, behaviour, and handlers of using Ext components rather than plain old HTML elements.
I am currently successfully doing this with an Ext.Button. In the template I am writing a placeholder div like so:
<div id="paceholder-1"></div>
After I have called apply() on the template I then create a new Ext component and render it in like so:
this._replacePlaceholders.defer(1, this, [html, 'placeholder-1', collection]);
The _replacePlaceholders function looks like this:
_replacePlaceholders: function(html, id, collection) {
var emailField = new Ext.form.TextField({
emptyText: 'Email address',
hideLabel: true
});
var downloadButton = new Ext.Button({
text: 'Download as...',
icon: 'images/down.png',
scope: this,
menu: this._createDownloadOptionsMenu(collection) // Create Menu for this Button (works fine)
});
var form = new Ext.form.FormPanel({
items: [emailField, downloadButton]
});
downloadButton.render(html, id);
}
This works and renders the button into the html correctly. The button menu behaves as expected.
But if I change the last line of replacePlaceholders to emailField.render(html, id); or form.render(html, id); I get a javascript error.
TypeError: ct is null
ct.dom.insertBefore(this.el.dom, position);
ext-all-debug.js (line 10978)
I'm a bit confused because from what I can tell from the docs the render() method called is going to be the same one (from Ext.Component). But I've had a bit of a play around and can't seem to track down what is happening here.
So is there any good reason why these components behave differently from Ext.Button? and is it possible to render an Ext.form.TextField or an Ext.form.FormPanel or anything that will let me use an Ext text field in mt XTemplate html?
NB. I am using ExtJS 3.3.1 and don't have the opportunity to upgrade the version. I believe ExtJS 4 has functionality which would make doing what I doing much easier.
Thanks!
Solution is quite simple - use form.render(id) instead of form.render(html, id).
See [api][1] if you have doubts.
The reason why button is rendering properly is that it has weird onRender implementation, different from Component.
onRender : function(ct, position){
[...]
if(position){
btn = this.template.insertBefore(position, targs, true);
}else{
btn = this.template.append(ct, targs, true);
}
[...]
}
As you can see in code above, if you provide position (which is basically second argument provided to render) it doen't use ct (which is first argument passed to render).
In normal component onRender method looks like this:
onRender : function(ct, position){
[...]
if(this.el){
this.el = Ext.get(this.el);
if(this.allowDomMove !== false){
ct.dom.insertBefore(this.el.dom, position);
if (div) {
Ext.removeNode(div);
div = null;
}
}
}
}
In code above, you can see, that ct is called always, despite the position is not null.
The bottom line is that rendering of button works by accident.

Resources