PDA

View Full Version : Uncaught TypeError: Cannot read property 'mergeAttributes' of null



kalyankonduri
8 Jun 2015, 11:16 PM
Hi,

I have an editable grouping grid using ExtJS 4.2.

Occasionally on random records, I get this error "Uncaught TypeError: Cannot read property 'mergeAttributes' of null" upon clicking.

I tried to see a pattern but there is nothing like that, JSON input was fine and so was everything else. It also changes the background color after the click.

No clue currently, so please help me on this.

PS: attached the before and after jpg files for reference.

Thanks
Kalyan

tristan.lee
9 Jun 2015, 5:18 AM
Can you provide a small test case as a fiddle to demonstrate this issue so I can help determine where the error is originating from?

kalyankonduri
10 Jun 2015, 1:59 AM
Thanks for the response. I have links to our internal systems, let me remove the links and make it static. Not sure if that would be ok for testing.

Basically, the issue is random it makes the row normal as in no CSS will be applied. But the same row worked fine yesterday, hence could not get a clear picture.

Console gives the exception and I have cellclick and beforeedit listerners, nothing gets invoked.

Will get back with code.

kalyankonduri
10 Jun 2015, 3:02 AM
Below is the code but I could not remove the calls to our other systems to make it static. Could you comment on the part of code that could lead to issue. Sorry for the raw code.

A clue here: If I click on a column where this issue is there, the previous row is getting selected instead.


****************
var gridFunc = gridFunc || {}
var kk = '';
var unlinkFromProject = Ext.create('Ext.Action', { //
text: 'Unlink Ticket from current Iteration Project',
disabled: false,
handler: function(widget, event) {
var selection = Ext.getCmp('myPanel').getSelectionModel().getSelection();

//Validate if the user can unlink or not
for ( var i = 0; i < selection.length; i++) {
if(selection[i].data["sm"] == "N"){
Ext.Msg.alert('Unlink Validation', 'You should be Scrum Master/Dev Manager/Project Manager to do this operation!');
return;
}

if(selection[i].data["projectlist"] != ""){
var pjlist = selection[i].data["projectlist"];
if(pjlist.indexOf(';')<=0 ){
Ext.Msg.alert('Unlink Validation', 'This ticket is linked only to current project. You cannot unlink this ticket. You have to move it to another project via Lotus client!');
return;
}
}
};

var theJSON=[];
var curPrject = getParameter("pid");
var cuser = getParameter("pid1");
theJSON.push({'docid': selection[0].data["docid"],
'projectid': curPrject,
'action': 'unlinkProject',
'curuser': cuser}
)
GetAgentData(JSON.stringify(theJSON));
}
})


//context menu
gridFunc = {
self: this,
grid: function(){
return Ext.getCmp('myPanel')
},
contextMenu: Ext.create('Ext.menu.Menu', {
items: [
unlinkFromProject
]
})
}


function aRandomNumber() {
return Math.floor(Math.random()*1000001)
}


function updateTicketData(txtdata){
Ext.Ajax.request({
url: '/bugdatabase.nsf/(json_readArgument)?openagent',
method: 'POST',
headers: {'Content-Type': 'application/json' },
jsonData : txtdata,
success: function(conn, response, options, eOpts) {
Ext.Msg.alert('Back-end data updated!!!')
},
failure: function(conn, response, options, eOpts) {
Ext.Msg.alert('Back-end data NOT updated, please report to Internal Solutions Team!!!');
console.log(conn.responseText)
}
});
}


function getGridData(url, callback){
var dataURL = url
$.ajax({
url: dataURL
}).done(function (data) {
//one the data is loaded then pass it to the callback function
//passed in as one of the function arguments
if(callback){
callback(data)
}
});
}


//readableDuration is used to convert duration in seconds to hrs in 00.00 format
var readableDuration = (function() {
// Each unit is an object with a suffix s and divisor d
var units = [
{s: '', d: 1}, // Seconds
{s: '', d: 60}, // Minutes
{s: ':', d: 60} // Hours
];
// Closure function
return function(t) {
t = parseInt(t); // In order to use modulus
var trunc, n = Math.abs(t), i, out = []; // out: list of strings to concat
for (i = 0; i < units.length; i++) {
n = Math.floor(n / units[i].d); // Total number of this unit
// Truncate e.g. 26h to 2h using modulus with next unit divisor
if (i+1 < units.length) // Tweak substr with two digits
trunc = ('00'+ n % units[i+1].d).substr(-2, 2); // ?if not final unit
else
trunc = n;
if(i>0)
out.unshift(''+ trunc + units[i].s); // Output
}
//(t < 0) ? out.unshift('-') : null; // Handle negative durations
return out.join('');
};
})();


