PLUpload not working with Extj 5 - extjs

Please find the fiddles for PLUpload against Extjs 4 and 5
Fiddle with Extjs 4 [WORKING]
http://jsfiddle.net/jaspreets/dvsbsgse/
Fiddle with Extjs 5 [NOT WORKING]
http://jsfiddle.net/jaspreets/797sLzsn/
Please tell me why it is not working with ExtJs - 5 .
What can be the possible solution for this ?
Thanks in advance.

You need to migrate PLUpload plugin. For that you need to have several changes in the plugin.
Button.js:
/**
* #class Ext.ux.upload.Button
* #extends Ext.button.Button
*
* #author Harald Hanek (c) 2011-2012
* #license http://harrydeluxe.mit-license.org
*/
Ext.define('Ext.util.multiupload.Button', {
extend: 'Ext.button.Button',
alias: 'widget.uploadbutton',
requires: ['Ext.util.multiupload.Basic'],
disabled: true,
listeners: {
boxready: function() {
var me = this;
Ext.applyIf(me.config.uploader, {
browse_button: me.config.id || me.getId()
});
me.uploader = me.createUploader();
me.uploader.initialize();
me.plugins[0].initializeComponent(me);
me.relayEvents(me.uploader, ['beforestart',
'uploadready',
'uploadstarted',
'uploadcomplete',
'uploaderror',
'filesadded',
'beforeupload',
'fileuploaded',
'updateprogress',
'uploadprogress',
'storeempty'
]);
}
},
/**
* #private
*/
createUploader: function() {
return Ext.create('Ext.util.multiupload.Basic', this, Ext.applyIf({
listeners: {}
}, this.initialConfig));
}
});
Basic.js:
Remove AddEvents. Change Store to this.
me.store = Ext.create('Ext.data.JsonStore', {
fields: ['id', 'loaded', 'name', 'size', 'percent', 'status', 'msg'],
listeners: {
load: me.onStoreLoad,
remove: me.onStoreRemove,
update: me.onStoreUpdate,
scope: me
}
});
Second argument of onStoreRemove method is an array. So take first element.
Window.js:
Remove Id "form-statusbar". Rename init function to initializeComponent.
It should be working now.

Related

ExtJS 6.5 - SelectionModel select not working

I am trying to select the first row in a grid:
Ext.getCmp('myGrid').getSelectionModel.select(0);
I got an error saying select is undefined
but I can see the function exists.
I was using the ExtJS 4.2 version and I had no problem with that, now I am using the 6.5 version and I got that error.
What could be the problem here?
--EDIT--
Basically I have a function to create the store:
CreateDataStore: function (result, id) {
Ext.define('DDM', {
extend: 'Ext.data.Model',
fields: result.FieldColumns
});
var dataRecords = JSON.parse(result.Records);
return Ext.create('Ext.data.Store', {
id: id,
autoLoad: true,
model: 'DDM',
data: dataRecords,
remoteSort: false,
fields: result.Fields
});
}
And another function to assign the store to the grid and select the first row:
function BindDataGrid(result) {
gridStore = CreateDataStore(result, 'myGridStore');
Ext.getCmp('myGrid').reconfigure(gridStore);
if (Ext.getCmp('myGrid').getStore().getCount() > 0)
Ext.getCmp('myGrid').getSelectionModel().select(0);
}

ExtJS: Custom ComboBox

