extjs6 update mask message not updating in long running method where chart is being updated with new series - extjs

In my extjs6 project I have this long running method. Starting with a loaded store, it groups by 'instrument', then creates an array with each item of that 'instrument', then creates a new store with only that data, then creates a series for a extjs chart, and adds the series to the chart.
there is a ton of data with about 100 instruments and a daily number for 2-3 years of data for each instrument. the process takes a long time and I want to update the mask window to say which instrument is being updated so the user can see what is going on.
How can I update the mask message in the middle of this long running method?
var me = this;
var myMask = Ext.get(me.windowCumulative.getEl()).mask('hello');
var task = new Ext.util.DelayedTask(function () {
//fadeout section
myMask.fadeOut({
duration: 500,
remove: true
});
//convert sql date to date datatype
myStoreTab1.each(function (record) {
record.set('filedate', new Date(record.get('filedate')));
});
myStoreTab1.sort('filedate');
myStoreTab1.group('instrument');
myStoreTab1.getGroups().each(function (group, i) {
var groupName = group._groupKey;
var targetStore = Ext.create('Ext.data.Store', {
model: 'xxx.model.HistoricalInstrumentProfitModel'
});
var records = [];
group.each(function (record) {
records.push(record.copy());
});
targetStore.add(records);
var series = {
type: 'line',
axis: 'left',
xField: 'filedate',
yField: 'cumulativePl',
store: targetStore,
title: groupName,
tooltip: {
trackMouse: true,
renderer: 'onSeriesTooltipRender'
}
};
me.chartTab1.addSeries(series);
//me.chartTab1.redraw();
//me.windowCumulative.setLoading(false);
console.log('added series: ' + groupName);
});
});
task.delay(500);
//debugger;
//me.chartTab1.redraw();
UPDATE...
for every group I run this
function DoMask(step, panel, countGroups, group, chart) {
setTimeout(function () {
var groupName = group._groupKey;
var targetStore = Ext.create('Ext.data.Store', {
model: 'xxx.model.HistoricalInstrumentProfitModel'
});
var records = [];
group.each(function (record) {
records.push(record.copy());
});
targetStore.suspendEvents();
targetStore.add(records);
var series = {
type: 'line',
axis: 'left',
xField: 'filedate',
yField: 'cumulativePl',
store: targetStore,
title: groupName,
tooltip: {
trackMouse: true,
renderer: 'onSeriesTooltipRender'
}
};
chart.addSeries(series);
console.log('added series: ' + groupName);
console.log(count);
panel.mask('step : ' + count);
if (count == countGroups) {
chart.resumeEvents();
chart.resumeLayouts();
chart.resumeChartLayout();
chart.redraw();
panel.unmask();
}
count = count + 1;
}, 500);
}

Take a look at these two ways to present the progress to the user:
Here is the FIDDLE
Ext.application({
name: 'Fiddle',
launch: function () {
var count;
var p = Ext.create('Ext.ProgressBar', {
width: 300,
textTpl: 'my Progress {value*100}%'
});
var window = Ext.create('Ext.window.Window', {
title: 'Progress',
modal:true,
hidden:true,
closable:false,
items:[
p
]
});
var panel = Ext.create('Ext.panel.Panel', {
title: 'teste',
height: 400,
renderTo: Ext.getBody(),
items: [{
xtype: 'button',
text: 'START LONG PROCESS MASK',
handler: function () {
count = 0;
this.up('panel').mask('Start');
DoMask(count);
}
}, {
xtype: 'button',
text: 'START LONG PROGRESS BAR',
handler: function () {
count = 0;
window.show();
DoProgress(count);
}
}]
});
function DoMask(step) {
setTimeout(function () {
panel.mask('step : ' + step);
count++;
if (count <= 10) {
DoMask(count);
} else {
panel.unmask();
}
}, 500);
}
function DoProgress(step) {
setTimeout(function () {
p.setValue(step/10);
count++;
if (count <= 10) {
DoProgress(count);
} else {
window.hide();
}
}, 500);
}
}
});

Related

How to add additional keys to the itemselector keymap EXTjs?