//getParameter is used to read URL for PID
function getParameter(v) {
var query = window.location.search.substring(1);
var vars = query.split("?");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
var usr = pair[1].split("&");
// console.log(usr[0]);
// console.log(usr[1]);
if(pair[0]== v){
return usr[0];
}else{
return usr[1];
}
}
return "";
}


var myStr = "";


function getProjectDetails (){
var pid = getParameter("pid")
var theJSON=[];
var url = '/bugdatabase.nsf/(json_readArgument)?openagent';
theJSON.push({
'projectid': pid,
'action': 'getprojectdetails'
});
var result = ''
var retVal = "#"
Ext.Ajax.request({
url: url,
method: 'POST',
headers: {'Content-Type': 'application/json'},
jsonData : theJSON,
scope: this,
success: function(conn, response, options, eOpts) {
result = conn.responseText;
var jsn = JSON.parse(result);
var popupWin = "#";
winurl = window.location.hostname;
if (winurl=='lotusdr01.portware.com'){
winurl = 'mother.portware.com';
}
if (jsn.projectunid != ""){
popupWin = 'notes://'+winurl+'/bugdatabase.nsf/0/' + jsn.projectunid+'?opendocument';
retVal = 'Iteration Project by Resource view - <a href = "'+popupWin+'" >'+ jsn.projectname + '</a>'
Ext.getCmp('myPanel').setTitle(retVal);
}
},
failure: function(conn, response, options, eOpts) {
console.log(conn.responseText)
}
});
return retVal
};