I'm just trying to create a custom ComboBox to reduce some boilerplate:
Ext.define('App.AutoComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.autocombobox',
states: null,
initComponent: function() {
this.callParent(arguments);
if (!this.states) {
this.queryMode = 'remote';
} else {
this.queryMode = 'local';
this.bindStore(Ext.create('Ext.data.Store', {
type: 'array',
fields: ['_placeholder_'],
data: _.map(this.states, function(state) {
return {_placeholder_ : state}; })
}));
this.displayField = this.valueField = '_placeholder_'
}
this.validator = function(v) {
var field = this.displayField,
index = this.getStore().findExact(field, v);
return (index!==-1) ? true : 'Invalid selection';
};
},
listeners: {
select: function(combo, records) {
console.log(combo.getStore().indexOf(records[0])); // !== -1
}
}
});
So that I can use it like:
requires: ['App.AutoComboBox'],
...
items: [{
xtype: 'autocombobox',
name: 'test_local',
fieldLabel: 'test_local',
states: [ 'cat', 'dog' ] // local
}, {
xtype: 'autocombobox',
name: 'test_remote',
fieldLabel: 'test_remote',
store: 'Chipmunks', // a remote store
displayField: 'chipmunk_name'
}]
...
But something is amiss. The AutoComboBox renders OK, shows dropdown of records fine, but when I select an item from the dropdown, the combobox's display field is not set. The store seems to find the selected record (as seen by the select listener), but the value is still not set...
Help? thanks.
Edit: FIXED by moving this.callParent(arguments) after the new store is bound. Now accepting answers that explain why this fixes it... (I don't know why it works.. but it does)
In the parent initComponent method, the displayField is used to create the displayTpl:
if (!me.displayTpl) {
me.displayTpl = new Ext.XTemplate(
'<tpl for=".">' +
'{[typeof values === "string" ? values : values["' + me.displayField + '"]]}' +
'<tpl if="xindex < xcount">' + me.delimiter + '</tpl>' +
'</tpl>'
);
} else if (Ext.isString(me.displayTpl)) {
me.displayTpl = new Ext.XTemplate(me.displayTpl);
}
The bindStore call has probably nothing to do with it, I believe that this is this line that must be put before the call to the parent method:
this.displayField = this.valueField = '_placeholder_';

Dynamic scrolling in combobox ext 4.0

I am using extjs 4.0 and having a combobox with queryMode 'remote'. I fill it with data from server. The problem is that the number of records from server is too large, so I thought it would be better to load them by parts. I know there is a standart paginator tool for combobox, but it is not convinient because needs total number of records.
Is there any way to add dynamic scrolling for combobox? When scrolling to the bottom of the list I want to send request for the next part of records and add them to the list. I can not find appropriate listener to do this.
The following is my solution for infinite scrolling for combobox, Extjs 4.0
Ext.define('TestProject.testselect', {
extend:'Ext.form.field.ComboBox',
alias: ['widget.testselect'],
requires: ['Ext.selection.Model', 'Ext.data.Store'],
/**
* This will contain scroll position when user reaches the bottom of the list
* and the store begins to upload data
*/
beforeRefreshScrollTop: 0,
/**
* This will be changed to true, when there will be no more records to upload
* to combobox
*/
isStoreEndReached : false,
/**
* The main thing. When creating picker, we add scroll listener on list dom element.
* Also add listener on load mask - after load mask is hidden we set scroll into position
* that it was before new items were loaded to list. This prevents 'jumping' of the scroll.
*/
createPicker: function() {
var me = this,
picker = me.callParent(arguments);
me.mon(picker, {
'render' : function() {
Ext.get(picker.getTargetEl().id).on('scroll', me.onScroll, me);
me.mon(picker.loadMask, {
'hide' : function() {
Ext.get(picker.id + '-listEl').scroll("down", me.beforeRefreshScrollTop, false);
},
scope: me
});
},
scope: me
});
return picker;
},
/**
* Method which is called when user scrolls the list. Checks if the bottom of the
* list is reached. If so - sends 'nextPage' request to store and checks if
* any records were received. If not - then there is no more records to load, and
* from now on if user will reach the bottom of the list, no request will be sent.
*/
onScroll: function(){
var me = this,
parentElement = Ext.get(me.picker.getTargetEl().id),
parentElementTop = parentElement.getScroll().top,
scrollingList = Ext.get(me.picker.id+'-items');
if(scrollingList != undefined) {
if(!me.isStoreEndReached && parentElementTop >= scrollingList.getHeight() - parentElement.getHeight()) {
var multiselectStore = me.getStore(),
beforeRequestCount = multiselectStore.getCount();
me.beforeRefreshScrollTop = parentElementTop;
multiselectStore.nextPage({
params: this.getParams(this.lastQuery),
callback: function() {
me.isStoreEndReached = !(multiselectStore.getCount() - beforeRequestCount > 0);
}
});
}
}
},
/**
* Took this method from Ext.form.field.Picker to collapse only if
* loading finished. This solve problem when user scrolls while large data is loading.
* Whithout this the list will close before finishing update.
*/
collapse: function() {
var me = this;
if(!me.getStore().loading) {
me.callParent(arguments);
}
},
/**
* Reset scroll and current page of the store when loading all profiles again (clicking on trigger)
*/
doRawQuery: function() {
var me = this;
me.beforeRefreshScrollTop = 0;
me.getStore().currentPage = 0;
me.isStoreEndReached = false;
me.callParent(arguments);
}
});
When creating element, should be passed id to the listConfig, also I pass template for list, because I need it to be with id. I didn't find out more elegant way to do this. I appreciate any advice.
{
id: 'testcombo-multiselect',
xtype: 'testselect',
store: Ext.create('TestProject.testStore'),
queryMode: 'remote',
queryParam: 'keyword',
valueField: 'profileToken',
displayField: 'profileToken',
tpl: Ext.create('Ext.XTemplate',
'<ul id="ds-profiles-boundlist-items"><tpl for=".">',
'<li role="option" class="' + Ext.baseCSSPrefix + 'boundlist-item' + '">',
'{profileToken}',
'</li>',
'</tpl></ul>'
),
listConfig: {
id: 'testcombo-boundlist'
}
},
And the store:
Ext.define('TestProject.testStore',{
extend: 'Ext.data.Store',
storeId: 'teststore',
model: 'TestProject.testModel',
pageSize: 13, //the bulk of records to receive after each upload
currentPage: 0, //server side works with page numeration starting with zero
proxy: {
type: 'rest',
url: serverurl,
reader: 'json'
},
clearOnPageLoad: false //to prevent replacing list items with new uploaded items
});
Credit to me1111 for showing the way.
Ext.define('utils.fields.BoundList', {
override:'Ext.view.BoundList',
///#function utils.fields.BoundList.loadNextPageOnScroll
///Add scroll listener to load next page if true.
///#since 1.0
loadNextPageOnScroll:true,
///#function utils.fields.BoundList.afterRender
///Add scroll listener to load next page if required.
///#since 1.0
afterRender:function(){
this.callParent(arguments);
//add listener
this.loadNextPageOnScroll
&&this.getTargetEl().on('scroll', function(e, el){
var store=this.getStore();
var top=el.scrollTop;
var count=store.getCount()
if(top>=el.scrollHeight-el.clientHeight//scroll end
&&count<store.getTotalCount()//more data
){
//track state
var page=store.currentPage;
var clearOnPageLoad=store.clearOnPageLoad;
store.clearOnPageLoad=false;
//load next page
store.loadPage(count/store.pageSize+1, {
callback:function(){//restore state
store.currentPage=page;
store.clearOnPageLoad=clearOnPageLoad;
el.scrollTop=top;
}
});
}
}, this);
},
});
You can implement the infinite grid as list of the combobox.
Look at this example to implement another picker:
http://www.sencha.com/forum/showthread.php?132328-CLOSED-ComboBox-using-Grid-instead-of-BoundList
If anyone needs this in ExtJS version 6, here is the code:
Ext.define('Test.InfiniteCombo', {
extend: 'Ext.form.field.ComboBox',
alias: ['widget.infinitecombo'],
/**
* This will contain scroll position when user reaches the bottom of the list
* and the store begins to upload data
*/
beforeRefreshScrollTop: 0,
/**
* This will be changed to true, when there will be no more records to upload
* to combobox
*/
isStoreEndReached: false,
/**
* The main thing. When creating picker, we add scroll listener on list dom element.
* Also add listener on load mask - after load mask is hidden we set scroll into position
* that it was before new items were loaded to list. This prevents 'jumping' of the scroll.
*/
createPicker: function () {
var me = this,
picker = me.callParent(arguments);
me.mon(picker, {
'afterrender': function () {
picker.on('scroll', me.onScroll, me);
me.mon(picker.loadMask, {
'hide': function () {
picker.scrollTo(0, me.beforeRefreshScrollTop,false);
},
scope: me
});
},
scope: me
});
return picker;
},
/**
* Method which is called when user scrolls the list. Checks if the bottom of the
* list is reached. If so - sends 'nextPage' request to store and checks if
* any records were received. If not - then there is no more records to load, and
* from now on if user will reach the bottom of the list, no request will be sent.
*/
onScroll: function () {
var me = this,
parentElement = me.picker.getTargetEl(),
scrollingList = Ext.get(me.picker.id + '-listEl');
if (scrollingList != undefined) {
if (!me.isStoreEndReached && me.picker.getScrollY() + me.picker.getHeight() > parentElement.getHeight()) {
var store = me.getStore(),
beforeRequestCount = store.getCount();
me.beforeRefreshScrollTop = me.picker.getScrollY();
store.nextPage({
params: this.getParams(this.lastQuery),
callback: function () {
me.isStoreEndReached = !(store.getCount() - beforeRequestCount > 0);
}
});
}
}
},
/**
* Took this method from Ext.form.field.Picker to collapse only if
* loading finished. This solve problem when user scrolls while large data is loading.
* Whithout this the list will close before finishing update.
*/
collapse: function () {
var me = this;
if (!me.getStore().loading) {
me.callParent(arguments);
}
},
/**
* Reset scroll and current page of the store when loading all profiles again (clicking on trigger)
*/
doRawQuery: function () {
var me = this;
me.beforeRefreshScrollTop = 0;
me.getStore().currentPage = 1;
me.isStoreEndReached = false;
me.callParent(arguments);
}
});
First of all thanks to #me1111 for posting the code for dynamic rendering on scroll. That code is working for me after doing a small change.
tpl: Ext.create('Ext.XTemplate',
'<ul id="testcombo-boundlist-items"><tpl for=".">',
'<li role="option" class="' + Ext.baseCSSPrefix + 'boundlist-item' + '">',
'{profileToken}',
'</li>',
'</tpl></ul>'
),
listConfig: {
id: 'testcombo-boundlist'
}
in code from <ul id="ds-profiles-boundlist-items"><tpl for="."> i have changed id to "testcombo-boundlist-items". as we defined id as 'testcombo-boundlist' in listConfig.
Before modification, in onScroll method scrollingList = Ext.get(me.picker.id + '-items'); i am getting scrollList as a null. because me.picker.id will return 'testcombo-boundlist' on this we are appending '-items' so it became 'testcombo-boundlist-items' but this id does not exists. so iam getting null.
After modification of id in <ul id="testcombo-boundlist-items"> we have a list object on "testcombo-boundlist-items". so i got a object.
Hope this small change will help.

Extjs4 add an empty option in a combobox

I have a combobox in ExtJS4 with this initial config
xtype: 'combobox',
name: 'myCombo',
store: 'MyStore',
editable: false,
displayField: 'name',
valueField: 'id',
emptyText: 'Select an Option'
I don't know if there is an easy way to tell the combo to add an option so the user can deselect the combo (first he select an option and then he want to not select anything... so he wants to return to "Select an Option")
I solved this before by adding an extra option to the fetched data so I can simulate to have the "Select an Option" as a valid option in the combo but I think there should be a better way.
You do not need any new design or graphics or any complex extensions. ExtJS has is all out of the box.
You should be able to use this:
Ext.define('Ext.ux.form.field.ClearCombo', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.clearcombo',
trigger2Cls: 'x-form-clear-trigger',
initComponent: function () {
var me = this;
me.addEvents(
/**
* #event beforeclear
*
* #param {FilterCombo} FilterCombo The filtercombo that triggered the event
*/
'beforeclear',
/**
* #event beforeclear
*
* #param {FilterCombo} FilterCombo The filtercombo that triggered the event
*/
'clear'
);
me.callParent(arguments);
me.on('specialkey', this.onSpecialKeyDown, me);
me.on('select', function (me, rec) {
me.onShowClearTrigger(true);
}, me);
me.on('afterrender', function () { me.onShowClearTrigger(false); }, me);
},
/**
* #private onSpecialKeyDown
* eventhandler for special keys
*/
onSpecialKeyDown: function (obj, e, opt) {
if ( e.getKey() == e.ESC )
{
this.clear();
}
},
onShowClearTrigger: function (show) {
var me = this;
if (show) {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.setWidth(el.originWidth, false);
el.setVisible(true);
me.active = true;
}
});
} else {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.originWidth = el.getWidth();
el.setWidth(0, false);
el.setVisible(false);
me.active = false;
}
});
}
// ToDo -> Version specific methods
if (Ext.lastRegisteredVersion.shortVersion > 407) {
me.updateLayout();
} else {
me.updateEditState();
}
},
/**
* #override onTrigger2Click
* eventhandler
*/
onTrigger2Click: function (args) {
this.clear();
},
/**
* #private clear
* clears the current search
*/
clear: function () {
var me = this;
me.fireEvent('beforeclear', me);
me.clearValue();
me.onShowClearTrigger(false);
me.fireEvent('clear', me);
}
});
Here's a JSFiddle
And a JSFiddle without a default combo.
Note that both examples don't require any new graphic or style
To the best of my knowledge there is no better way of doing this. Although others may have a hack to do what you do by means of a plugin.
If I'm perfectly honest, no selection option in comboboxes is somewhat of a user interface paradox - users are required to select 'no selection'. A blank option is not very clear - looks a bit like a bug.
From a user interface perspective I think the preferred solution to this scenario is to have a button next to the combobox saying 'clear selection' (or just one with an X icon). Once you manage to put a button there, you can just call clearValue().