Is there a solution to extend the KeyMap of the ItemSelector?
I would like to add a keymap(like pageUp and pageDown keyEvent in itemselector) that when I press the letter 'A-Z' will take me to the item that starts with the letter pressed and select it.
You can use the following override (fiddle sample) to achieve it. It will not work correctly on view sore reload. And you will have to define the record search record field. In case of complicated view templates you can remove hardcoded search function and use it as a setting.
Ext.define('overrides.view.NavigationModel', {
override: 'Ext.view.NavigationModel',
searchRecordField: false,
initKeyNav: function (view) {
var me = this;
// Drive the KeyNav off the View's itemkeydown event so that beforeitemkeydown listeners may veto.
// By default KeyNav uses defaultEventAction: 'stopEvent', and this is required for movement keys
// which by default affect scrolling.
var keyNavConfig = {
target: view,
ignoreInputFields: true,
eventName: 'itemkeydown',
defaultEventAction: 'stopEvent',
processEvent: me.processViewEvent,
up: me.onKeyUp,
down: me.onKeyDown,
right: me.onKeyRight,
left: me.onKeyLeft,
pageDown: me.onKeyPageDown,
pageUp: me.onKeyPageUp,
home: me.onKeyHome,
end: me.onKeyEnd,
space: me.onKeySpace,
enter: me.onKeyEnter,
A: {
ctrl: true,
// Need a separate function because we don't want the key
// events passed on to selectAll (causes event suppression).
handler: me.onSelectAllKeyPress
},
F: me.onAlphabetKeyPress,
scope: me
};
if(this.view.searchRecordField) {
keyNavConfig = Ext.Object.merge(keyNavConfig, this.getAdditionalKeyNav());
}
me.keyNav = new Ext.util.KeyNav(keyNavConfig);
},
getAdditionalKeyNav: function() {
var keyNav = {};
this.view.getStore().each(function(record) {
var firstLetter = record.get(this.view.searchRecordField)[0].toUpperCase();
if(!keyNav[firstLetter]) {
keyNav[firstLetter] = this.onAlphabetKeyPress
}
}, this);
return keyNav;
},
onAlphabetKeyPress: function(keyEvent) {
const key = keyEvent.event.key;
var foundRecordIndex = this.view.getStore().findBy(function(record) {
return record.get('title').toLowerCase().indexOf(key) === 0;
}, this);
if(foundRecordIndex > -1) {
this.setPosition(foundRecordIndex, keyEvent);
}
}
});
Ext.application({
name: 'Fiddle',
launch: function () {
Ext.define('ListItem', {
extend: 'Ext.data.Model',
fields: [{
name: 'src',
type: 'string'
}, {
name: 'caption',
type: 'string'
}]
});
Ext.create('Ext.data.Store', {
id: 'ListItemsStore',
model: 'ListItem',
data: [{
title: "One"
}, {
title: "Two"
}, {
title: "Three"
}, {
title: "Four"
}, {
title: "Three"
}, ]
});
var imageTpl = new Ext.XTemplate(
'<tpl for=".">',
'<div style="margin-bottom: 10px;" class="thumb-wrap">',
'<span>{title}</span>',
'</div>',
'</tpl>'
);
Ext.create('Ext.view.View', {
store: Ext.data.StoreManager.lookup('ListItemsStore'),
tpl: imageTpl,
itemSelector: 'div.thumb-wrap',
emptyText: 'No images available',
// Search Record Field
searchRecordField: 'title',
renderTo: Ext.getBody()
});
}
});

How to drilldown to third level when its data is determined by second's click event?