function createGrid(data){
var groupingFeature = Ext.create('Ext.grid.feature.GroupingSummary',{
id: 'group',
ftype: 'groupingsummary',
groupHeaderTpl: '{name} (Available hrs: {[values.rows[0].data.avail]} ; To be planned: {[values.rows[0].data.tbp]})',
// groupHeaderTpl: [
// '<div>{columnName:this.formatName}</div>',
// {
// formatName: function(c) {
// console.log(c)
// console.log(Ext.)
// if (columnName == 'Name'){
// return 'l'
// }else {
// return 'm'
// }
// }
// }
// ],
enableGroupingMenu: true,
startCollapsed: true
});

store = new Ext.data.Store({
groupField: "name",
groupDir: 'ASC',
model : 'Person',
autoLoad: true,
autoDestroy: true,
sorters: {
property : 'name',
direction: 'ASC'
},
id: 'store',
data: data,
remoteFilter: false,
buffered: false,
proxy: {
type: 'memory'
}

});

var filters = {
ftype: 'filters',
local: true // defaults to false (remote filtering)
};

gridDock = [
{
id: 'activeStorePaging',
xtype: 'toolbar',
dock: 'top',
items: [
{
text: 'Clear Filter Data',
handler: function () {
grid.filters.clearFilters();
}
}
]
},
'-',
{
id: 'activeStorePaging1',
xtype: 'toolbar',
dock: 'top',
items: [
{
text: 'Expand All',
handler: function () {
gridFunc.grid().features[0].expandAll();
}
}
]


},
'-',
{
id: 'activeStorePaging2',
xtype: 'toolbar',
dock: 'top',
items: [
{
text: 'Collapse All',
handler: function () {
gridFunc.grid().features[0].collapseAll();
}
}
]
},
'-',
{
id: 'activeStorePaging3',
xtype: 'toolbar',
dock: 'top',
items: [
{
text: 'Save Ticket Tracker data',
handler: function (){
var theJSON=[];
var allRecords = grid.getStore().getUpdatedRecords();
var prj = '"projectid":'+'"'+getParameter("pid")+'"';
var cuser = getParameter("pid1");
var action = '"action":'+'"updateTicketDet"'
for (i=0;i < allRecords.length;i++)
{
var dataNew = allRecords[i].data;
if (dataNew.sm == "N"){
if (Ext.Msg.alert('You cannot save back-end data since you are not Scrum Master')){
// window.location.reload(true)
}
return
}
// console.log(dataNew.sm)

var retStr = JSON.stringify(dataNew);
retStr = '{'+retStr.substring(1,retStr.length-1) +"," +prj;
if (kk == ''){
retStr = retStr +','+action+'}';
}else{
retStr = retStr +','+action+',"devrep":"'+kk+'"}';
}

console.log(retStr)
updateTicketData(retStr);
}
window.location.reload(true)
}
}
]
},
'-',
{
id: 'activeStorePaging4',
xtype: 'toolbar',
dock: 'top',
bodyStyle: "background: #FF0000;" ,
items: [
{
text: 'Clear Changes',
handler: function (){
var allRecords = grid.getStore().getUpdatedRecords();
if(allRecords.length > 0) {
Ext.Msg.confirm('Confirm', 'Are you sure you want to clear the changes?', showResult);
}
}
}
]
}
]

buffRend = new Ext.grid.plugin.BufferedRenderer( {
pluginId: 'buff1'
});

function showResult(btn){
if(btn=="yes"){
store.rejectChanges()
}
};


var cellEditing = Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1,
listeners: {
'beforeedit': function (editor, e){
var retVal = false;
var editableCols = [1,4,5,7,10,13,14,15,16]
if (editableCols.indexOf(e.colIdx) >=0){
if (e.record.data.status == "Done"){
if(e.colIdx ==3 ){
Ext.Msg.alert('Task is already Done. You cannot Edit back-end data.')
//this is to set back the value selected
e.record.set('planned',!e.value);
}
return retVal;
}
retVal = true;
}
return retVal;
},
'validateedit' : function(editor, e){
if (e.colIdx == 7){
var theJSON=[];
var curPrject = getParameter("pid");
var cuser = getParameter("pid1");
var url = '/bugdatabase.nsf/(json_readArgument)?openagent';
theJSON.push({
'docid': e.record.data.docid,
'projectid': curPrject,
'action': 'validateEstimates',
'plannedestimate' : e.value,
'itpid' : e.record.data.itpid,
'role': e.record.data.role,
'curuser': cuser
});

Ext.Ajax.request({
url: url,
method: 'POST',
headers: {'Content-Type': 'application/json' },
jsonData : theJSON,
success: function(conn, response, options, eOpts) {
var result = conn.responseText;
var jsn = JSON.parse(result);
if (!jsn.isValid) {
Ext.Msg.alert('Cannot exceed Ticket Duration: '+ jsn.totalD);
return false;
} else return true;
},
failure: function(conn, response, options, eOpts) {
console.log(conn.responseText)
}
});
}
}
}
});