Filter store on combobox selection

I have tried this different ways, but still can't get the filter to work. My ext app lets user to choose a single state from a combobox, and the grid below displays more data on that selected "value"=state.. On select, the combobox fires a function that filters the store of the grid and updates the store...
this is my store for the grid...
var store = Ext.create('Ext.data.Store', {
autoLoad: true,
id: 'OurData',
pageSize: 20,
pageNumber: 1,
remoteSort: true,
fields: [
{ name: 'States' },
{ name: 'FullName' },
{ name: 'Capital' },
{ name: 'Population' }
],
proxy: {
type: 'ajax',
url: 'GetState/getS',
reader: {
type: 'json',
root: 'myTable',
idProperty: 'States',
totalProperty: '#count'
}
}
});
store.loadPage(1);
this is my combobox
xtype: 'combo',
id: 'iccombo',
scope: this,
store: this.Combostore,
fieldLabel: 'Short State',
displayField: 'States',
valueField: 'States',
typeAhead: true,
triggerAction: 'all',
queryMode: 'remote',
name: 'State',
labelWidth: 125,
anchor: '95%',
listeners: {
scope: this,
select: this.fireFilter
}
and this is where the filter should take place...
fireFilter: function (value) {
// Get passed value
this.selectedC = value.getValue();
console.log('selectedValue: ' + this.selectedC);
// Clear existing filters
this.store.clearFilter(false);
// Build filter
var myfilter = Ext.create('Ext.util.Filter', {
scope: this,
filterFn: function (item) {
var fieldNames = item.fields.keys;
for (var j = 0; j < fieldNames.length; j++) {
var fieldName = fieldNames[j];
if (item.data[fieldName] != null) {
var stringVal = item.data[fieldName].toString();
if (stringVal != null && stringVal.toLowerCase().indexOf(value.toLowerCase()) != -1) {
return true;
}
}
}
return false;
}
});
// Apply filter to store
this.store.filter(myfilter);
}
when I run the code, it display all data in the grid, and on selection of combobox, it still shows the same data..
For some reason, the code never runs through the filterFn... because my console.log doesn't show up
this is what I got in firebug's response
_dc 1352902173425
filter [{"property":null,"value":null}]
limit 20
page 1
start 0
as you can clearly see, the selected 'value' is null, but my 'console.log' prints the value selected... I think the way I am getting the passed value and applying the filter is incorrect... can someone please take a look... thanks
UPDATE... I am able to get inside the function and my console.log shows all the fields... but once I get to the last if statement... I get this error
TypeError: value.toLowerCase is not a function
What am I doing wrong here? Thanks
In addition to dbrin's anwser I also can't understand why you are using remoteSort but not remoteFilter? You may also have a scope issue by using this.
Anyway I would recommend you to extend a new combo type so that you are also be able to clear your filter if you have the need to. Here is an extension I have written for my own use. Note that the filtering itself needs to be implemented in the onSearch method, which can be either a remote or a local sort.
Ext.define('Ext.ux.form.field.FilterCombo', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.filtercombo',
/**
* #cfg {string} recordField
* #required
* The fieldname of the record that contains the filtervalue
*/
/**
* #cfg {string} searchField
* #required
* The fieldname on which the filter should be applied
*/
/**
* #cfg {boolean} clearable
* Indicates if the clear trigger should be hidden. Defaults to <tt>true</tt>.
*/
clearable: true,
initComponent: function () {
var me = this;
if (me.clearable)
me.trigger2Cls = 'x-form-clear-trigger';
else
delete me.onTrigger2Click;
me.addEvents(
/**
* #event clear
*
* #param {Ext.ux.form.field.FilterCombo} FilterCombo The filtercombo that triggered the event
*/
'clear',
/**
* #event beforefilter
*
* #param {Ext.ux.form.field.FilterCombo} FilterCombo The filtercombo that triggered the event
* #param {String/Number/Boolean/Float/Date} value The value to filter by
* #param {string} field The field to filter on
*/
'beforefilter'
);
me.callParent(arguments);
// fetch the id the save way
var ident = me.getId();
me.on('select', function (me, rec) {
var value = rec[0].data[me.recordField],
field = me.searchField;
me.fireEvent('beforefilter', me, value, field)
me.onShowClearTrigger(true);
me.onSearch(value, field);
}, me);
me.on('afterrender', function () { me.onShowClearTrigger(); }, me);
},
/**
* #abstract onSearch
* running a search on the store that may be removed separately
* #param {String/Number/Boolean/Float/Date} val The value to search for
* #param {String} field The name of the Field to search on
*/
onSearch: Ext.emptyFn,
/**
* #abstract onFilterRemove
* removing filters from the the
* #param {Boolean} silent Identifies if the filter should be removed without reloading the store
*/
onClear: Ext.emptyFn,
onShowClearTrigger: function (show) {
var me = this;
if (!me.clearable)
return;
show = (Ext.isBoolean(show)) ? show : false;
if (show) {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.setWidth(el.originWidth, false);
el.setVisible(true);
me.active = true;
}
});
} else {
me.triggerEl.each(function (el, c, i) {
if (i === 1) {
el.originWidth = el.getWidth();
el.setWidth(0, false);
el.setVisible(false);
me.active = false;
}
});
}
// Version specific methods
if (Ext.lastRegisteredVersion.shortVersion > 407) {
me.updateLayout();
} else {
me.updateEditState();
}
},
/**
* #override onTrigger2Click
* eventhandler
*/
onTrigger2Click: function (args) {
this.clear();
},
/**
* #private clear
* clears the current search
*/
clear: function () {
var me = this;
if (!me.clearable)
return;
me.onClear(false);
me.clearValue();
me.onShowClearTrigger(false);
me.fireEvent('clear', me);
}
});
Here is an untested implementation of your combo. Please note that I cleaned up your filterFn but I didn't make any further check.
{
xtype: 'filtercombo',
id: 'iccombo',
scope: this,
store: this.Combostore,
fieldLabel: 'Short State',
displayField: 'States',
valueField: 'States',
typeAhead: true,
triggerAction: 'all',
queryMode: 'remote',
name: 'State',
labelWidth: 125,
anchor: '95%',
// begin new parts
recordField: 'States',
searchField: '',
clearable: false,
onSearch: function (me, value, field) {
// New part!
var store = Ext.StoreMgr.lookup('YourStoreIdName');
// Clear existing filters
store.clearFilter(false);
// Build filter
var myfilter = Ext.create('Ext.util.Filter', {
scope: this,
filterFn: function (item) {
var fieldNames = item.fields.keys,
fieldName, stringVal,
len = fieldNames.length,
j = 0;
for (; j < len; j++) {
fieldName = fieldNames[j];
if (item.data[fieldName] != null) {
stringVal = item.data[fieldName].toString();
if (stringVal != null && stringVal.toLowerCase().indexOf(value.toLowerCase()) != -1) {
return true;
}
}
}
return false;
}
});
// Apply filter to store
store.filter(myfilter);
}
}
I guess this should work too
var myfilter = Ext.create('Ext.util.Filter', {
scope: this,
filterFn: function (rec) {
var fieldValue = rec[this.fieldName];
if (fieldValue && fieldValue === this.value)
return true;
return false;
}
});
I set this before two vars to mark them as from a external scope.
i see 2 issues
store should have remoteFilter: true set
in JavaScript all variables declarations are picked out and hoisted to the beginning of the function. so any variables declared inside a loop should be taken out and declared at the top of the function. JS has no block scope (like Java).

Resources