So, I have been trying to use drill down to multiple levels, problem I am facing is that I couldn't drill down to the third level because the data will be fetched by ajax upon second drilldown's selection.
for example, refer to this link:
https://codepen.io/ajaymalhotra15/pen/aZpxXq
drilldown example
Here, the third level is possible because he has the data already, but mine will be depended on seconds selection.
So, how to make this happen, where am I supposed to call the ajax request and set the drill down series data dynamically?
EDIT:
Highcharts.chart("energy_chart", {
chart: {
type: "column",
spacingBottom: 15,
spacingTop: 10,
spacingLeft: 10,
spacingRight: 10,
backgroundColor: "#f2f2f2",
events: {
load: function() {
var fin = new Date();
var finDate = fin.getDate();
var finMonth = fin.getMonth();
var finYear = fin.getFullYear();
var ini = new Date();
ini.setFullYear(ini.getFullYear() - 1);
var iniDate = ini.getDate();
var iniMonth = ini.getMonth();
var iniYear = ini.getFullYear();
if (this.yAxis[0].dataMax == 0) {
this.yAxis[0].setExtremes(null, 1);
}
//this.yAxis.set
this.xAxis[0].setExtremes(
Date.UTC(iniYear, iniMonth, iniDate),
Date.UTC(finYear, finMonth, finDate)
);
},
drilldown: function(e) {
var charts_this = this;
var inidrillDate = new Date(e.point.x);
setTimeout(function() {
inidrillDate.setDate(0);
inidrillDate.setMonth(inidrillDate.getMonth());
var DateinidrillDate = inidrillDate.getDate();
var MonthinidrillDate = inidrillDate.getMonth();
var YearinidrillDate = inidrillDate.getFullYear();
var findrillDate = inidrillDate;
findrillDate.setMonth(findrillDate.getMonth() + 1);
findrillDate.setDate(findrillDate.getDate() - 1);
var DatefindrillDate = findrillDate.getDate();
var MonthfindrillDate = findrillDate.getMonth();
var YearfindrillDate = findrillDate.getFullYear();
charts_this.xAxis[0].setExtremes(
Date.UTC(
YearinidrillDate,
MonthinidrillDate,
DateinidrillDate
),
Date.UTC(
YearfindrillDate,
MonthfindrillDate,
DatefindrillDate
)
);
if (charts_this.yAxis[0].dataMax === 0) {
charts_this.yAxis[0].setExtremes(null, 1);
}
}, 0);
}
}
},
title: {
text: '<p className="energy_gen">Energy Generated</p>'
},
exporting: { enabled: false },
xAxis: {
type: "datetime",
labels: {
step: 1
},
dateTimeLabelFormats: {
day: "%e"
}
},
yAxis: {
title: {
text: "kWh"
}
},
credits: {
enabled: false
},
plotOptions: {
series: {
cursor: "pointer",
dataLabels: {
enabled: true,
format: "{point.y}"
},
color: "#fcd562",
point:{
events:{
click:function(event){
if(this.options!=null){
var dayOfYear=new Date(this.x).getFullYear() +"-"+(new Date(this.x).getMonth()+1)+"-"+new Date(this.x).getDate();
var formatted_date = new Date(this.x).getDate() + " " + months[(new Date(this.x).getMonth())] +" "+ new Date(this.x).getFullYear();
// document.getElementById('chart_date_id').innerHTML = formatted_date; //setting modal title with current date
$('#container').bind('mousemove touchmove touchstart', function (e) {
var chart,
point,
i,
event;
var sync_charts = $('.chart');
for (i = 0; i < sync_charts.length; i = i + 1) {
var chart_1 = sync_charts[i];
var chart_2 = chart_1.getAttribute('data-highcharts-chart');
chart=Highcharts.charts[chart_2];
event = chart.pointer.normalize(e.originalEvent);
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
});
Highcharts.Pointer.prototype.reset = function () {
return undefined;
};
Highcharts.Point.prototype.highlight = function (event) {
event = this.series.chart.pointer.normalize(event);
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
};
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function (chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(
e.min,
e.max,
undefined,
false,
{ trigger: 'syncExtremes' }
);
}
}
});
}
}
axios({
url: config.fvcstat,
method: "POST",
data: {
"customerId":self.props.location.state.detail.customerId,"rmsVendorId":self.props.location.state.detail.rmsVendorId,
"date":dayOfYear,
"powerType":self.props.location.state.detail.powerType
},
headers: {
"Content-Type": "application/json"
}
}).then((res)=>{
let activity = fvc.data;
if($('.chart')){
$('.chart').remove();
}
$.each(activity.datasets, function (i, dataset) {
console.log(1)
var chartDiv = document.createElement('div');
chartDiv.className = 'chart';
document.getElementById('container').appendChild(chartDiv);
Highcharts.chart(chartDiv,{
chart: {
},
plotOptions: {
series: {
marker:{
enabled:false
}
}
},
exporting: { enabled: false },
title: {
text: dataset.name,
align: 'left',
margin: 0,
x: 30
},
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
crosshair:{ width: 3},
events: {
setExtremes: syncExtremes
},
labels: {
format: '{value}'
},categories: activity.xData
},
yAxis: {
title: {
text: null
}
},
series: [{
data: dataset
}],
tooltip: {
positioner: function () {
return {
x: this.chart.chartWidth - this.label.width,
y: 10 // align to title
};
},
borderWidth: 0,
backgroundColor: 'none',
pointFormat: '{point.y}',
headerFormat: '',
shadow: false,
style: {
fontSize: '18px'
},
valueDecimals: dataset.valueDecimals
},
series: [{
data: dataset.data,
name: dataset.name,
type: dataset.type,
color: Highcharts.getOptions().colors[i],
fillOpacity: 0.3,
tooltip: {
valueSuffix: ' ' + dataset.unit
}
}]
});
});
})
}
}
}
}
}
},
tooltip: {
formatter: function() {
if (this.point.options.drilldown) {
return (
"Energy generated: <b> " +
this.y +
"</b> kWh " +
"<br>" +
Highcharts.dateFormat("%b %Y", new Date(this.x))
);
} else {
return (
"Energy generated: <b> " +
this.y +
"</b> kWh " +
"<br>" +
Highcharts.dateFormat("%e %b %Y", new Date(this.x))
);
}
}
},
series: [{'data':obj.data,'name':obj.name,"color":"#4848d3"}],
drilldown: {
series: obj.data
}
});
So, here if you notice in plotoptions i am trying to create a whole new chart which is a synced line charts showing frquency, voltage and current.
But, i am guessing my approach is not correct as i am plotting a new highchart.
So, how do i make this synced line chart part of my drilldown.
let me know if you require any help in understanding.
I will suggest first minimize the plotoption. Then expand for further fuck up :P
Thanks.
You can put all your logic to get the third level data and to create a drilldown series in drilldown event:
chart: {
type: 'column',
events: {
drilldown: function(e) {
if (!thirdLevel.length) {
// get data
}
if (!e.seriesOptions) {
var chart = this,
drilldowns = {
'Animals': {
name: 'Animals',
data: [
['Cows', 2],
['Sheep', 3]
]
},
'Fruits': {
name: 'Fruits',
data: [
['Apples', 5],
['Oranges', 7],
['Bananas', 2]
]
},
'Cars': {
name: 'Cars',
data: [
['Toyota', 1],
['Volkswagen', 2],
['Opel', 5]
]
}
},
series = drilldowns[e.point.name];
chart.addSingleSeriesAsDrilldown(e.point, series);
chart.applyDrilldown();
}
}
}
}
Live demo : http://jsfiddle.net/BlackLabel/86v3L4ft/
API Reference: https://api.highcharts.com/highcharts/chart.events.drilldown