var grid = Ext.create('Ext.grid.Panel', {
id: 'myPanel',
renderTo: 'gridHere',
height:880,
frame: true,
// title: 'Iteration by Resource View - <a href = "http://yahoo.com" target ="_blank" >'+getParameter("pid") +'</a>',
title: getProjectDetails(),
plugins: [buffRend,cellEditing],
columnLines:true,
features: [groupingFeature, filters],
store: store,
stateful: false,
viewConfig: {
stripeRows: true,
getRowClass: function(record) {
if (record.get('color') =='green'){
return 'custom-column-green'
}else if (record.get('color') =='red'){
return 'custom-column-red'
}else return 'custom-column-yellow'
},
listeners: {
itemcontextmenu: function(view, rec, node, index, e) {
e.stopEvent();
gridFunc.contextMenu.showAt(e.getXY());
return false;
}
}
},
listeners: {
itemdblclick: function(dataview, record, item, index, e) {
var selection = grid.getSelectionModel().getSelection();
var winurl = window.location.hostname;
if (winurl=='lotusdr01.portware.com'){
winurl = 'mother.portware.com';
}
var popupWin = window.open('notes://'+winurl+'/bugdatabase.nsf/0/'+selection[0].data.docid+'?opendocument');
setTimeout(function () { popupWin.close()}, 3000);
},
'cellclick': function( gridView,htmlElement,columnIndex,dataRecord ){
console.log(columnIndex)
var editableCols = [1,4,5,7,10,13,14,15,16]
if (editableCols.indexOf(columnIndex) >=0){
if(dataRecord.data.status == "Done"){
Ext.Msg.alert('Task is already Done. You cannot Edit back-end data.')
return false;
}
if(dataRecord.data.sm == "N"){
Ext.Msg.alert('You cannot Edit back-end data since you are not Scrum Master')
return false;
}
}else return false;
}
},
columns: [
{
text: '',
width: 10,
renderer: function(value, metaData, record) {
if (record.get('color') =='green'){
metaData.style = 'background-color: green';
metaData.tdAttr = 'data-qtip="Ticket is WITHIN estimated date limits"';
}else if (record.get('color') =='red'){
metaData.tdAttr = 'data-qtip="Ticket is BEYOND estimated date limits"';
metaData.style = 'background-color: #900';
}else
{
metaData.tdAttr = 'data-qtip="Ticket is WITHIN estimated date limits AND is INPROGRESS"';
metaData.style = 'background-color: #B8860B';
}


return value;
},
filterable: true
},
{
id:'name',
header: 'Name',
sortable: true,
dataIndex: 'name',
queryMode: 'local',
forceSelection: true,

editor: {
xtype: 'combobox',
displayField: 'display',
valueField: 'display',
typeAhead: true,
minChars: 1,
filterable: true,
listeners: {
change: function (name, newValue, oldValue, records) {
kk = newValue+'@'+oldValue // to know who replaces who
}
},
store: new Ext.data.Store({
id: 'comboStore',
autoLoad: true,
scope: this,
fields: ['display'],
proxy: {
type: 'ajax',
scope: this,
reader: 'json',
url : '/bugdatabase.nsf/($json_agt_getRsc_ByPID)?openagent&pid='+getParameter('pid')
}
})
}
},{
text: 'Role',
sortable: true,
dataIndex: 'role',
field: 'textfield',
filterable: true
},{
text: 'Status',
sortable: true,
dataIndex: 'status',
field: 'textfield',
filterable: true
},{
text: 'Goal',
sortable: true,
dataIndex: 'goal',
field: 'textfield',
filterable: true
},{
id:'planned',
text: 'Planned',
field: 'textfield',
// xtype:'checkcolumn',
width: 50,
dataIndex: 'planned',
type: 'bool',
filterable: true
},{
text: 'Subject',
width: 180,
sortable: true,
dataIndex: 'subject',
field: 'textfield',
renderer: function(value, metaData, record) {
metaData.tdAttr = 'data-qtip="' + record.get('subject') + '"';
return value;
},
filterable: true
},{
text: 'Planned Estimate',
width: 80,
sortable: true,
dataIndex: 'plannedestimate',
field: 'textfield',
renderer: function(v, params, record,row){
if (String(v) == '0.00'){
return ' '
}else return String(v) ;
},
summaryType: function(records){
var helpDuration=0;
Ext.Array.forEach(records, function (record){
helpDuration += Number(record.data.plannedestimate);
});
return helpDuration.toFixed(2);
},
filterable: true
},{
text: 'Total Duration',
width: 80,
sortable: true,
dataIndex: 'totalestimate',
field: 'textfield',
summaryType: function(values){
var helpDuration=0;
Ext.Array.forEach(values, function (record){
helpDuration += Number(record.data.totalestimate);
});
return helpDuration.toFixed(2);
},
renderer: function(v, params, record,row){
if (String(v) == '0.00'){
return ' '
}else return String(v) +' Hrs' ;
},
filterable: true
},{
text: 'Actual Duration',
width: 80,
sortable: true,
dataIndex: 'actualduration',
field: 'textfield',
summaryType: function(values){
var helpDuration=0;
Ext.Array.forEach(values, function (record){
helpDuration += Number(record.data.actualduration);
});
return helpDuration.toFixed(2);
},

renderer: function(v, params, record,row){
if (String(v) == '0.00'){
return ' '
}else
return String(v) +' Hrs';
},
filterable: true
},{
text: 'Priority',
width: 50,
sortable: true,
dataIndex: 'priority',
field: 'textfield',
filterable: true
},{
text: 'Ticket ID',
width: 80,
sortable: true,
dataIndex: 'ticketid',
field: 'textfield',
filterable: true,
hidden: true
},{
text: 'Type',
width: 80,
sortable: true,
dataIndex: 'type',
field: 'textfield',
filterable: true
},{
text: 'Start Date',
name: 'startValidityDate',
id : 'startValidityDate',
xtype:'datecolumn',
width: 80,
sortable: true,
dataIndex: 'startdate',
applyTo : 'startValidityDate',
editor: {
xtype: 'datefield',
allowBlank : true,
disabledDays: [0,6],
format:'m/d/Y'
},
filterable: true
},{
text: 'End Date',
name:'endValidityDate',
id:'endValidityDate',
xtype:'datecolumn',
width: 80,
sortable: true,
dataIndex: 'enddate',
applyTo : 'endValidityDate',
editor: {
xtype: 'datefield',
disabledDays: [0,6],
allowBlank : true,
format:'m/d/Y',
listeners: {
'select': function (recordIndex) {
if (this.getValue() != '') {
console.log(this.getValue())
}
}
}
},
filterable: true
},{
text: 'Comments',
width: 100,
sortable: true,
dataIndex: 'comments',
renderer: function(value, metaData, record) {
metaData.tdAttr = 'data-qtip="' + record.get('comments') + '"';
return value;
},
field: 'textfield',
filterable: true

},{
text: 'Keywords',
width: 100,
sortable: true,
dataIndex: 'keywords',
renderer: function(value, metaData, record) {
metaData.tdAttr = 'data-qtip="' + record.get('keywords') + '"';
return value;
},
field: 'textfield',

filterable: true
},{
text: 'docid',
width: 1,
dataIndex: 'docid',
field: 'textfield',
hidden: true
},{
text: 'projectlist',
width: 1,
dataIndex: 'projectlist',
field: 'textfield',
hidden: true
},{
text: 'curuser',
width: 1,
dataIndex: 'curuser',
field: 'textfield',
hidden: true
},{
text: 'sm',
width: 1,
dataIndex: 'sm',
field: 'textfield',
hidden: true
},{
text: 'color',
width: 1,
dataIndex: 'color',
field: 'textfield',
hidden: true
},{
text: 'avail',
width: 1,
dataIndex: 'avail',
field: 'textfield',
hidden: true
},{
id:'tbp',
name:'tbp',
text: 'To be planned',
width: 1,
dataIndex: 'tbp',
field: 'textfield',
hidden: true
},{
id:'avail',
name:'avail',
text: 'Available Hrs',
width: 1,
dataIndex: 'avail',
field: 'textfield',
hidden: true
},],
tbar: gridDock
// paging bar on the bottom
//bbar: gridDock
});
}


