I am trying to use a Dojo dgrid inside a programmatically created Dojo Dialog using the following code:
define(["dojo/_base/declare","dgrid/Grid", "dijit/Dialog"], function (declare, Grid, Dialog){
return declare("modules.egisDataGrid", null, {
showFeedBack:function(){
var myDialog = new Dialog({
title: "FeedBackList",
style: "width: 600px; height:320px;",
content: ""
});
var data = [
{ first: "Bob", last: "Barker", age: 89 },
{ first: "Vanna", last: "White", age: 55 },
{ first: "Pat", last: "Sajak", age: 65 }
];
var grid = new Grid({
columns: {
first: "First Name",
last: "Last Name",
age: "Age"
}
}, myDialog.containerNode);
grid.renderArray(data);
grid.startup();
myDialog.show();
}
});
});
The dialog appears, but the layout is horribly messed up as follows:
I think this is due to the order in which the dgrid is initilized. I have tried everything I could think of, but I still can't figure out how to initilize it properly/
You need to make sure the grid calls its resize method when the dialog is shown. At the moment your grid is started while the dialog is not shown, so it has no idea what to do as far as layout and size goes.
myDialog.on('show', someGridResizeFunction);
And call grid.resize() somewhere in that function
Related
Let's say I have a TabPanel that gets two components added to it. Each component contains an Infinite Grid. Each Infinite Grid loads its data from a service call, and each set of data contains 2,000 records. After the components are added to the TabPanel, we set each one to be the active tab, using setActiveTab. We first set the 2nd tab as the active tab and then set the first tab. When the page loads, the first tab is selected, as we expected.
When looking at the first tab, everything looks fine... we can infinitely scroll, sort, hide columns, etc. However, if we switch to the second tab, we see that it has partially loaded, and we can't scroll past the x records that have loaded, hide columns, sort, etc. It's almost as if using setActiveTab was a bit premature with rendering the grid... as if the store wasn't completely loaded, but the tab was rendered anyway. (this is what I'm assuming the issue is)
I do have code, but it takes a little work on your end to reproduce (because you need a service call). I'm using CompoundJS within a Node.js application, so it was very easy for me to create the test case. If you have access to a database, and can make a quick service call, just modify my Ext JS code, but if you want to use Node.js, you can try this:
Ext JS 4.2.1
Ext.onReady(function() {
var tabPanel = Ext.create('Ext.tab.Panel', {
width: 400,
height: 400,
renderTo: Ext.getBody()
});
Ext.define('myGrid', {
extend: 'Ext.grid.Panel',
constructor: function(config) {
this.columns = config.columns;
this.store = Ext.create('Ext.data.Store', {
fields: config.fields,
buffered: true,
leadingBufferZone: 20,
pageSize: 50,
proxy: {
type: 'ajax',
url: '/getData?id=' + config.id,
reader: {
totalProperty: 'totalCount',
type: 'json',
root: 'root'
}
},
autoLoad: true
});
this.id = config.id;
this.callParent();
}
});
var grid1 = Ext.create('myGrid', {
id: 'blah',
columns: [{
text: 'one',
dataIndex: 'one'
}, {
text: 'two',
dataIndex: 'two'
}, {
text: 'three',
dataIndex: 'three'
}],
fields: ['one', 'two', 'three'],
title: 'grid1'
});
var grid2 = Ext.create('myGrid', {
id: 'bleh',
columns: [{
text: 'one',
dataIndex: 'one'
}, {
text: 'two',
dataIndex: 'two'
}, {
text: 'three',
dataIndex: 'three'
}],
fields: ['one', 'two', 'three'],
title: 'grid2'
});
var c1 = [];
c1.items = [grid1];
c1.title = "BLAH";
c1.layout = 'fit';
var c2 = [];
c2.items = [grid2];
c2.title = "BLEH";
c2.layout = "fit";
tabPanel.add([c1, c2]);
tabPanel.setActiveTab(1);
tabPanel.setActiveTab(0);
});
Node.js code
compound init test && cd test
npm install
compound g s testcontroller
Replace app/controllers/testcontrollers_controller.js with:
load('application');
action('getData', function(data) {
var query = data.req.query;
var id = query.id;
var page = query.page;
var pushObj;
if (id === 'blah') {
pushObj = {
one: 'bye',
two: 'goodbye',
three: 'auf wiedersehen'
};
}
else {
pushObj = {
one: 'hi',
two: 'hello',
three: 'guten tag'
};
}
var obj = [];
for (var i = 0; i < 50; i++) {
obj.push(pushObj);
}
send({
totalCount: 2000,
root: obj,
page: page
});
});
In config/routes.js, remove testcontroller's map.resources line, and add this:
map.get('getData', 'testcontrollers#getData');
In public/index.html, make it a generic HTML file, and add in your links to Ext JS and my above Ext JS code.
Now, once you've done all of that, you should be able to reproduce my issue. The first tab will open and be just fine, but the second tab only loads the first x amount of records and doesn't function properly. I believe this is due to the store not loading completely when setActiveTab is fired, which makes the rendering load an impartial store.
What I want to know is, how do I get this to work properly? I've tried waiting for the store to load, and then adding it to the tab, but that still gives me inconsistent results, as well as tried waiting for the grid to stop rendering, but still, I get inconsistent results... inconsistent meaning, sometimes the grid loads all the way, and the tab is fine, but other times, I get a cannot read property 'length' of undefined in ext-all-dev.js:135,786... which makes it seem like the store hasn't completely loaded, as that line contains a reference to records.length.
If anyone has any ideas, I'd love to hear them! Cross-posted from the Sencha forums.
EDIT: Thanks to #rixo, I was able to reproduce the problem in this example. If you enable Firebug, you'll see the error about property length of undefined, as I stated above.
I tracked the issue down to the plugin caching incorrect size values when it is hidden.
Here's an override that would fix this behavior:
/**
* Prevents BufferedRenderer plugin to break when buffered views are
* rendered or refreshed while hidden, like in a card layout.
*
* Tested with Ext 4.2.1
*/
Ext.define('Ext.ux.Ext.grid.plugin.BufferedRenderer.HiddenRenderingSupport', {
override: 'Ext.grid.plugin.BufferedRenderer'
/**
* Refreshes the view and row size caches if they have a value of 0
* (meaning they have probably been cached when the view was not visible).
*/
,onViewResize: function() {
if (this.rowHeight === 0) {
if (this.view.body.getHeight() > 0) {
this.view.refresh();
}
} else {
this.callParent(arguments);
}
}
});
That being said, I advice against using this and unnecessarily bloating your code base.
You should rather organise your code in a way that avoid this situation. The problem is created by the fact that you're calling setActiveTab multiple time in the same execution frame. If you avoid that, Ext will handle the situation correctly all by itself.
As a general principle, you should try to make your Ext code more declarative... Ext architecture rather expects you to build big configuration objects to create your component at once (allowing for atomic DOM updates, etc.), and use methods that will update the state only later, to drive the components behaviour.
In your precise case, you should use the activeTab option to set the initial tab, and only call setActiveTab later, when you actually need to change the tab. That will save some unnecessary processing at initialization, and will also resolve your rendering issue...
Here's how your code should look like:
Ext.create('Ext.tab.Panel', {
renderTo: Ext.getBody(),
width: 400,
height: 400,
// Use this option to set the initial tab.
activeTab: 2,
// You don't need to overnest your grids into another Panel, you can add
// them directly as children of the tab panel.
items: [
// Ideally, you should give an xtype to your class, and add this to
// avoid instantiating the object yourself... Thus giving Ext more
// control on the rendering process, etc.
Ext.create('myGrid', {
id: 'blah',
title: 'Bleh',
columns: [{
text: 'one',
dataIndex: 'one'
}, {
text: 'two',
dataIndex: 'two'
}, {
text: 'three',
dataIndex: 'three'
}],
fields: ['one', 'two', 'three'],
title: 'grid1'
}),
Ext.create('myGrid', {
id: 'bleh',
title: 'Bleh',
columns: [{
text: 'one',
dataIndex: 'one'
}, {
text: 'two',
dataIndex: 'two'
}, {
text: 'three',
dataIndex: 'three'
}],
fields: ['one', 'two', 'three'],
title: 'grid2'
})
]
});
As you can see, the tab panel child items are set at creation time, avoiding useless processing with the add method. Likewise, the activeTab option avoid to initialize the panel in a given state and change it right away... Later (in another execution frame, for example in a button handler), you can use setActiveTab without triggering the bug demonstrated in your original example.
I know how to set the optionList on Initiliaztion but how do I set it programmatically?
I have an inviteList array:
$("#select-invite").options(inviteList);
You can use load method to set options via programmatic API. You may also want to call clear and clearOptions methods to reset selected values and old options.
Here is how to put it all together:
var selectize = $("#select-invite")[0].selectize;
selectize.clear();
selectize.clearOptions();
selectize.load(function(callback) {
callback(inviteList);
});
Please note that inviteList should be an array of objects that have properties configured in valueField and labelField options when select was initialized. For example, here is how inviteList should look like with default valueField and labelField options:
var inviteList = [
{
text: 'Option One',
value: 1
},
{
text: 'Option Two',
value: 2
}
];
As far as I know there's no method for adding multiple options through the API. You'll need to write a loop that uses the addOption() method. You'll need to get the control instance of selectize before trying to use the API. Take a look at the example below, from the Github examples:
// Create the selectize instance as usual
var $select = $('#select-tools').selectize({
maxItems: null,
valueField: 'id',
labelField: 'title',
searchField: 'title',
options: [
{id: 1, title: 'Spectrometer', url: 'http://en.wikipedia.org/wiki/Spectrometers'},
{id: 2, title: 'Star Chart', url: 'http://en.wikipedia.org/wiki/Star_chart'},
{id: 3, title: 'Electrical Tape', url: 'http://en.wikipedia.org/wiki/Electrical_tape'}
],
create: false
});
// Get the selectize control instance
var control = $select[0].selectize;
// Add the new option when a button is clicked
// Remove the click event and put the addOption call in a loop
$('#button-addoption').on('click', function() {
control.addOption({
id: 4,
title: 'Something New',
url: 'http://google.com'
});
});
From the Github examples.
I know this is an old question but as of 2021 I have found this to be the simplest way to achieve this:
Building on #byte255's answer above, you only need to use the clearOptions method and addOption method.
let selectize = $("#select-invite")[0].selectize;
selectize.clearOptions();
let newOptions = [
{
text: 'Option One',
value: 1
},
{
text: 'Option Two',
value: 2
}
]
selectize.addOption(newOptions);
Faced with a problem at work with ExtJS
There is such a code - a new class (view)
Ext.define('FormApp.view.ElementContainer', {
extend: 'Ext.Container',
alias: 'widget.elemcontainer',
initComponent: function() {
this.items = [{
layout: {
type: 'hbox',
align: 'middle'
},
items: [
{ xtype: 'component',
html: ' Поиск  '},
{ xtype: 'textfield',
width: 495,
name: 'search'
},
{ xtype:'component',
html:'  '},
{ xtype: 'button',
text: 'Найти',
width: 80,
action: 'searchTreeData'}
]}
];
this.callParent();
}
});
Then in the controller I write code like this to get the value of the textfield
Ext.define('FormApp.controller.ControlOne', {
extend: 'Ext.app.Controller',
views: ['ElementContainer', 'TreeView'],
init: function() {
this.control({
'elemcontainer button[action=searchTreeData]': {
click: this.searchTree
},
'elemcontainer textfield[name=search]':{change: this.str}
});
},
searchTree: function(searchStr) {
var dat = Ext.widget('elemcontainer');
var str = dat.down('textfield').getValue();
alert (str.getValue());
},
str: function()
{alert('OK');}
});
I can not get the value of a text field in extJS
How to access the elements to get their values?
Thanks in advance and sorry for my clumsy English
The problem is that by using Ext.widget(...) in searchTree(), you're creating a new instance of the class (be sure to check the docs), rather than getting the of the component that already exists.
Another issue is that str is already the "value" of the textfield, so calling getValue() on str.getValue() won't get you very far either.
So a few suggestions:
Update you searchTree method to pass the correct arguments. Since this method is getting called on the click event of a button, the arguments will be those of the click event for Ext.button.Button : searchTree( btn, e, opts ) {...}
Once you have the correct arguments being passed to searchTree(), you can then use the component selector methods to get the existing instance of the container. For example, since the button is already a descendant of the container, you can do the following to get the correct instance of the component:
var ctr = btn.up( "elemcontainer" )
And now that you have the correct instance of the container, you can again use one of the component selector methods to find the textfield:
var str = ctr.down( 'textfield' ).getValue()
I have several records of the same type that I want to show on the screen. I thought about creating several panels that will print the data of each record. I chose this solution because data structure is too complex to be printed in a simple grid. Here is a simplified example of that structure :
{
label: 'myLabel',
{
attr1: 'value1',
attr2: 'value2'
}
startValidityDate: oneDay,
endValidityDate: anotherDay
}
I try to add dynamically nested panels in my current panel :
var myStore = new Ext.data.Store({
id: 'myStore',
restful: true,
idProperty: 'OID',
root: 'tbas',
proxy: myProxy,
reader: myReader,
autoLoad: false,
listeners: {
'load': function(data){
var records = data.getRange();
var currStore = null;
for(var i=0; i<records.length; i++) {
currStore = new Ext.Panel({
layout:'vBox',
items: [{
xtype: 'textfield',
fieldLabel: I18nManager.get('label_ttbaUI_label'),
name: 'tbaLabel',
value: records[i].data.tbaLabel
},{
xtype: 'textfield',
fieldLabel: I18nManager.get('label_ttbaUI_label'),
name: 'tbaOid',
value: records[i].data.tbaoid
}]
});
recordList.add(currStore);
console.log("------------------------------");
console.log(currStore);
}
recordList.doLayout();
}
}
});
var recordList = new Ext.Panel({
id: 'recordList',
renderTo: 'recordPart',
layout:'vBox',
title: I18nManager.get('label_ttbaUI_selected_tariffs')
});
recordList.doLayout();
In the firebug console, the UI objects seems to be ok.
My problem is that the recordList elements are not visible
I can see that they exist in the FB console, but they are not well printed on the screen.
Did I forgot something that make the elements hidden ? or bad printed ?
I'm sure that it is a CSS problem, some trouble with ext-all.css : when I remove the content of that CSS, I can see my fields
There must be something wrong in the way I wrote the code so that it causes the render problem WDYT ???
I have a table using tabulator.
Everything works great, but I am trying to get autocomplete working with Ajax
What I am trying is:
var customerNumbers = [];
var table = new Tabulator("#edi-table",
ajaxURL: baseUrl + '/PaginatedEndPoint',
pagination: "remote",
paginationSize: 30,
paginationSizeSelector: [30, 60, 100, 200],
ajaxSorting: true,
ajaxFiltering: true,
selectable: true,
cellEdited: function (cell) {
cell.getElement().style.backgroundColor = "#32CD32";
},
dataLoading: function () {
customerNumbers = ["11", "12", "13"];
},
columns: [
{
title: "CustomerNumber", field: "CustomerNumber", headerFilter: "input", editor: "autocomplete", editorParams: {
searchFunc: function (term, values) {
var matches = [];
values.forEach(function (item) {
if (item.value === term) {
matches.push(item);
}
});
console.log(matches);
return matches;
},
listItemFormatter: function (value, title) {
return "Mr " + title;
},
values: customerNumbers
}
}
]
However, this does not show any predictions value predictions for me, it seems that autocomplete is built before "dataLoading" or any other Callback (I have tried many) is called.
I have tried to make an auxilary array in the style of values like {Title: "Mr + title", value: "title"} and then assign it in the searchFunc, and it didn't work despite being returned in matches.
Is it even possible to dynamically create autofill?
It seems like the current autocomplete functionality does not allow for the editorParams to take a function as an argument to set the dropdown values. You can set it with an object of key/values if you can send that via AJAX, but as far as dynamically setting, altering, or searching the data, it seems like that's impossible to do at the moment.
The other option would be use the editor:"select", which can take a function to set its editorParams. It's not the best solution, but it's the one I had to go with at the moment.
There is an open issue on the Tabulator docs, but so far no response from the developers.
I wish I had a better answer for you!