Extjs: Tree, Selecting node after creating the tree - javascript

I have a simple TreePanel. I would like to select a particular node upon loading it. The nodes are from a remote file (json).
The tree is loading as expected. However, the node is not being selected. Firebug shows node as undefined. This perhaps because of the async property. But, I an unable to configure this other wise, or specify the node be selected.
Any suggestions welcomed, and thank you.
LeftMenuTree = new Ext.tree.TreePanel({
renderTo: 'TreeMenu',
collapsible: false,
height: 450,
border: false,
userArrows: true,
animate: true,
autoScroll: true,
id: 'testtest',
dataUrl: fileName,
root: {
nodeType: 'async',
iconCls:'home-icon',
expanded:true,
text: rootText
},
listeners: {
"click": {
fn: onPoseClick,
scope: this
}
},
"afterrender": {
fn: setNode,
scope: this
}
});
function setNode(){
alert (SelectedNode);
if (SelectedNode == "Orders"){
var treepanel = Ext.getCmp('testtest');
var node = treepanel.getNodeById("PendingItems");
node.select();
}
}

I use this code in the TreeGrid to select a node
_I.treeGrid.getSelectionModel().select(_I.treeGrid.getRootNode());
I haven't tried this in a TreePanel but since the TreeGrid is based on it I'll just assume this works. I used the load event of the loader to plugin similar code after the XHR request was done, so try to write your setNode function like this:
var loader = LeftMenuTree.getLoader();
loader.on("load", setNode);
function setNode(){
alert (SelectedNode);
if (SelectedNode == "Orders"){
var treepanel = Ext.getCmp('testtest');
treepanel.getSelectionModel().select(treepanel.getNodeById("PendingItems"));
}
}

this work for me...
var loader = Ext.getCmp('testtest').getLoader();
loader.on("load", function(a,b,c){
b.findChild("id",1, true).select(); // can find by any parameter in the json
});

I have documented a way to accomplish something very similar here:
http://www.sourcepole.ch/2010/9/28/understanding-what-s-going-on-in-extjs
what you'll need to make sure is that the node that you are selecting is visible. You can accomplish that by traversing the tree and node.expand()ing all the nodes parents (from the root down).

This is because the node isn't really selectable until the tree has been rendered. Try adding your node selection to an event listener listening for the render event.

