extjs 2.3 grid fails on store update - javascript

First time posting, have only started using extjs 2.3 (at my work) and have run into a strange issue. Basically I have an option for a user to get SLD (straight line distance) between a location they have selected and a number of predefined locations, so the users clicks the SLD button, a new window opens which does the following, loads predefined locations into a jsonstore, links this store into a grid in the new window, when the store is created I also send a request to googles directions service to return the driving distance between the locations, on callback I add this data to store which in turns updates the grid.
The issue I see is, the first time the SLD button is clicked, the grid displays the information, and then the google callback adds the extra data into the store and I can see this displayed on the grid. I have a back button on the window, which when clicked returns the user to the menu window, destroys the SLD window and empties the store, so there is no trace of the SLD window any more. The issue will happen now when I click the SLD button again on the main menu, I can see the grid with data but now when the google callback returns and updates the store I see that the cells look like they have been edited and not saved.
On my production machine this issue does not happen when I use Firefox or Chrome, only happens on IE, however I have wrote a small jsFiddle to reproduce the issue and now get the issue to happen on Chrome when I run the test.
I can't understand how it can work correctly the first time, then the second time have this issue, and basically its running the same code as the first time!
This is what my test looks like, have added dummy data and simplified things to reproduce issue
var testData = [
{'name': 'home', 'distance': 16.5, 'driving_distance': 0 },
{'name': 'work', 'distance': 35.2, 'driving_distance': 0 },
{'name': 'gym', 'distance': 12.8, 'driving_distance': 0 },
];
var locations;
// create store and load it with data
function createStore() {
locations = new Ext.data.JsonStore({
data: testData,
sortInfo: {
field: 'distance',
direction: 'ASC'
},
fields: [
{ name: 'name' },
{ name: 'distance', type: 'float' },
{ name: 'driving_distance', type: 'float' }
]
});
var myLocation = new google.maps.LatLng( '55.033778', '-7.125324' );
var anyLocation = new google.maps.LatLng( '54.972441', '-7.345526' );
var directionsService = new google.maps.DirectionsService();
var request = {
origin: new google.maps.LatLng( '55.033778', '-7.125324' ),
destination: anyLocation,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
// get driving distance from myLocation to anyLocation and update locations store
for ( var x = 0; x < locations.data.length; x++ )
{
// call directions service
directionsService.route(request, function(response, status) {
// do stuff if we get a result
if (status == google.maps.DirectionsStatus.OK) {
// update store items to use same value just for text purposes
var distance = response.routes[0].legs[0].distance.value;
distance = distance / 1000;
// update on return call even though it updating the same thing 3 times
locations.data.items[0].set('driving_distance', distance.toFixed(1));
locations.data.items[1].set('driving_distance', (distance + 10.1).toFixed(1) );
locations.data.items[2].set('driving_distance', (distance + 23.3).toFixed(1) );
locations.commitChanges();
}
});
}
}
new Ext.Window ({
// menu normally consists of a combo box in which a user can select SLD
title: 'Menu - cut down',
id: 'rightClickWindow',
headerPosition: 'left',
scope: this,
buttons: [{
text: 'SLD',
id: 'SLDButton',
handler: function () {
// hide menu window
Ext.getCmp('rightClickWindow').hide();
// create store
createStore();
// create SLD window
new Ext.Window ({
title: 'SLD',
id: 'createSLDWindow',
headerPosition: 'left',
width: 450,
scope: this,
items: [{
xtype: 'grid',
id: 'SLDGrid',
singleSelect: true,
store: locations,
columns: [
{id: 'name', header: 'Location', width: 160, sortable: false, dataIndex: 'name'},
{header: 'SLD', width: 80, align: 'center', sortable: false, renderer: 'distance', dataIndex: 'distance'},
{header: 'Driving Distance', width: 90, align: 'center', sortable: false, renderer: 'driving_distance', dataIndex: 'driving_distance'}],
stripeRows: true,
autoExpandColumn: 'name',
enableHdMenu: false,
height: 250,
header: false
} ],
buttons: [{
text: 'Back',
id: 'SLDBackButton',
handler: function () {
// destroy SLD window
Ext.getCmp('createSLDWindow').destroy();
// show menu window
Ext.getCmp('rightClickWindow').show();
// destroy store
locations.loadData([],false);
}
}],
listeners: {
close: function (form) {
// destory everything
Ext.getCmp('createSLDWindow').destroy();
Ext.getCmp('rightClickWindow').destroy();
// destroy store
locations.loadData([],false);
}
}
}).show();
}
}]
}).show();
jsFiddle http://jsfiddle.net/UDkDY/74/
to reproduce click SLD -> back -> SLD

I think there is a problem with the way you update the values :
you send 3 requests (one for each line in the grid), but on the callback of each request you update ALL the lines (when you should only update the line corresponding to the request).
Example :
http://jsfiddle.net/xer0d0he/
-

Related

itemmouseleave event is not getting called if we move cursor quickly

I have treepanel. On some specific condition I want to show image on mouse enter and remove that image on mouseleave in treenode. but when i hover the mouse quickly image get added but not getting removed as itemmouseleave event is not getting fired.
I have prepared jsfiidle to understand my problem in which I am trying to change text of node on mouseenter and mouseleave. on slow motion it is working fine but if hover quickly it shows mouseenter even if we are away from node.
Link to jsfiddle : http://jsfiddle.net/79ZkX/238/
Ext.create("Ext.tree.Panel", {
title: "Car Simple Tree",
width: 400,
height: 600,
store: store,
rootVisible: false,
lines: true, // will show lines to display hierarchy.
useArrows: true, //this is a cool feature - converts the + signs into Windows-7 like arrows. "lines" will not be displayed
renderTo: Ext.getBody(),
listeners: {
itemmouseenter: function(_this, _item) {
var name = _item.get("name");
_item.set("name", "mouseenter");
},
itemmouseleave: function(_this, _item) {
//var name = _item.get('name');
_item.set("name", "leave");
}
},
columns: [
{
xtype: "treecolumn",
dataIndex: "name", // value field which will be displayed on screen
flex: 1
}
]
});
I want to remove the image on mouseleave. Thanks
Added manual workaround for this. On Fast Mouse Hover itemmouseleave event will not get triggered. so i am maintaining array of hovered node and on mouseenter of node, checking if array contain element then set text of that node.
added code to this jsfiddle: http://jsfiddle.net/79ZkX/250/
Ext.create('Ext.tree.Panel', {
title: 'Car Simple Tree',
width: 400,
height: 600,
store: store,
rootVisible: false,
visibleNodes: [],
lines: true, // will show lines to display hierarchy.
useArrows: true, //this is a cool feature - converts the + signs into Windows-7 like arrows. "lines" will not be displayed
renderTo: Ext.getBody(),
listeners : {
itemmouseenter: function(_this, _item) {
for (var i = 0; i < this.visibleNodes.length; i++) {
var node = this.visibleNodes[i];
node.set('name', "leave");
this.visibleNodes.splice(i, 1);
}
var name = _item.get('name');
_item.set('name', "mouseenter");
this.visibleNodes.push(_item);
},
itemmouseleave: function(_this, _item) {
//var name = _item.get('name');
_item.set('name', "leave");
var index = this.visibleNodes.indexOf(_node);
if (index != -1){
this.visibleNodes.splice(index, 1);
}
},
},
columns: [{
xtype: 'treecolumn',
dataIndex: 'name', // value field which will be displayed on screen
flex: 1
}]
});

Extjs Set value to end timefield with respect to input in start timefield

I have a question.
I have 2 timeFields in my form, startTime and endTime. On selection of time from startTime I need the endTime to be filled with a value which is 2 hrs greater than the selected value in startTime field.
Can anyone help me out.
I'm using ext 4.2 and sencha architect.
var win = this.getRfRWindow();
var form = win.down('form');
var startTime = win.down('form').down('timefield[name=sTime]');
var endTime = win.down('form').down('timefield[name=eTime]');
endTime.clearValue();
endTime.setMinValue(startTime.getValue());
And I know this is how to set minValue.
I am not 100% sure if this is what you are trying to do but this code snippet will use the value set in one, and set the value in the other to 2 hours later. It will also set the min value to the start time selected.
Ext.application({
name : 'Fiddle',
launch : function() {
Ext.create('Ext.form.Panel', {
title: 'Time Card',
width: 300,
bodyPadding: 10,
renderTo: Ext.getBody(),
items: [
{
xtype: 'timefield',
name: 'in',
fieldLabel: 'Time In',
increment: 30,
anchor: '100%',
listeners:{
select: function(tf, record, eOpts){
// when selected, we want to use the same functionality as blur or you can change it.
tf.fireEvent('blur',tf);
},
// this is fired when the value is changed in the start field.
blur:function(tf, e,eOpts){
// get the value in the field.
var value = tf.getValue();
// if the value isn't empty, and is valid time.
if (value != ''){
var fullValue = new Date(value),
hours = fullValue.getHours(), // get hours
minutes = fullValue.getMinutes(); // get minutes
//create a new datetime object using hours / minutes from selected value
var timeOut = new Date();
timeOut.setHours(hours+2);
timeOut.setMinutes(minutes);
// get the out and set the value.
var timeOutField = tf.up('panel').down('timefield[name=out]');
timeOutField.setValue(timeOut);
//set min value to what was in the start time.
timeOutField.setMinValue(tf.getValue());
}
}
}
},
{
xtype: 'timefield',
name: 'out',
fieldLabel: 'Time Out',
increment: 30,
anchor: '100%'
}
]
}).show();
}
});
See Fiddle Here:
https://fiddle.sencha.com/#fiddle/16up
Considering you have two time fields in a panel, you only need to listen to select event of the first time field and get the second time field using component query and set its value accordingly.
Here is a working code example:
items: [
{
xtype: 'timefield',
fieldLabel: 'Start Time',
name: 'sTime',
editable: false,
increment: 30,
anchor: '100%',
listeners:{
// This event is fired after user selects a value from drop down menu
select: function(combo, record, eOpts){
// Creating Date object according to new Start date
var dateForNewStartTime = new Date(combo.getValue());
//create a new datetime object using selected time
var dateForEndTime = new Date();
dateForEndTime.setHours(dateForNewStartTime.getHours()+2); // Adding 2 more hours
dateForEndTime.setMinutes(dateForNewStartTime.getMinutes());
// Getting and Setting value (you can change this component query according to your requirements)
var timeOutField = Ext.ComponentQuery.query('timefield[name=eTime]')[0];
timeOutField.setValue(dateForEndTime);
// Setting minimum slectable value for End Time field accordingly
timeOutField.setMinimumValue(combo.getValue());
}
}
},
{
xtype: 'timefield',
fieldLabel: 'End Time',
name: 'eTime',
increment: 30,
editable: false,
anchor: '100%'
}
]
You can use ViewModel + Formulas to achieve your requirement. ViewModels are fundamental blocks in ExtJs and help you do model, control binding. It supports two-way binding, meaning if one changes other follows the change.
In your case you can have a formula to derive EndTime based on StartDate and since these two are model binding, the controls will automatically follow the update in date values.
Look at this sample code I've created to demonstrate the ViewModel + Formula example usage
The same is reproduced here for quicker reference
Ext.define('MyApp.view.TimeViewModel', {
extend: 'Ext.app.ViewModel',
alias: 'viewmodel.timeviewmodel',
data: {
startTime: new Date(),
endTime: new Date()
},
formulas: {
getEndDate: function(get){
console.log(Ext.Date.add(get('startTime'), Ext.Date.HOUR, 2));
return Ext.Date.add(get('startTime'), Ext.Date.HOUR, 2);
}
}
});
Ext.define('dualDatePanel', {
extend: 'Ext.container.Container',
viewModel: {
type: 'timeviewmodel'
},
items: [{
xtype: 'timefield',
bind: '{startTime}'
},
{
xtype: 'timefield',
bind: '{getEndDate}'
}]
});
Ext.create('dualDatePanel', {
renderTo: Ext.getBody(),
width: 100,
height: 32,
emptyText: 'Binding Failed'
});

How to call FooterView method

According to the Alloy UI API, the FooterView class has a method called refreshFooter() which
Refreshes the summary items in the footer view and populates the footer elements based on the current "data" contents.
I am trying to figure out how to call this function after a certain event, not sure how to make the call since the footerView is defined as an attribute. Here is my Datatable:
var dataTable = new Y.DataTable({
columns: columns,
height: '95%',
footerView: Y.FooterView,
footerConfig: {
fixed: true,
heading: {
colspan: 5,
content: "Number of Records : {row_count}"
}
}
});
I've tried placing the footerView into a variable and invoking but, but no luck. Any ideas on how to execute this function?
Source: http://stlsmiths.github.io/new-gallery/classes/Y.FooterView.html#method_refreshFooter
Basically tables require two kinds of information, table columns and data. Pass both into your Data Table after columns and data, and don't forget to render it! . I think you are not rendering it. Try it ! GoodLuck!
YUI().use(
'aui-datatable',
function(Y) {
var columns = [
name,
age
];
var data = [
{
name: 'Bob',
age: '28'
},
{
name: 'Joe',
age: '72'
},
{
name: 'Sarah',
age: '35'
}
];
new Y.DataTable(
{
columns: columns,
data: data
}
).render("#myDataTable");
}
);
#myDataTable is the div that you want to render.

ExtJS 5 Pie Chart Not Rendering Using Remote Store

I have a basic pie chart in ExtJS 5. The issue I am having is that the chart renders with a static JsonStore but won't render properly with a remote data.store?
Here is my code:
View (Chart)
Ext.define('APP.view.core.graphs.Countytotals', {
extend: 'Ext.Panel',
alias: 'widget.countytotalchart',
id: 'countyTotalsGraph',
width: 650,
initComponent: function() {
var me = this;
// Doesn't work?
var countyStore = Ext.create('APP.store.Countytotalsgraph');
// Works
var store = Ext.create('Ext.data.JsonStore', {
fields: ['COUNTY', 'AMOUNT'],
data: [{
'COUNTY': 'London',
'AMOUNT': 10.92
}, {
'COUNTY': 'Lancashire',
'AMOUNT': 6.61
}, {
'COUNTY': 'Kent',
'AMOUNT': 5.26
}, {
'COUNTY': 'West Yorkshire',
'AMOUNT': 4.52
}, {
'COUNTY': 'Nottinghamshire',
'AMOUNT': 4.01
}, {
'COUNTY': 'Other',
'AMOUNT': 68.68
}]
});
var chart = new Ext.chart.PolarChart({
width: '100%',
height: 500,
insetPadding: 50,
innerPadding: 20,
legend: {
docked: 'bottom'
},
listeners: {
afterrender: function (chart) {
if (chart.isVisible()) {
countyStore.load();
chart.redraw();
}
}
},
interactions: ['itemhighlight'],
store: countyStore,
series: [{
type: 'pie',
angleField: 'AMOUNT',
label: {
field: 'COUNTY',
display: 'outside',
calloutLine: {
length: 60,
width: 3
// specifying 'color' is also possible here
}
},
highlight: true,
tooltip: {
trackMouse: true,
renderer: function(storeItem, item) {
this.setHtml(storeItem.get('COUNTY') + ': ' + storeItem.get('AMOUNT') + '%');
}
}
}]
});
me.items = [chart];
this.callParent();
}
});
Store
Ext.define('APP.store.Countytotalsgraph', {
extend: 'Ext.data.Store',
model: 'APP.model.Countytotalsgraph',
autoLoad: false,
storeId: 'countyTotalsGraphStore',
proxy: {
type: 'ajax',
url : '/dashboard/countytotals',
method : 'POST',
reader: {
type: 'json',
rootProperty: 'data'
}
},
listeners: {
beforeload: function(store, eOpts) {
//if ( this.data.items.length ) {
//Ext.getCmp('optionsGrid').getView().refresh();
//}
store.proxy.extraParams = {
percentage: 'true'
}
}
}
});
Model
Ext.define('APP.model.Countytotalsgraph', {
extend: 'Ext.data.Model',
fields: ['COUNTY', 'AMOUNT']
});
This is how is renders with the static store:
This is how it renders with the remote store:
I am on the latest version of the GPL although the charts were built using Sencha CMD and the "sencha ant build" command in the sencha-charts directory.
Why does the static store display it (well still there is still an issue regarding the legend at the bottom) but the remote json not?
Iv'e tried to load the store after it the chart is rendered and is visible as I have seen a previous post regarding holding off on loading the store to give the chart time to render but this did not work:
listeners: {
afterrender: function (chart) {
if (chart.isVisible()) {
countyStore.load();
chart.redraw();
}
}
},
Thanks in advance :)
Nathan
Probably a bug in Ext.
The chart colors are set in Ext.chart.AbstractChart#updateColors. This is a "config" method, that is called automatically when setColors is called, and also from the constructor, when the config is initialized.
In your case, it is only called at construction time, before the remote store has been loaded; and it happens that polar series need to know the number of records in the store in order to know how many colors it must used (unlike other kind of charts that rely on number of axis or so).
Here's the code of that method:
updateColors: function (newColors) {
var me = this,
colors = newColors || (me.themeAttrs && me.themeAttrs.colors),
colorIndex = 0, colorCount = colors.length, i,
series = me.getSeries(),
seriesCount = series && series.length,
seriesItem, seriesColors, seriesColorCount;
if (colorCount) {
for (i = 0; i < seriesCount; i++) {
seriesItem = series[i];
// Ext.chart.series.Polar#themeColorCount uses store.getCount()
// so seriesColorCount will be 0
seriesColorCount = seriesItem.themeColorCount();
// ... hence seriesColor will be an empty array
seriesColors = me.circularCopyArray(colors, colorIndex, seriesColorCount);
colorIndex += seriesColorCount;
seriesItem.updateChartColors(seriesColors);
}
}
me.refreshLegendStore();
}
You could probably get it working by creating the chart after the load event of the store, but that's kind of kinky given your usage is as intended, and the bug will probably get smashed in a coming release.
For now, a possible fix is to override the onRefresh of the chart, that is called, well, when the store is refreshed, and force colors to be updated at this time:
Ext.define(null, {
override: 'Ext.chart.PolarChart'
,onRefresh: function() {
this.callParent(arguments);
var colors = this.getColors();
if (colors) {
this.updateColors(colors);
}
}
});