Buffered grid and row expander plugin together are not working in extjs

Requirement: To have infinite scrolling for a extjs(4.2.2) grid along with row expanding functionality.
But both the plugins together not working,
row expander plugin not allowing data to load into buffered store of a grid.
when rowexpander plugin is commented, then infinite scroll grid is loading.
how to have both the functionalities?
i have created a rowexpander plugin as below by extending extjs rowexpander,
here if i comment out below line thne atleast one rec is getting loaded
feature.getRowBodyContents = Ext.bind(me.getRowBodyContents, me);
Ext.define('Premier.view.tools.NM.Plugin.NotesGridRowExpander', {
extend: 'Ext.grid.plugin.RowExpander',
alias: 'plugin.notes-rowexpander',
rowBodyTpl: [
'<div class="notes-management-tool"> ',
'<table cellspacing="0" cellpadding="0" class="notes-management-tool-table">',
'<tr>',
'<th valign="middle" class="notes-management-tool-table-date" style="color:black;width:130px;">Last Edited Date</th>',
'<th valign="middle" class="notes-management-tool-table-note" style="color:black;width:360px;">Notes</th>',
'<th valign="middle" class="notes-management-tool-table-editedBy" style="color:black;width:210px;">Last Edited By</th>',
'<th valign="middle" class="notes-management-tool-table-actions" style="color:black;width:50px;">Actions</th>',
'</tr>',
'<tpl for=".">',
'<tr>',
'<td valign="middle" class="notes-management-tool-table-date" style="color:black">{[(Ext.util.Format.date(Ext.Date.parse(values.LastEditedDate, "MS"), "M-d-Y h:i A"))|| " "]}</td>',
'<td valign="middle"class="notes-management-tool-table-note" style="color:black">{[(values.Note)|| " "]}</td>',
'<td valign="middle" class="notes-management-tool-table-editedBy" style="color:black">{[(values.LastEditedBy)|| " "]}</td>',
'<td valign="right" class="notes-management-tool-table-actions" ><div style="margin-left:3px" data-qtip="Edit" class = "{[parent.AccountId == values.LastEditedById ? "edit-notes-record-icon-active " : "edit-notes-record-icon-inactive "]}-noteId-{[this.getStringId(values.NoteId)]}-entityId-{[this.getStringId(values.EntityId)]}-entityTypeId-{[this.getStringId(values.EntityTypeId)]}"></div><div style="margin-left:3px" data-qtip="Delete" class = "{[parent.AccountId == values.LastEditedById ? "trash-icon " : "disabled-trash-icon " ]}-noteId-{[this.getStringId(values.NoteId)]}-entityId-{[this.getStringId(values.EntityId)]}-entityTypeId-{[this.getStringId(values.EntityTypeId)]}"></div></td>',
'</tr>',
'</tpl>',
'</table>',
'</div> ',
{
getStringId: function (id) {
return id.toString();
}
}
],
expandAllFlag: false,
constructor: function () {
var me = this;
me.callParent(arguments);
},
setCmp: function (grid) {
var me = this, features, i, feature;
me.callParent(arguments);
features = grid.features;
for (i = 0; i < features.length; i++) {
if ((feature = features[i]).ftype == 'rowbody') {
break;
}
}
if (feature) {
//This function is abstracted as a private function kind of. So overriding the method to call
//our own function
feature.getRowBodyContents = Ext.bind(me.getRowBodyContents, me);
}
},
getRowBodyContents: function (record) {
var content = '', data;
if(record.data) {
data = record.data.NotesChildViews.sort(
function (rec1, rec2) {
var a= Ext.Date.parse(rec1.LastEditedDate, "MS");
var b =Ext.Date.parse(rec2.LastEditedDate, "MS");
return a>b ? -1 : a<b ? 1 : 0;
});
}
if (data) {
data.AccountId = this.grid.app.getAccountId();
content = this.rowBodyTpl.apply(data);
}
return content;
},
getHeaderConfig: function () {
var me = this;
return {
itemId: 'rowExpanderHeader',
width: 40,
lockable: false,
sortable: false,
resizable: false,
draggable: false,
hideable: false,
text: '<div class="notes-grid-expand"></div>',
menuDisabled: true,
tdCls: Ext.baseCSSPrefix + 'grid-cell-special',
innerCls: Ext.baseCSSPrefix + 'grid-cell-inner-row-expander',
renderer: function (value, metadata) {
// Only has to span 2 rows if it is not in a lockable grid.
if (!me.grid.ownerLockable) {
metadata.tdAttr += ' rowspan="2"';
}
return '<div style="margin: 4px 0px 0px 8px;" class="' + Ext.baseCSSPrefix + 'grid-row-expander" role="presentation"></div>';
},
processEvent: function (type, view, cell, rowIndex, cellIndex, e, record) {
if (type == "mousedown" && e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-expander')) {
me.toggleRow(rowIndex, record);
return me.selectRowOnExpand;
}
},
listeners: {
'afterrender': me.handleExpansion,
scope: me
}
};
},
handleExpansion: function (headerObj) {
var me = this;
headerObj.el.on('click', function () {
me.cmp.fireEvent('notes-grid-expansion');
});
},
updateHeaderText: function () {
var me = this;
if (me.expandAllFlag == false) {
me.cmp.headerCt.down('#rowExpanderHeader').setText('<div class="notes-grid-collapse"></div>');
me.expandAllFlag = true;
}
else {
me.cmp.headerCt.down('#rowExpanderHeader').setText('<div class="notes-grid-expand"></div>');
me.expandAllFlag = false;
}
},
setDefaultHeader: function () {
var me = this;
me.cmp.headerCt.down('#rowExpanderHeader').setText('<div class="notes-grid-expand"></div>');
me.expandAllFlag = false;
}
});
when getrowbodycontent is removed from setCmp then ,
when calling getrowbodycontent in setCmp,
This solution uses bufferedrenderer and rowexpander plugins. I don't know how you get data, so the example generates some fake data and loads data dynamically.
I hope this helps.
Ext.define('testmodel', {
extend: 'Ext.data.Model',
fields: [
{name: 'name', type: 'string'},
{name: 'index', type: 'number'}
]
});
Ext.define('Test.RowExpander', {
extend: 'Ext.grid.plugin.RowExpander',
rowBodyTpl: [
"<tpl>",
"<p>Item name: {name}</p>",
"<p>Item index: {index}</p>",
"</tpl>"
],
expandAllFlag: false,
constructor: function() {
this.callParent(arguments);
},
setCmp: function (grid) {
var me = this, features, i, feature;
me.callParent(arguments);
features = grid.features;
for (i = 0; i < features.length; i++) {
if ((feature = features[i]).ftype == 'rowbody') {
break;
}
}
if (feature) {
feature.getRowBodyContents = Ext.bind(me.getRowBodyContents, me);
}
},
getRowBodyContents: function (record) {
var content = '', data;
if (record.data) {
data = record.data;
}
if (data) {
content = this.rowBodyTpl.apply(data);
}
return content;
}
});
Ext.onReady(function() {
Ext.define('Test.TestWindow', {
extend: 'Ext.window.Window',
closeAction: 'destroy',
border: false,
width: 560,
height: 500,
modal: true,
closable: true,
resizable: false,
layout: 'fit',
setStoreData: function() {
var me = this;
me.storeData = [];
for (i = 1; i <= 15000; i++) {
me.storeData.push(
{"name": "Name"+i, "index": i}
);
}
},
initComponent: function() {
var me = this;
me.callParent(arguments);
me.setStoreData();
me.store = Ext.create('Ext.data.Store', {
autoLoad: false,
pageSize: 16,
data: [],
model: 'testmodel'
});
me.rowexpander = Ext.create('Test.RowExpander', {
});
me.bufferedrenderer = Ext.create('Ext.grid.plugin.BufferedRenderer', {
});
me.grid = Ext.create('Ext.grid.Panel', {
loadMask: true,
plugins: [
me.bufferedrenderer,
me.rowexpander
],
selModel: {
pruneRemoved: false
},
stripeRows: true,
store: me.store,
columnLines: false,
columns : [
{header : 'Name', sortable : true, width: 100, dataIndex : 'name'},
{header : 'Index', sortable : true, width : 100, dataIndex : 'index'}
]
});
me.add(me.grid);
me.store.loadData(me.storeData);
}
});
var win = new Test.TestWindow({
});
win.show();
});
Notes:
Tested with ExtJS 4.2.1 and Internet Explorer 11, Google Chrome 68, Mozilla Firefox 61, Microsoft Edge 42.
Fiddle can be found here.