Ext.Loader.setConfig({
enabled : true,
disableCaching : false
});


Ext.require([
'Ext.selection.CellModel',
'Ext.data.*',
'Ext.grid.*',
'Ext.grid.plugin.BufferedRenderer',
'Ext.ux.grid.FiltersFeature',
'Ext.ux.CheckColumn',
'Ext.util.*',
'Ext.Action'
]);


Ext.define('Person', {
extend: 'Ext.data.Model',
fields: ['name', 'role', 'status', 'goal', 'planned', 'subject', 'plannedestimate', 'actualduration', 'priority', 'ticketid', 'type', 'startdate', 'enddate', 'comments','keywords','projectlist','curuser', 'sm', 'devrev', 'qarev', 'itpid' ,'docid','color','tbp','avail','totalestimate']
});


Ext.onReady(function(){


//We are going to pass to the getDataGrid function the url we want
//and the call back function which we then wants executing once the data is retrieved
//doing it this way gives us maximum control over what happens to the data
//and makes the function more generic should we want to use it for other data
//and other grids in the future
Ext.tip.QuickTipManager.init();
var pID = getParameter("pid");
var cUser = getParameter("p")
var url = '/bugdatabase.nsf/(CPOC-$agt_createJSONData_Project)?openagent&pid='+pID+'&user='+cUser, theData;
var theCallBack = function(theData){
createGrid(theData)
};
getGridData(url, theCallBack);
$("#Loading").html(' ');
});

tristan.lee
16 Jun 2015, 8:10 AM
I would recommend stepping through the debugger to figure out which line this is getting called on. mergeAttributes is located a few different places in the source so without being able to recreate, it's not easy to determine the cause.