Properly Remove LoadMask Once GeoExt MapPanel is Completely Loaded

I would like to have a mask over the whole page which does not get removed until the entire page has completely loaded. More specifically, I have a map created with OpenLayers and GeoExt and I am trying to use an ExtJS loadMask. However, I have not been able to find any other way of doing this other than using a manual setTimeout which I do not want to use. I'd much rather have the mask removed only if the page is completely loaded. I have tried to use the 'loadend' event on the openLayers map as well as windows.onload etc:
My map and loadMask config:
var mask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
mask.show();
Ext.onReady(function() {
var options = {
controls: [new OpenLayers.Control.Navigation()],
maxExtent: new OpenLayers.Bounds(-20037508.34, -20037508.34, 20037508.34, 20037508.34),
units: 'm',
allOverlays: false
}
var map = new OpenLayers.Map(options);
map.events.register("loadend", map , function() {
mask.hide(); alert('howdy');
});
var mapPanel = new GeoExt.MapPanel({
title: "Map",
map: map,
id: 'mapPanel',
layerStore: map.layers,
//Set the map to be centered at specified longitude/latitude, transform our layers (SRID=4326) to display properly on Google
//base layers (SRID=900913)
center: new OpenLayers.LonLat(95.20, 30.34).transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")),
zoom: 7,
region: "center",
tbar: [measureLength, '-', measureArea, '-'],
bbar: [
{
xtype: "label",
text: "Scale = 1 : "
}
],
items: [{
xtype: "gx_zoomslider",
vertical: true,
height: 300,
aggressive: true,
x: 10,
y: 20,
plugins: new GeoExt.ZoomSliderTip()
}]
});
It seems this event never happens as I never get an alert message. I really really want to get this working, other attempts were:
window.onload = mask.hide();
after the Ext.onReady and at the end of the </body> tag, but then the mask is hidden way before the map is done loading. Could anyone share some insight, I'd really appreciate it!
Add the event 'onMapReady' just after the items like so:
onMapReady: function() {
Ext.getBody().unmask();
}

Categories

Resources