Im trying to use the example from sencha and configure rowexpander. When the grid renders the expander is already opened for each row and with nothing inside. When i click on its icon the following error is generated:
Uncaught TypeError: Cannot call method 'addCls' of null
Have anyone else faced this issue ? Can someone post an example of hpw to use rowexpander please.
plugins : [{
ptype : 'rowexpander',
rowBodyTpl : ['<div id="NestedGridRow-{rowId}"></div>'],
pluginId : 'rowexpanderplugin',
selectRowOnExpand : true,
// this gives each row a unique identifier based on record's "acct_no"
rowBodyTpl : ['<div id="NestedGrid-{name}" ></div>'],
// stick a grid into the rowexpander div whenever it is toggled open
toggleRow : function(rowIdx) {
var rowNode = this.view.getNode(rowIdx), row = Ext.get(rowNode), nextBd = Ext.get(row).down(this.rowBodyTrSelector), hiddenCls = this.rowBodyHiddenCls, record = this.view.getRecord(rowNode), grid = this.getCmp(), name= record.get('name'), targetId = 'NestedGrid-' + name;
if (row.hasCls(this.rowCollapsedCls)) {
row.removeCls(this.rowCollapsedCls);
this.recordsExpanded[record.internalId] = true;
this.view.fireEvent('expandbody', rowNode, record, nextBd.dom);
if (rowNode.grid) {
nextBd.removeCls(hiddenCls);
rowNode.grid.doComponentLayout();
rowNode.grid.view.refresh();
} else {
// this is the store for the inner grid
Ext.create('innsergridstore', {
autoLoad : {
callback : function() {
// create the inner grid and render it to the row
nextBd.removeCls(hiddenCls);
var grid = Ext.create('NestedGrid', {// <-- this is my "inner" grid view
renderTo : targetId,
store : this,
row : row
});
rowNode.grid = grid;
grid.suspendEvents();
}
}
});
}
} else {
row.addCls(this.rowCollapsedCls);
nextBd.addCls(this.rowBodyHiddenCls);
this.recordsExpanded[record.internalId] = false;
this.view.fireEvent('collapsebody', rowNode, record, nextBd.dom);
}
}
}]
Related
Recently I started working on a multi-platform application using Titanium Alloy.
One of the things I'd like to achieve is having a menu button in the actionbar (infront of the appicon).
When pressed, it toggles the drawermenu.
After a little investigation I failed to find a widget / module that could offer me both. So I decided to use a combination of com.alcoapps.actionbarextras and com.mcongrove.slideMenu.
Both a custom actionbar and a drawer option seem to functionate as they appear they should.
The problem is however, that it does show the 'menu' image, it is clickable, but I have no idea how to attach an event to it.
I've tried several ways, like binding the event to the entire actionbar of what so ever.. But I can't seem to find the appropriate binding to accomplish this.
index.js
var abextras = require('com.alcoapps.actionbarextras');
$.MainWindow.on('open', function(evt) {
// set extras once the Activity is available
abextras.title = "Test Window";
abextras.homeAsUpIcon = "/images/menu.png";
//abextras.titleColor = "#840505";
//abextras.subtitle = "for some extra action";
//abextras.subtitleFont = "Chunkfive.otf";
//abextras.subtitleColor = "#562A2A";
//abextras.backgroundColor = "#F49127";
var activity = evt.source.activity;
if (activity) {
activity.onCreateOptionsMenu = function(e) {
e.menu.clear();
activity.actionBar.displayHomeAsUp = true;
//abextras.setHomeAsUpIcon("/images/menu.png");
//activity.actionBar.addEventListener("click", function(ev) {
// console.log("HI");
//});
};
}
/*
// now set the menus
evt.source.activity.onCreateOptionsMenu = function(e) {
// aboutBtn and creditsBtn will be displayed in the menu overflow
aboutBtn = e.menu.add({
title : "About",
showAsAction : Ti.Android.SHOW_AS_ACTION_NEVER
});
aboutBtn.addEventListener("click", function(e) {
console.log('Clicked on About');
});
creditsBtn = e.menu.add({
title : "Credits",
showAsAction : Ti.Android.SHOW_AS_ACTION_NEVER
});
creditsBtn.addEventListener("click", function(e) {
console.log('Clicked on Credits');
});
// create the Share intent and add it to the ActionBar
var intent = Ti.Android.createIntent({
action : Ti.Android.ACTION_SEND,
type : 'text/plain'
});
intent.putExtra(Ti.Android.EXTRA_TEXT, 'Hello world!');
abextras.addShareAction({
menu : e.menu,
//intent : intent
});
};
*/
});
function doClick(e) {
alert($.label.text);
}
// Create our node items
var nodes = [{
menuHeader : "My Tabs",
id : 0,
title : "Home",
image : "/images/home.png"
}, {
id : 1,
title : "Contact",
image : "/images/phone.png"
}, {
id : 2,
title : "Settings",
image : "/images/gear.png"
}];
// Initialize the slide menu
$.SlideMenu.init({
nodes : nodes,
color : {
headingBackground : "#000",
headingText : "#FFF"
}
});
// Set the first node as active
$.SlideMenu.setIndex(0);
// Add an event listener on the nodes
$.SlideMenu.Nodes.addEventListener("click", handleMenuClick);
// Handle the click event on a node
function handleMenuClick(_event) {
if ( typeof _event.row.id !== "undefined") {
// Open the corresponding controller
openScreen(_event.row.id);
}
}
function openMenu() {
$.AppWrapper.animate({
left : "300dp",
duration : 250,
curve : Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
});
$.SlideMenu.Wrapper.animate({
left : "0dp",
duration : 250,
curve : Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
});
toggleMenu();
}
function closeMenu() {
$.AppWrapper.animate({
left : "0dp",
duration : 250,
curve : Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
});
$.SlideMenu.Wrapper.animate({
left : "-300dp",
duration : 250,
curve : Ti.UI.ANIMATION_CURVE_EASE_IN_OUT
});
toggleMenu();
}
function toggleMenu() {
//
console.log($.AppWrapper.left);
}
$.AppWrapper.addEventListener("swipe", function(_event) {
if (_event.direction == "right") {
openMenu();
} else if (_event.direction == "left") {
closeMenu();
}
});
$.MainWindow.open();
//$.index.open();
index.xml
<Alloy>
<Window class="container" id="MainWindow">
<Require type="widget" src="com.mcongrove.slideMenu" id="SlideMenu" />
<View id="AppWrapper">
<Label text="Profile View" />
</View>
</Window>
</Alloy>
I hope people with more knowledge about Titanium and/or these modules could guide me.
Kind Regards, larssy1
After contacting the creator of the widget, the outcome is as the following:
var activity = evt.source.activity;
if (activity){
activity.actionBar.onHomeIconItemSelected = function() {
// your callback here
alert('I was clicked');
}
}
using these lines of code I can display aproximately hundreds of tableviewrows in a tableview. The problem is that the window is opening in 3 seconds (android device). I guess there's some optimization I have to do in order to display the table in less than 1 sec.
any advice about it?
thanks in advance
EDIT
lines of code
module.exports.draw = function(){
els = [];
var cocktails = Ti.App.cocktails;
for(var i=0;i<cocktails.length;i++){
els.push({
type: 'Ti.UI.View',
searchableText : cocktails[i].nome,
properties : {
cocktail_id:cocktails[i].id,
borderColor:"#eee",
borderWidth: 1,
height: 100,
nome:cocktails[i].nome
},
childTemplates : [
{
type: 'Ti.UI.Label',
bindId : cocktails[i].id,
properties : {
text: cocktails[i].nome,
cocktail_id:cocktails[i].id,
color:"#000",
left:30,
zIndex:10,
top:10,
font:{
fontSize:20,
fontWeight:'bold'
}
},
events : {
click : function(e) {
Ti.App.fireEvent("render",{pag:'prepare',id:e.bindId});
}
}
},
{
type : 'Ti.UI.Label',
properties : {
left:30,
color:"#999",
top:50,
cocktail_id:cocktails[i].id,
text:cocktails[i].ingTxt != undefined?cocktails[i].ingTxt:''
},
bindId:cocktails[i].id,
events : {
click : function (e){
Ti.App.fireEvent("render",{pag:'prepare',id:e.bindId});
}
}
}
]
});
}
var search = Ti.UI.createSearchBar({
height:50,
width:'100%'
});
search.addEventListener('cancel', function(){
search.blur();
});
var content = Ti.UI.createListView({sections:[Ti.UI.createListSection({items: els})],searchView:search});
search.addEventListener('change', function(e){
content.searchText = e.value;
});
return content;
};
You need to look into lazy loading
http://www.appcelerator.com/blog/2013/06/quick-tip-cross-platform-tableview-lazy-loading/
As thiswayup suggests, lazy loading would probably be a very good idea.
If you do not wan't to use lazy loading you could do this:
function getListWindow( items ) {
var firstLayout = true;
var win = Ti.UI.createWindow({
//Your properties here.
});
var list = Ti.UI.createTableView({
data : [],
//other properties here
});
win.add(list);
win.addEventListener('postlayout', function() {
if(firstLayout) {
firstLayout = false;
var rows = [];
//Assuming the items argument is an array.
items.forEach(function( item ) {
rows.push( Ti.UI.createTableViewRow({
//Properties here based on item
}));
});
list.data = rows;
}
});
return win;
}
Doing this will open your window right away and load the rows after the window is shown. Showing a loader while the rows are being generated would probably be a good idea.
I've a grid panel in extJS 4.1.1. With other columns I've an actioncolumn(xtype:'actioncolumn'). I include a handler with that column. When I click it , it works fine and open a new window with many things loading. But I got error when I double click on that column. Advance welcome for any help.......
{
text : 'Signature',
menuDisabled : true,
sortable : false,
id : 'signature',
xtype : 'actioncolumn',
width : 60,
items : [{
icon : "${resource(dir: 'images', file: 'ADD01003.png')}",
tooltip : 'Add Signature',
scope : this,
handler : function(grid, rowIndex, colIndex) {
var records = grid.getStore().getAt(rowIndex).data,
fullName = records.fullName,
nickName = records.nickName,
salutation = grid.getStore().getAt(rowIndex).raw.m00i012001.name,
searchValue = records.id ;
var filters = new Array();
var store =Ext.data.StoreManager.lookup('S02X004001');
store.clearFilter();
filters.push({property:'member', value:searchValue});
store.loadPage(1, {
filters : filters,
callback : function(records, options, success) {
var view = Ext.widget('v02x004001');
view.show();
Ext.getCmp('fullName-sv02x00400104').setValue(fullName);
Ext.getCmp('nickName-sv02x00400104').setValue(nickName);
Ext.getCmp('member-sv02x00400104').setValue(searchValue);
Ext.getCmp('salutation-sv02x00400104').setValue(salutation);
}
});
}
}]
}
Not to resurrect any Zombies, but for anyone having the same Problem in ExtJS 4, 5 or 6 this workaround should work (maybe the Classnames need to be slightly modified, pasted Code is proven to work in 5.1.1).
The trick is to not modify the actioncolumn but to fix the itemdblclick event handler one might have like so:
listeners:
{
itemdblclick: function(v, record, item, index, e)
{
var flyTarget = Ext.fly(e.target);
if(flyTarget.hasCls('x-action-col-icon') || flyTarget.hasCls('x-grid-cell-inner-action-col'))
{
e.stopEvent();
return;
}
//your code here
}
}
How do I get access to the columns/datastore fields that are part of the sort set.
I am looking to modify the a grid's sort parameters for remote sorting. I need the remote sort param's sort key to match the column's field's mapping property. I need these things to happen though the normal 'column header click sorts the data' functionality.
Remote sorting and field mapping (ExtJS 4.1)
This functionality seems not to be implemented in ExtJS. Here is a solution using the encodeSorters function provided since ExtJS 4. Accessing fields map throught the model's prototype is a bit dirty but it does the job :
var store = Ext.create('Ext.data.Store', {
...,
proxy: {
...,
encodeSorters: function (sorters) {
var model = store.proxy.model,
map = model.prototype.fields.map;
return Ext.encode(Ext.Array.map(sorters, function (sorter) {
return {
property : map[sorter.property].mapping || sorter.property,
direction: sorter.direction
};
}));
}
}
});
However, it would be more relevant to override the original method :
Ext.data.proxy.Server.override({
encodeSorters: function(sorters) {
var min, map = this.model.prototype.fields.map;
min = Ext.Array.map(sorters, function (sorter) {
return {
property : map[sorter.property].mapping || sorter.property,
direction: sorter.direction
};
});
return this.applyEncoding(min);
}
});
Assuming you are using simpleSortMode, you could do something like this in your store.
listeners: {
beforeload: function( store, operation, eOpts ) {
if (store.sorters.length > 0) {
var sorter = store.sorters.getAt(0),
dir = sorter.direction,
prop = sorter.property,
fields = store.model.getFields(),
i,
applyProp = prop;
for (i = 0; i < fields.length; i++) {
if (fields[i].name == prop) {
applyProp = fields[i].mapping || prop;
break;
}
}
//clearing the sorters since the simpleSortMode is true so there will be only one sorter
store.sorters.clear();
store.sorters.insert(0, applyProp, new Ext.util.Sorter({
property : applyProp,
direction: dir
}));
}
}
},
I created a View extend Ext.grid.Panel and also attach a tbar to it, in the toolbar I got 2 buttons [Add] and [Remove] in this question I am focus on the [Remove] command only.
As usual I want to get hold to current selected record in the grid which I want to delete.
so in the controller:
init: function() {
this.control({
'extendgridlist button[action=remove]': {
click: this.removeCurrentRow;
}
});
}
removeCurrentRow: function(t){
// how do i get current selected record
}
removeCurrentRow: function(t){
var grid = t.up('gridpanel');
var arraySelected =grid.getSelectionModel().getSelection();
//assuming you have a single select, you have the record at index 0;
var record = arraySelected[0]
}
The answer by 'nscrob' should work just fine, I just wanted to point out an alternate method. Every Ext component can have an 'id' field. So, if you gave your grid a config option like the following when it was created:
id:'my_grid_id'
Then, from anywhere including inside your removeCurrentRow function, you could do the following:
var grid = Ext.getCmp('my_grid_id');
var rows = grid.getSelectionModel().getSelection();
if(!rows.length)
{ //in case this fires with no selection
alert("No Rows Selected!");
return;
}
var row = rows[0];
As I said, similar to other answers, but just another way of accessing the grid.
In case grid is to selType = "cellModel" use code below:
var grid = Ext.getCmp('id-of-grid');
var recid = grid.getSelectionModel().getCurrentPosition();
if(recid && recid.row){
var r = grid.getStore().getAt(recid.row)
alert(r.data.anyofgridfieldid)
}
more details: http://as400samplecode.blogspot.com/2012/01/extjs-grid-selected-row-cell.html
...
xtype: 'button',
text: 'Edit',
handler: function() {
onEdit(this.up('theGrid')); // alias: 'widget.theGrid' in definition
}
...
function onEdit(theGrid) {
if (theGrid.getSelectionModel().hasSelection()) {
var rows = theGrid.getSelectionModel().getSelection();
var row = rows[0];
console.log('Count Rows Selected : ' + rows.length);
console.log('The Row : ' + row); // columns: 'id', 'name', 'age'
console.log('Student Id: ' + row.get('id'));
console.log('Student Name: ' + row.get('name'));
console.log('Student Age: ' + row.get('age'));
}
else {
console.log('No Row Selected.');
}
}