ExtJS add tooltip in a element inside iframe

I'm try to use tooltip in a element inside a iframe(generated by htmleditor component).
This is i'm trying:
Ext.tip.QuickTipManager.init();
Ext.create('Ext.form.HtmlEditor', {
width: 750,
height: 250,
renderTo: Ext.getBody(),
listeners: {
afterrender: function () {
this.getToolbar().add([{
xtype: "combobox",
flex: 1,
displayField: "name",
valueField: "value",
store: {
data: [{
name: "#NAME# (User's name)",
value: "#NAME#"
}]
}
}, {
xtype: "button",
text: "Add",
handler: function () {
var value = this.prev().getValue();
var htmlEditor = this.up("htmleditor");
if (value) {
var id = Ext.id();
value = "<span id=\"" + id + "\" style=\"cursor:pointer;\">" + value + "</span>";
htmlEditor.insertAtCursor(value);
var doc = htmlEditor.getDoc();
var elSpan = doc.getElementById(id);
var tTip = Ext.create("Ext.tip.ToolTip", {
html: "User's name tooltip.",
shadow: false,
scope: doc
});
elSpan.addEventListener("mouseover", function () {
tTip.showAt(elSpan.offsetLeft, elSpan.offsetTop)
});
elSpan.addEventListener("mouseleave", function () {
tTip.hide();
});
}
}
}])
}
}
});
But, when the component is shown, it appear in wrong position. See on the fiddle.
Sencha Fiddle: https://fiddle.sencha.com/#view/editor&fiddle/1vj4
I found a solution!
elSpan.addEventListener("mouseover", function (e) {
var x = e.pageX;
var y = e.pageY;
var region = htmlEditor.getRegion();
x += region.x;
y += region.y;
tTip.showAt([x, y]);
});