If you're using a recent enough version of ExtJS then I find using ViewModels and the Selection config far easier for this kind of thing.
Something like:
LeftMenuTree = new Ext.tree.TreePanel({
renderTo: 'TreeMenu',
collapsible: false,
height: 450,
border: false,
userArrows: true,
animate: true,
autoScroll: true,
id: 'testtest',
dataUrl: fileName,
bind: {
Selection: '{SelectedNode}'
},
root: {
nodeType: 'async',
iconCls:'home-icon',
expanded:true,
text: rootText
},
listeners: {
"click": {
fn: onPoseClick,
scope: this
}
"afterrender": {
fn: setNode,
scope: this
}
});
(You'll need to either have a ViewModel set up in the TreePanel or the owning view)
Then assuming you're using a ViewController and setNode is a member:
setNode: function(){
var nodeToSelect = // code to find the node object here
this.getViewModel().set('Selection', nodeToSelect);
}
The nice thing about the ViewModel approach is that it seems to just handle all of the rendering / data loading issues automatically.

Related

Angular with Kendo, Using Grid Values Asynchronously

Ok I'm pretty sure I know exactly what I need to do here but I'm not sure how to do it. Basically I have a grid that I want to make a key column bind to an array of key/values, which I've done before with kendo (not using Angular) and I know that when I'm creating my key/value array asynchronously then that needs to complete before I can get them show-up with kendo, which I have done using promises before.
So here I have the same issue only angular is also involved. I need to fetch and format an array of data into the format in which a kendo grid column can digest it, so no problem here is my controller code:
var realm = kendo.data.Model.define({
id: 'realmID',
fields: {
realmID: { editable: false, nullable: true }
realmType: { type: 'string', validation: { required: true } }
}
})
var ds1 = kendoHelpers.dataSourceFactory('realms', realm, 'realmID')
var realmType = kendo.data.Model.define({
id: 'realmTypeID',
fields: {
realmTypeID: { editable: false, nullable: true },
name: { type: 'string', validation: { required: true } }
}
})
var ds2 = kendoHelpers.dataSourceFactory('realms/types', realmType, 'realmTypeID')
$scope.mainGridOptions = {
dataSource: ds1,
editable: true,
navigatable: true,
autoBind:false,
toolbar: [
{ name: "create" },
{ name: 'save' },
{ name: 'cancel' }
],
columns: [
{ field: 'realmID', title: 'ID' }
{ field: 'realmTypeID', title: 'Realm Type', editor: realmTypesDDL, values: $scope.realmTypeValues },
{ command: "destroy" }
]
}
$scope.secondGridOptions = {
dataSource: ds2,
editable: true,
navigatable: true,
toolbar: [
{ name: "create" },
{ name: 'save' },
{ name: 'cancel' }
],
columns: [
{ field: 'realmTypeID', title: 'ID' },
{ field: 'name', title: 'Name' }
{ command: "destroy" }
]
}
ds2.fetch(function () {
$scope.realmTypeValues = [{ text: 'Test', value: "24bc2e62-f761-4e70-804c-bc36fdeced3d" }];
//this.data().map(function (v, i) {
// $scope.realmTypeValues.push({ text: v.name, value: v.realmTypeID})
//});
//$scope.mainGridOptions.ds1.read()
});
function realmTypesDDL(container, options) {
$('<input />')
.appendTo(container)
.kendoDropDownList({
dataSource: ds2,
dataTextField: 'name',
dataValueField: 'realmTypeID'
});
}
I made this dataSourceFatory helper method above to return me a basic CRUD kendo dataSource that uses transport and also injects an authorization header which is working fine so don't get hung up on that, ultimately I'm going to be using this data in another grid as well as for reference values for the main grid, but I've hard coded some values that I can use to test with in the ds2.fetch callback.
My HTML is pretty plain:
<div>
<h2>Realms</h2>
<kendo-grid options="mainGridOptions"></kendo-grid>
<h2>Realm Types</h2>
<kendo-grid options="secondGridOptions"></kendo-grid>
</div>
This all works fine and well except I am only seeing the GUID of the realmTypeID in the grid, I click it and the editor is populated correctly so that's good but I want the text value to be displayed instead of the GUID. I'm sure the issue is that the array of values is empty whenever angular is binding to the grid options. My questions are:
How do I either delay this bind operation or manually rebind it after the fetch call?
Is there a better way to handle a situation like this? I try not to expend finite resources for no reason (IE making server calls when unnecessary)
Note: When I move the creation of the text/value array to happen before the grid options, I get the desired behavior I am after
EDIT A work around is to not use the directive to create the grid and instead defer the grid creation until the callback of whatever data your column is dependent on, I was hoping for a more elegant solution but this is better than nothing. So your HTML becomes something like
<h2>Realms</h2>
<div id="realms"></div>
<h2>Realm Types</h2>
<kendo-grid options="secondGridOptions"></kendo-grid>
Then you can create the grid in the fetch callback for example:
ds2.fetch(function () {this.data().map(function (v, i) {
$scope.realmTypeValues.push({ text: v.name, value: v.realmTypeID})
});
$('#realms').kendoGrid($scope.mainGridOptions);
$scope.mainGridOptions.dataSource.fetch()
});
But this doesn't feel very angularish so I'm really hoping for a better solution!
Ok...well I think I hacked this enough and without another suggestion I'm going to go forward with this approach. I'm just going to move the binding logic to the requestEnd event of the second grid so that the values array can be populated right before the binding even. I'm also reworking the values array in this method. It is a bit weird though, I think there is some kendo black magic going on with this array because I can't just set it to a new empty array without it breaking completely...which is why I'm poping everything out prior to repopulating the array. That way when something is deleted or edited in the second grid, the DDL in the first grid is updated in the callback.
function requestEnd(e) {
for (var i = $scope.realmTypeValues.length; i >= 0; i--) $scope.realmTypeValues.pop();
var data;
if (e.type == "read")
data = e.response;
else
data = e.sender.data();
data.map(function (v, i) { $scope.realmTypeValues.push({ text: v.name, value: v.realmTypeID }); });
if ($('#realms').data('kendoGrid') == undefined) {
$('#realms').kendoGrid($scope.mainGridOptions);
}
else
$('#realms').data('kendoGrid').columns[4].values = $scope.realmTypeValues;
}
ds2.bind('requestEnd', requestEnd);
So I'm going to accept my own answer unless anyone has a better approach!

afterrender not working for combobox when used in grid in EXTJS

This is my code for combo box inside grid:
{
header: 'FSCS',
dataIndex: 'acntOvrrideTypeCd',
flex: 1,
renderer: function(val, metaData, record, rowIndex, colIndex) {
var id = Ext.id();
var store = new Ext.data.Store({
fields: ['code', 'description'],
data: [{
"code": "",
"description": ""
}, {
"code": "E",
"description": "E"
}, {
"code": "D",
"description": "D"
}, {
"code": "S",
"description": "S"
}]
});
Ext.Function.defer(
(function() {
var cb = Ext.create('Ext.form.ComboBox', {
id: 'acntOvrrideTypeCd-' + rowIndex,
queryMode: 'local',
renderTo: id,
store: store,
forceSelection: true,
triggerAction: 'all',
lazyRender: true,
size: 5,
valueField: 'code',
displayField: 'description',
value: val
//listeners:{
// scope: this,
// 'select': Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex)
//}
});
cb.on(afterrender, function() {
console.log("------- box---" + rowIndex);
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
});
}), 0.25);
console.log("i----------" + id);
return (Ext.String.format('<div id="{0}"></div>', id));
}
}
'afterrender' event is not fired. I need to enable or disable component after its rendered.
Can anyone help?
It's just a typo, afterrender should be in quotes otherwise you will just add the function for undefined event.
cb.on('afterrender',function(){
console.log("------- box---" + rowIndex);
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
});
There are a few problems with your code.
It looks like you're trying to create a combobox in the renderer function of a grid (your code at the top didn't get included in the code block). You're better off using the Ext.grid.plugin.CellEditing plugin instead, which will create a field on demand instead of when the column renders. Plus, every time your grid view refreshes you'll be creating another store and combobox for every row in the grid. Not good for performance, not good for the user experience either.
When calling defer, the duration is in milliseconds, not seconds. Also, you don't need to wrap the function in parenthesis. Just give it the function itself. Like this:
Ext.defer(function(){
// do stuff
}, 25);
Setting lazyRender to true only works if your component is the child of some container that doesn't render all its components immediately (like a tabpanel).
It may be easier to just set the disabled config in the combobox when you create it instead of when you render it, unless you don't have the information available at creation time.
Like nscrob said, when using the on method you need to specify the event as a string. If you use the listeners config (which you have commented out), you can just do:
listeners: {
afterrender: function(){
console.log("------- box---" + rowIndex);
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
},
select: function(){
Ext.getCmp('amlFscsForm').controller.amlShow(rowIndex);
}
}
It's important to note that the scope of these listener functions defaults to the component itself (your combobox) so scope: this is unnecessary. Unless you want the scope to be whatever object is creating this combobox, that is.
The first point is the most important. Look into using the CellEditing (or RowEditing) plugin and I guarantee things will go a lot more smoothly.

extjs 4: how to add button to any custom form

I am currently trying to add a custom button that will be able to call when ever the user wants to add a new button using EXTJS 4.
Here is the TimeButton.js file that i want to use to create the button
Ext.namespace("Ext.controls");
Ext.create('Ext.Button', {
text: 'Time',
renderTo: Ext.getBody(),
handler: function() {
alert('The current time is '+Date())
}
});
Ext.reg('timebutton', Ext.controls.TimeButton);
but when ever i try add it to any form i get the following error
types[config.xtype || defaultType] is not a constructor
Or would it be better to do something like this
Ext.controls.TimeButton = Ext.extend(Ext.Panel, {
initComponent: function(){
Ext.apply(this, {
frame: true
,height: 330
,layout: 'border'
,layout: 'column'
,split: true
,cls: 'DocumentWindow'
,items: []
});
this.documentGrid = new Ext.controls.DocumentGrid({height: 220,
agentId : this.agentId,
columnWidth: 0.5})
Ext.controls.DocumentPanel.superclass.initComponent.apply(this, arguments);
}
From what i understand this happens when trying to instantiate (create) a component that does not exist but i do not see where the error might be! is there an error in the code that I have posted?
Define your button as a class, register it for using with xtype by providing alias property and instantiate it in the parent items container. Here is an example.

How to remove checkall option in extjs checkboxmodel?

How to remove check all option is extjs 4 checkboxmodel?
Regards
When defining a grid (in 4.2.1), set this config option to:
selModel: Ext.create('Ext.selection.CheckboxModel', { showHeaderCheckbox: false }),
(The relevant part is showHeaderCheckbox :false)
I have managed to hide it using pure CSS:
Code:
.x-column-header-checkbox {display:none;}
When you're creating your checkboxmodel, try specifying injectCheckbox: false into its configuration. From the API:
Instructs the SelectionModel whether or not to inject the checkbox header automatically or not. (Note: By not placing the checkbox in manually, the grid view will need to be rendered 2x on initial render.) Supported values are a Number index, false and the strings 'first' and 'last'.
Inside grid panel afterrender event using jquery
listeners: {
afterrender: function (grid) {
$('.x-column-header-checkbox').css('display','none');
}
}
According to API, the type of "header" property is String. Said that, the correct value is ''. It has worked for me on ExtJS 3.4
this.queueSelModel = new Ext.grid.CheckboxSelectionModel({
singleSelect : true, // or false, how you like
header : ''
});
heder:false in config or injectCheckBoxHeader = false hide the entire column. CSS solution is class based so any other widget using the same selection model would also hide the entire check.
In ExtJS 4 a header config can be provided as below to display a blank or custom text in the header.
getHeaderConfig: function() {
var me = this;
showCheck = false;
return {
isCheckerHd: showCheck,
text : ' ',
width: me.headerWidth,
sortable: false,
draggable: false,
resizable: false,
hideable: false,
menuDisabled: true,
dataIndex: '',
cls: showCheck ? Ext.baseCSSPrefix + 'column-header-checkbox ' : '',
renderer: Ext.Function.bind(me.renderer, me),
//me.renderEmpty : renders a blank header over a check box column
editRenderer: me.editRenderer || me.renderEmpty,
locked: me.hasLockedHeader()
};
},
I encountered this issue in ExtJS 4.0.7 version.
First I removed checkbox layout:
.rn-grid-without-selectall .x-column-header-checkbox .x-column-header-text
{
display: none !important;
}
Then I used the following code in afterrender listener of the grid:
afterrender: function (grid) {
this.columns[0].isCheckerHd = false;
}
It is not a good solution but it can be used as a starting point.
Thanks for all the good hints here.
For Sencha 3.4, this is the extremely simple pure CSS I ended up using,
My_Panel_With_a_Grid_Without_Header_CheckBox = Ext.extend(Ext.Panel, {....
cls: 'innerpanel hiddeGridCheckBoxOnSingleSelect',
....}
in my CCS file:
.hiddeGridCheckBoxOnSingleSelect .x-grid3-hd-checker {
visibility:hidden
}
Define {Header: false} in checkboxselectionModel
this.queueSelModel = new Ext.grid.CheckboxSelectionModel({
singleSelect : false,
header : false
});

DataView hides spawning ComboBox

I posted this at the Ext forums a couple of days ago, but no response, so maybe better luck here.
I currently have a combo box loading data from php through ajax. Everything works fine except that when my results are returned, the DataView covers the ComboBox (fig 2.) I have included the relevant code below, so any help would be greatly appreciated.
I may be wrong, but I think I've eliminated CSS problems, as the DataView element is rendered with an absolute position.
alt text http://img.skitch.com/20100216-8t4pmbc3e6mydqqrac9qm9ucj.jpg
fig 1.
alt text http://img.skitch.com/20100216-n5t44g8rua7fawkwjrj49fk7t4.jpg
fig 2.
var dataStore = new Ext.data.JsonStore({
url: '/ajaxGateway.php',
root: 'data',
baseParams: {
useClass: 'App_GeoIP_GeoIP',
useMethod: 'getLocationsStartingWith'
},
fields: [
{name:'text', mapping:'TITLE'},
{name:'stateName', mapping:'STATE_NAME'},
{name:'regionHierarchy', mapping:'REGION_HIERARCHY'},
{name:'id', mapping:'ID', type:'int'},
{name:'lat', mapping:'LATITUDE', type:'float'},
{name:'lng', mapping:'LONGITUDE', type:'float'}
]
});
_
var resultTpl = new Ext.XTemplate(
'<tpl for="."><div class="search-item" style="text-align:left">',
'<span>{text}, <small>{stateName}</small></span>',
'</div></tpl>'
);
_
var locationBasedRulesTree = new Ext.tree.TreePanel({
title: 'Location Based Regions',
height: 329,
width: 480,
autoScroll: true,
useArrows: true,
animate: false,
rootVisible: false,
frame: true,
enableDrag: true,
root: new Ext.tree.AsyncTreeNode({
id:'custom_root'
}),
tbar: new Ext.Toolbar(),
listeners:
{
listenersHandlers...: function(){}
}
});
_
locationBasedRulesTree.getTopToolbar().addField(
new Ext.form.ComboBox({
store: dataStore,
displayField: 'text',
typeAhead: false,
loadingText: 'Finding...',
blankText: "Search for a Place...",
width: (Ext.isIE6) ? 155:200,
hideTrigger: true,
forceSelection: true,
selectOnFocus:true,
tpl: resultTpl,
itemSelector: 'div.search-item',
enableKeyEvents: true,
onSelect: function(record)
{
selectHandler...();
},
listeners:
{
keypress : function(comboBox, event) {
keypressHandler...();
}
}
})
);
Hard to say. The first thing I would do is rip the combo box out of your layout and try rendering it to a plain page and see if you still have this issue (should be simple to do). That will immediately confirm or rule out that it's related to your particular layout. You also did not mention whether or not this happens only in particular browser/OS combinations -- if so, it could be an Ext bug. If it happens in any browser, then it's probably an issue with your layout. Try narrowing it down first then maybe it will be more obvious where to go next.
It looks almost like the listAlign or hideParent are set wrong.. I'm not seeing that in your definition, but would double-check... try setting those to configuration options manually. I've also had issues with IE when not setting the listWidth config property.

Categories

Resources