Why isn't my spline chart showing when using combo charts?

So I'm using Highcharts-ng with Angular to create a combination chart of a spline chart and a column chart formatted as a histogram to show trends.
The way it works is that the on the load of the page, I only see the histogram, and not the spline. Changing the order does nothing.
It looks as though if I have the spline chart data hard-coded it shows, but using the program to add in the data after a service is called in does not work.
(function() {
'use strict';
angular
.module('app.widgets')
.directive('trends', trends);
trends.$inject = ['ResultsService'];
/* #ngInject */
function trends() {
var ddo = {
restrict: 'EA',
templateUrl: 'app/widgets/trends/trends.directive.html',
link: link,
scope: {
data: '=',
config: '='
},
controller: controller,
controllerAs: 'vm',
bindToController: true
};
return ddo;
function link(scope, element, attrs) {
}
function controller($scope, ResultsService) {
var vm = this;
var parent = $scope.widgetController;
var size = {
height: angular.element('li.widget-border.ng-scope.gridster-item')[1].style.height - 20 ,
width: angular.element('li.widget-border.ng-scope.gridster-item')[1].style.width - 20
};
vm.histogram = {
chart: {
zoomType: 'xy'
},
title: {
text: 'Average Monthly Weather Data for Tokyo'
},
subtitle: {
text: 'Source: WorldClimate.com'
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
crosshair: true
},
yAxis: { // Primary yAxis
labels: {
style: {
color: Highcharts.getOptions().colors[2]
}
},
title: {
text: 'Events',
style: {
color: Highcharts.getOptions().colors[2]
}
}
},
tooltip: {
shared: true
},
legend: {
layout: 'vertical',
align: 'left',
x: 80,
verticalAlign: 'top',
y: 55,
floating: true,
backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColor) || '#FFFFFF'
},
series: [{
name: 'Average',
type: 'spline',
data: [],
marker: {
enabled: true
}
}],
loading: false,
useHighStocks: false,
size: {
height: size.height,
width: size.width
}
};
vm.processChartData = processChartData;
vm.data = {
charts: {
}
};
ResultsService.getData().then(function(res) {
vm.data = {
charts: {}
};
vm.data.charts = processChartData(res);
vm.histogram.xAxis.categories = [];
vm.histogram.series.push ({
name: 'Events per month',
type: 'column',
data: [],
marker: {
enabled: true
}
});
console.log(vm.histogram.series);
angular.forEach(vm.data.charts.months, function(v,k){
vm.histogram.xAxis.categories.push(k);
vm.histogram.series[1].data.push(v);
});
vm.histogram.options = {
plotOptions: {
}
};
vm.histogram.options.plotOptions = {
column: {
borderWidth: 0.5,
groupPadding: 0,
shadow: true
},
};
console.log(vm.data.charts.months);
vm.histogram.xAxis.categories.sort();
var average = calculateAverage();
vm.histogram.series[0].data=average;
console.log(vm.histogram.series);
});
function swap(pos1, pos2){
var temp = vm.histogram.series[pos1];
vm.histogram.series[pos1] = vm.histogram.series[pos2];
vm.histogram.series[pos2] = temp;
}
function calculateAverage() {
var averageArray = [];
var total = 0;
angular.forEach(vm.data.charts.months, function(v,k){
console.log(v);
total += v;
});
console.log((total/12.0).toFixed(2));
var average = (total/12.0).toFixed(2);
angular.forEach(vm.histogram.xAxis.categories, function(v,k){
averageArray.push(average);
});
return averageArray;
}
function processChartData(data) {
var output = {};
var months = {};
var dayOfWeek = {};
var epoch = {};
angular.forEach(data, function (value, index) {
// by month
if (!months[value.eventDate.month]) {
months[value.eventDate.month] = 1;
}
months[value.eventDate.month] += 1;
// by day of week
if (!dayOfWeek[value.eventDate.dayOfWeek]) {
dayOfWeek[value.eventDate.dayOfWeek] = 1;
}
dayOfWeek[value.eventDate.dayOfWeek] += 1;
// by day
if (!epoch[value.eventDate.epoch]) {
epoch[value.eventDate.epoch] = 1;
}
epoch[value.eventDate.epoch] += 1;
});
output.months = months;
output.dayOfWeek = dayOfWeek;
return output;
}
$scope.$on('gridster-item-resized', function(item){
var element = angular.element(item.targetScope.gridsterItem.$element[0]);
vm.histogram.size = {
height: element.height()-35,
width: element.width()
};
$scope.$broadcast('highchartsng.reflow');
});
}
}
})();
The chart on the webpage looks like this with the given code!
As you can see, it shows the legend with the spline, but the spline doesn't show up. I can't figure out why.
Your calculateAverage() function returns an array of strings since .toFixed(2) returns a string. Make sure it's an array of numbers. Convert average to a number with average = parseFloat(average) for example.

Resources