Rally App SDK getting object ID of feature - extjs

var showAssignedProgram = 1;
var value = null;
var showIterationCombo = 0;
var iterationComboValue = null;
var lumenize = window.parent.Rally.data.lookback.Lumenize;
var iterationComboField = null;
var iterationRecord = myMask = null;
var setOfStories = setOfFeatures = null;
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
//Write app code here
Ext.state.Manager.setProvider(
new Ext.state.CookieProvider({ expires: new Date(new Date().getTime()+(10006060247)) })
);
app = this;
var that = this;
console.log("launch");
// get the project id.
this.project = this.getContext().getProject().ObjectID;
// get the release (if on a page scoped to the release)
var tbName = getReleaseTimeBox(this);
var configs = [];
configs.push({ model : "Release",
fetch : ['Name', 'ObjectID', 'Project', 'ReleaseStartDate', 'ReleaseDate' ],
filters:[]
});
configs.push({ model : "Iteration",
fetch : ['Name', 'ObjectID', 'Project', 'StartDate', 'EndDate' ],
filters:[]
});
async.map( configs, this.wsapiQuery, function(err,results) {
that.releases = results[0];
that.iterations = results[1];
if (showAssignedProgram)
that.createAssignedProgramCombo();
that.createIterationCombo(that.iterations);
});
},
wsapiQuery : function( config , callback ) {
Ext.create('Rally.data.WsapiDataStore', {
autoLoad : true,
limit : "Infinity",
model : config.model,
fetch : config.fetch,
filters : config.filters,
listeners : {
scope : this,
load : function(store, data) {
callback(null,data);
}
}
});
},
createAssignedProgramCombo : function() {
// assigned Program (if set to true)
this.assignedProgramCombo = Ext.create("Rally.ui.combobox.FieldValueComboBox", {
model : "PortfolioItem/Feature",
field : "AssignedProgram",
stateful : true,
stateId : "assignedProgramCombo",
noData: false,
listeners:{
scope: this,
change: function(field,eOpts){
if(value!="" && value!=null)
{
this.afterCollapse(fieldValue,value);
}
}
}
});
this.add(this.assignedProgramCombo);
},
createIterationCombo: function(iterationRecords){
//console.log("Iteration records ",iterationRecords);
iterationRecord = iterationRecords;
var iterations = _.map(iterationRecords, function(rec){return {name: rec.get("Name"), objectid: rec.get("ObjectID"), startDate: new Date(Date.parse(rec.get("StartDate")))};});
console.log('iterations', iterations);
iterations = _.uniq(iterations, function(r){return r.name;});
iterations = _.sortBy(iterations, function(rec){return rec.StartDate;}).reverse();
var iterationStore = Ext.create('Ext.data.Store', {
fields: ['name','objectid'], data : iterations
});
var cb = Ext.create('Ext.form.ComboBox',{
fieldLabel: 'Iterations',
store: iterationStore,
queryMode: 'local',
displayField: 'name',
valueField: 'name',
listeners:{
scope: this,
change: function(field, eOpts){
console.log('field ', field, ' eOpts ',eOpts);
iterationComboValue = eOpts;
iterationComboField = field;
},
collapse: function(field, eOpts){
this.afterCollapse(field,eOpts);
}
}
});
this.add(cb);
},
afterCollapse: function(field,eOpts){
var r = [];
_.each(field.getValue().split(","), function(rn){
var matching_iterations = _.filter(iterationRecord, function(r){return rn == r.get("Name");});
var uniq_iterations = _.uniq(matching_iterations, function(r){return r.get("Name");});
_.each(uniq_iterations,function(iteration){r.push(iteration);});
});
if(r.length>0){
myMask = new Ext.LoadMask(Ext.getBody(), {msg:"Please wait..."});
myMask.show();
this.selectedIterations = r;
this.queryFeatures(r);
}
},
queryFeatures: function(iterations){
var that = this;
var filter = null;
if (showAssignedProgram && this.assignedProgramCombo.getValue() != null && this.assignedProgramCombo.getValue() != "") {
console.log("assingedValue",this.assignedProgramCombo.getValue());
filter = Ext.create('Rally.data.QueryFilter', {
property: 'AssignedProgram',
operator: '=',
value: this.assignedProgramCombo.getValue()
});
}
else{
_.each(iterations, function(iteration, i){
var f = Ext.create('Rally.data.QueryFilter', {
property: 'Iteration.Name',
operator: '=',
value: iteration.get("Name")
});
filter = i === 0 ? f : filter.or(f);
});
}
console.log("filter",filter.toString());
var configs = [];
configs.push({
model: 'PortfolioItem/Feature',
fetch: ['ObjectID','FormattedID','UserStories' ],
filters: [filter],
listeners: {
load: function(store, features) {
setOfFeatures = features;
console.log("# features",features.length,features);
that.StartDate = that.startDate(iterations);
that.start = _.min(_.pluck(iterations,function(r) { return r.get("StartDate");}));
isoStart = new lumenize.Time(that.start).getISOStringInTZ("America/Chicago");
console.log("isoStart1",isoStart);
that.end = _.max(_.pluck(iterations,function(r) { return r.get("EndDate");}));
that.iterations = iterations;
console.log('End date ',that.end);
// that.getStorySnapshotsForFeatures( features, iterations);
}
}
});
configs.push({
model: 'HierarchicalRequirement',
limit: 'Infinity',
fetch: ['Name','Iteration','ObjectID','Feature'],
filters: [{
property: 'Iteration.Name',
operator: '=',
value: iterationComboValue
}],
listeners: {
load: function(store, stories){
setOfStories = stories;
console.log('Iteration combo value is ', iterationComboValue);
console.log("# stories ",stories.length,stories);
}
}
});
async.map(configs, this.wsapiQuery, function(err,results){
setOfFeatures = results[0];
console.log("# features",setOfFeatures.length,setOfFeatures);
that.StartDate = that.startDate(iterations);
that.start = _.min(_.pluck(iterations,function(r) { return r.get("StartDate");}));
isoStart = new lumenize.Time(that.start).getISOStringInTZ("America/Chicago");
that.end = _.max(_.pluck(iterations,function(r) { return r.get("EndDate");}));
that.iterations = iterations;
//Here is the problem
setOfStories = results[1];
var stories = _.map(setOfStories, function(story){ return {name: story.get("Name"),fid: story.get("Feature").ObjectID,objectid: story.get("ObjectID")};}); //throws error
console.log('stories ',setOfStories);
var features = _.map(setOfFeatures, function(feature){return {name: feature.get("Name"), fid: feature.get("ObjectID")};});
console.log('features ',setOfFeatures);
var candidateStories = [];
_.each(stories, function(story){_.each(features, function(feature){
if(story.fid == feature){
candidateStories.push(story);
}
});});
console.log('candidate stories ',candidateStories.length,candidateStories);
if(candidateStories!=null){
that.getStorySnapShotsForFeatures(candidateStories);
}
//create snapshot store based on candidateStories.
});
},
getStorySnapShotsForFeatures: function(stories){
var snapshots = [];
var that = this;
async.map(stories, this.readStorySnapshots,function(err,results){
console.log('results ',results);
});
},
readStorySnapshots: function(parent,callback){
console.log('inside story snapshots ');
Ext.create('Rally.data.lookback.SnapshotStore',{
limit: 'Infinity',
autoLoad: true,
listeners:{
scope: this,
load: function(store,data,success){
callback(null,data);
}
},
fetch: ['ObjectID'],
filters:[{
property: 'ObjectID',
operator: 'in',
value: ['ObjectID']
},
{
property: '__At',
operator: '=',
value: 'current'
}]
});
},
startDate: function(iterations){
var start = _.min(_.pluck(iterations, function(r){return r.get("StartDate");}));
return Rally.util.DateTime.toIsoString(start, false);
}
});
In the async.map callback function, when setOfStories are returned, I try to map the name, fid, and objectID to a new array. But for some reason, the fid: story.get("Feature").ObjectID gives an error saying get("") is null. But just before returning the array, when I console log story.get("Feature").ObjectID the correct value is printed, but somehow when I try to return the same value, it generates an error.

The field on HierarchicalRequirement for its PI parent is called PortfolioItem (since the PI types are customizable- feature just happens to be the default name of the lowest level one).
story.get('PortfolioItem').ObjectID

Related

extjs gridpanel reconfigure issue

I have the below to reconfigure gridpanel, it works fine the first time, but the second time it throws the following error:
JavaScript runtime error: unable to get property 'id' of undefined or null reference"
What is wrong with my code?
$.ajax({
url: "/Home/Create",
data: JSON.stringify({ inputdata: selectedValues }),
async: false,
type: "POST",
contentType: 'application/json',
success: function (result) {
var columns = [];
var _fields = [];
var data = {
Key: [],
Value: []
};
var nameCount = 0;
var resultSet = result.result;
resultSet.forEach(function (element) {
for (var key in element) {
if (element.hasOwnProperty(key)) {
var keyStr = key;
data.Key.push(keyStr);
data.Value.push(element[key]);
}
}
}
});
var column;
var count = 0;
resultSet.forEach(function (element, index, ar) {
for (var key in element) {
if (!element.hasOwnProperty(key)) continue;
if (element.hasOwnProperty(key)) {
var field = {};
var elementValue = element[key];
var typeCheck = typeof elementValue;
field['name'] = key;
if (key.startsWith('Completed')) {
field['type'] = 'date';
field['submitFormat'] = 'm/d/Y';
field['submitValue'] = true;
}
else {
switch (typeCheck) {
case 'number':
field['type'] = 'int';
break;
case 'boolean':
field['type'] = 'string';
break;
case 'string':
field['type'] = 'string';
break;
case 'object':
field['type'] = 'object';
break;
case 'date':
field['type'] = 'date';
break;
default:
}
}
_fields.push(field);
if (key == 'EmployeeId' || key == 'EmployeeGroup'
|| key == 'GroupMemberId') {
column = Ext.create('Ext.grid.column.Column', {
text: key,
dataIndex: key,
hidden: true
});
columns.push(column);
}
else {
if (!key.startsWith('EmployeeName') && !key.startsWith('EmployeeStatus') && !key.startsWith('Completed')) {
column = Ext.create('Ext.grid.column.Column', {
text: key,
dataIndex: key,
width: 80
});
columns.push(column);
}
}
}
}
});
var keyValue = data.Key;
var values = data.Value;
var nameKeyValue = [];
var nameFinalValue;
for (var i = 0; i < keyValue.length; i++) {
for (var j = 0; j < values.length; j++) {
if (keyValue[i].startsWith('Name')) {
nameKeyValue.push(values[j]);
}
}
}
for (var i = 0; i < keyValue.length; i++) {
nameFinalValue = nameKeyValue[i];
if (keyValue[i].startsWith('Name')) {
var status = keyValue[i + 1];
var completed = keyValue[i + 2];
column = Ext.create('Ext.grid.column.Column', {
text: nameFinalValue,
dataIndex: nameFinalValue,
columns: [
Ext.create('Ext.grid.column.Column', {
text: 'Employee Status',
dataIndex: status,
width: 80,
stopSelection: false,
editable: true,
editor: {
xtype: 'checkbox'
},
field: { xtype: 'checkbox' }
}),
Ext.create('Ext.grid.column.Column', {
text: 'Completed',
dataIndex: completedOn,
width: 80,
editor: new Ext.form.DateField({
xtype: 'datefield',
format: 'm/d/y',
renderer: Ext.util.Format.dateRenderer('m/d/Y')
})
})
]
});
columns.push(column);
}
}
// *** new code
var store = Ext.create("Ext.data.Store", {
id: 'myStore',
fields: _fields,
groupField: 'Group',
proxy: {
type: 'ajax',
reader: {
type: 'json',
root: 'data'
}
}
});
var grid = App.myGrid;
grid.reconfigure(Ext.getStore('myStore'), columns);//this line throws error
grid.getStore().loadData(result.result);
grid.getView().refresh();
selectedValues = [];
}
}
});
replace error-throwing line with
grid.reconfigure(store, columns);
Try clearing your store before loading new data
grid.getStore().removeAll();

Exporting Rally grid information to CSV

I am trying to export rally grid data to csv. Here's my code:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items:[{ xtype: 'container', itemId: 'print_button_box', padding: 5},{xtype: 'container', itemId: 'grid_box'}],
count: 0,
globalStore: null,
launch: function() {
this.globalStore = null;
this.count = 0;
this._addPrintButton();
list = [];
//Write app code here
this._get_stories_of_feature();
},
_addPrintButton: function() {
var me = this;
this.down('#print_button_box').add( {
xtype: 'rallybutton',
itemId: 'print_button',
text: 'CSV',
disabled: false,
handler: function() {
console.log('globalStore ',me.globalStore);
me._onClickExport();
}
});
},
_onClickExport: function () { //using this function to export to csv
if (document.getElementById('grid_box')) {
//Ext.getBody().mask('Exporting Tasks...');
console.log('inside export');
setTimeout(function () {
var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-' +
'microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>' +
'<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>' +
'{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
'</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}' +
'</table></body></html>';
var base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)));
};
var format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
});
};
var table = document.getElementById('grid_box');
console.log("Exporting table ",table);
var excel_data = '<tr>';
Ext.Array.each(table.innerHTML.match(/<span .*?x-column-header-text.*?>.*?<\/span>/gm), function (column_header_span) {
excel_data += (column_header_span.replace(/span/g, 'td'));
});
excel_data += '</tr>';
Ext.Array.each(table.innerHTML.match(/<tr class="x-grid-row.*?<\/tr>/gm), function (line) {
excel_data += line.replace(/[^\011\012\015\040-\177]/g, '>>');
});
console.log("Excel data ",excel_data);
var ctx = {worksheet: name || 'Worksheet', table: excel_data};
window.location.href = 'data:application/vnd.ms-excel;base64,' + base64(format(template, ctx));
Ext.getBody().unmask();
}, 500);
}else{
console.log("grid_box does not exist");
}
},
_createStore: function(){
var me = this;
//var f = [{property: 'UserStories', operator: '!=', value: null}];
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "PortfolioItem/Feature",
limit: 5000,
fetch: ['FormattedID','Name','UserStories','c_DIteration','c_DPSI'],
listeners:{
load: function(store,data,success){
console.log("Store ",store);
var data_length = data.length;
console.log('Data length is '+data.length);
Ext.Array.each(data,function(item){
if(item.get('UserStories')==null){
store.remove(item);
}else{
var data = {
id: item.get("FormattedID"),
name: item.get("Name"),
UserStories: item.get("UserStories")._type,
DIteration: item.get("c_DIteration"),
DPSI: item.get("c_DPSI")
};
//list.push(data);
}
me._get_stories_of_feature(item.get("ObjectID"),item.get("Name"),data_length);
});
},
scope: this
}
});
},
_showGrid: function(store){
var me = this;
if(!this.grid){
this.grid = Ext.create('Rally.ui.grid.Grid',{
store: store,
columnCfgs:[
{text: 'Feature_ID', dataIndex: 'ide'},
{text: 'Feature Name', dataIndex: 'name'},
{text: 'Feature DIteration', dataInfex: 'fDIteration'},
{text: 'Story Name', dataIndex: 'UserStories'},
{text: 'Story ID', dataIndex: 'FormattedID'},
{text: 'Story DIteration', dataIndex: 'DIteration'},
{text: 'Story DPSI', dataIndex: 'DPSI'},
{text: 'Unscheduled', dataIndex: 'Unscheduled'}
]
});
me.globalStore = this.grid;
this.down('#grid_box').add(this.grid);
}
},
_get_stories_of_feature: function(){
var me = this;
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "hierarchicalrequirement",
limit: 5000,
fetch: ['Name','ObjectID','FormattedID','Feature','c_DIteration','c_DPSI','DragAndDropRank'],
sorters:[{
property: 'DragAndDropRank', direction: 'ASC'
}],
filters:{
property: 'Feature', operator: '!=', value:null
},
listeners:{
load: function(store,data,success){
var data_length = data.length;
if(data_length>0){
console.log("Total stories ",data_length);
for(var i=0;i<data_length;i++){
var flag;
if(data[i].data.Feature.c_DIteration==null || data[i].data.Feature.c_DIteration.toString().indexOf("*")!=-1)
flag = "YES";
else flag = "NO";
var element = {
ide: data[i].data.Feature.FormattedID,
name: data[i].data.Feature.Name,
fDIteration: data[i].data.Feature.c_DIteration,
UserStories: data[i].data.Name,
FormattedID: data[i].data.FormattedID,
DIteration: data[i].data.c_DIteration,
DPSI: data[i].data.c_DPSI,
Unscheduled: flag
};
list.push(element);
console.log("me count is ",me.count);
console.log("Found ",data[i].data);
me.count++;
}
}
if(me.count==data_length){
console.log("Building store");
//once all the stories and feature data is computed
var myStore = Ext.create("Rally.data.custom.Store",{
data: list,
pageSize: 100
});
me._showGrid(myStore);
}
}
}
});
},
exportGrid: function(grid) {
if (Ext.isIE) {
this._ieToExcel(grid);
} else {
var data = this._getCSV(grid);
var a = document.createElement('a');
a.href = 'data:attachment/csv,' + data;
a.target ='_blank';
a.download = 'myFile.csv,' + encodeURIComponent(data); ;
a.innerHTML = "Click me to download the file.";
window.location = a;
}
},
_escapeForCSV: function(string) {
if (string.match(/,/)) {
if (!string.match(/"/)) {
string = '"' + string + '"';
} else {
string = string.replace(/,/g, ''); // comma's and quotes-- sorry, just loose the commas
}
}
return string;
},
_getFieldText: function(fieldData) {
var text;
if (fieldData == null || fieldData == undefined) {
text = '';
} else if (fieldData._refObjectName && !fieldData.getMonth) {
text = fieldData._refObjectName;
} else if (fieldData instanceof Date) {
text = Ext.Date.format(fieldData, this.dateFormat);
} else if (!fieldData.match) { // not a string or object we recognize...bank it out
text = '';
} else {
text = fieldData;
}
return text;
},
_getFieldTextAndEscape: function(fieldData) {
var string = this._getFieldText(fieldData);
return this._escapeForCSV(string);
},
_getCSV: function (grid) {
var cols = grid.columns;
var store = grid.store;
var data = '';
var that = this;
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
data += that._getFieldTextAndEscape(col.text) + ',';
}
});
data += "\n";
store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
data += that._getFieldTextAndEscape(text) + ',';
}
});
data += "\n";
});
return data;
},
_ieGetGridData : function(grid, sheet) {
var that = this;
var resourceItems = grid.store.data.items;
var cols = grid.columns;
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
console.log('header: ', col.text);
sheet.cells(1,colIndex + 1).value = col.text;
}
});
var rowIndex = 2;
grid.store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
var value = that._getFieldText(text);
sheet.cells(rowIndex, colIndex+1).value = value;
}
});
rowIndex++;
});
},
_ieToExcel: function (grid) {
if (window.ActiveXObject){
var xlApp, xlBook;
try {
xlApp = new ActiveXObject("Excel.Application");
xlBook = xlApp.Workbooks.Add();
} catch (e) {
Ext.Msg.alert('Error', 'For the export to work in IE, you have to enable a security setting called "Initialize and script ActiveX control not marked as safe" from Internet Options -> Security -> Custom level..."');
return;
}
xlBook.worksheets("Sheet1").activate;
var XlSheet = xlBook.activeSheet;
xlApp.visible = true;
this._ieGetGridData(grid, XlSheet);
XlSheet.columns.autofit;
}
}
});
I am using the "_onClickExport" function that I read about here to export my grid data to CSV but when I execute that function, it can't find ElementId "grid_box", although I have defined it.
I was able to export with your code after making these changes:
line 31, change to
if (this.down('#grid_box')){
line 52, change to
var table = that.getComponent('grid_box');
where that is defined on the top of _onClickExport
var that = this;
line 57, and a similar line below it, replace table.innerHTML.match with table.getEl().dom.outerHTML.match
Ext.Array.each(table.getEl().dom.outerHTML.match

Loading grid only after "all" the data stores have loaded

I have this code that loads a data store for all stories that contain each of the names from an array.
var prefix_set = [list of names]
for(var i=0;i<prefix_set.length;i++)
_get_stories_of_feature(prefix_set[i]) //this function creates a data store
I am storing all the data into a global array and creating a data store which is passed to a grid in the end. The problem is that my grid loads up with incomplete data (the grid loads up even before all the data has been loaded into the global array).
Here's my code:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items:[{ xtype: 'container', itemId: 'print_button_box', padding: 5},{xtype: 'container', itemId: 'grid_box'}],
count: 0,
globalStore: null,
totalDataLength: 0,
gridCounter: 0,
launch: function() {
var me = this;
this.globalStore = null;
this.gridCounter = 0;
this.totalDataLength=0;
this.count = 0;
this._addPrintButton();
list = [];
//Write app code here
var prefix_set = ["Epic:","Arch:","Refa:","Innov:","Spike:","Producer:","Dependency:","Consumer:"];
var i;
for(i=0;i<prefix_set.length;i++){
this._get_stories_of_feature(prefix_set[i],prefix_set.length,i);
}
},
_addPrintButton: function() {
var me = this;
this.down('#print_button_box').add( {
xtype: 'rallybutton',
itemId: 'print_button',
text: 'CSV',
disabled: false,
handler: function() {
console.log('globalStore ',me.globalStore);
me.exportGrid(me.globalStore);
}
});
},
_onClickExport: function () {
if (document.getElementById('grid_box')) {
//Ext.getBody().mask('Exporting Tasks...');
console.log('inside export');
setTimeout(function () {
var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-' +
'microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>' +
'<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>' +
'{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
'</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}' +
'</table></body></html>';
var base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)));
};
var format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
});
};
var table = document.getElementById('grid_box');
console.log("Exporting table ",table);
var excel_data = '<tr>';
Ext.Array.each(table.innerHTML.match(/<span .*?x-column-header-text.*?>.*?<\/span>/gm), function (column_header_span) {
excel_data += (column_header_span.replace(/span/g, 'td'));
});
excel_data += '</tr>';
Ext.Array.each(table.innerHTML.match(/<tr class="x-grid-row.*?<\/tr>/gm), function (line) {
excel_data += line.replace(/[^\011\012\015\040-\177]/g, '>>');
});
console.log("Excel data ",excel_data);
var ctx = {worksheet: name || 'Worksheet', table: excel_data};
window.location.href = 'data:application/vnd.ms-excel;base64,' + base64(format(template, ctx));
Ext.getBody().unmask();
}, 500);
}else{
console.log("grid_box does not exist");
}
},
tableToExcel: function(){
var me = this;
console.log("Global store ",me.globalStore);
var uri = 'data:application/vnd.ms-excel;base64,'
, template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>'
, base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))); }
, format = function(s, c) { return s.replace(/{(\w+)}/g, function(m, p) { return c[p]; }); };
return function(table, name) {
if (!table.nodeType) table = document.getElementById(table);
var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML};
window.location.href = uri + base64(format(template, ctx));
};
},
_createStore: function(){
var me = this;
//var f = [{property: 'UserStories', operator: '!=', value: null}];
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "PortfolioItem/Feature",
limit: 10000,
fetch: ['FormattedID','Name','UserStories','c_DIteration','c_DPSI'],
listeners:{
load: function(store,data,success){
console.log("Store ",store);
var data_length = data.length;
console.log('Data length is '+data.length);
Ext.Array.each(data,function(item){
if(item.get('UserStories')==null){
store.remove(item);
}else{
var data = {
id: item.get("FormattedID"),
name: item.get("Name"),
UserStories: item.get("UserStories")._type,
DIteration: item.get("c_DIteration"),
DPSI: item.get("c_DPSI")
};
//list.push(data);
}
me._get_stories_of_feature(item.get("ObjectID"),item.get("Name"),data_length);
});
},
scope: this
}
});
},
_showGrid: function(store){
var me = this;
if(!this.grid){
this.grid = Ext.create('Rally.ui.grid.Grid',{
store: store,
columnCfgs:[
{text: 'Feature', dataIndex: 'name'},
{text: 'FormattedID', dataIndex: 'FormattedID'},
{text: 'Name', dataIndex: 'UserStories'},
{text: 'Release', dataIndex: 'Release'},
{text: 'Iteration', dataIndex: 'Iteration'},
{text: 'DIteration', dataIndex: 'DIteration'},
{text: 'DPSI', dataIndex: 'DPSI'},
{text: 'Schedule State', dataIndex: 'ScheduleState'},
{text: 'Task Remaining Total', dataIndex: 'TaskRemainingTotal'},
{text: 'Owner', dataIndex: 'Owner'},,
{text: 'Project', dataIndex: 'Project'},
{text: 'Unscheduled', dataIndex: 'Unscheduled'}
]
});
me.globalStore = this.grid;
this.down('#grid_box').add(this.grid);
}
},
_get_stories_of_feature: function(name,length,k){
var me = this;
Ext.create('Rally.data.WsapiDataStore',{
autoLoad: true,
model: "HierarchicalRequirement",
limit: 10000,
fetch: ['Name','ObjectID','FormattedID','Parent','Feature','TaskRemainingTotal','c_DIteration','c_DPSI','DragAndDropRank','Release','Iteration','Project','Owner','ScheduleState'],
sorters:[{
property: 'DragAndDropRank', direction: 'ASC'
,},{property: 'Project', direction: 'ASC'}],
filters:[{
property: 'Name', operator: 'contains', value: name
},{
property: 'ScheduleState', operator: '!=', value: 'Accepted'
}],
pageSize: 5000,
listeners:{
load: function(store,data,success){
var data_length = data.length;
if(data_length>0){
console.log("Total stories ",data_length);
for(var i=0;i<data_length;i++){
console.log("Data length for ",k," is ",data_length);
console.log("Data i ",data[i]);
me.totalDataLength++;
var flag;
var iteration=""; var release="";
var fid = "",fname="";
var owner="";
if(data[i].data.Feature!=null && (data[i].data.Feature.c_DIteration==null || data[i].data.Feature.c_DIteration.toString().indexOf("*")!=-1))
flag = "YES";
else flag = "NO";
if(data[i].data.Iteration!=null){
iteration = data[i].data.Iteration._refObjectName;
}
if(data[i].data.Release!=null){
release = data[i].data.Release._refObjectName;
}
if(data[i].data.Owner!=null){
owner = data[i].data.Owner._refObjectName;
}
if(data[i].data.Feature!=null){
fid = data[i].data.Feature.FormattedID;
fname = data[i].data.Feature.Name;
}
var element = {
name: fid+":"+fname,
UserStories: data[i].data.Name,
Project: data[i].data.Project._refObjectName,
Owner: owner,
FormattedID: data[i].data.FormattedID,
Iteration: iteration,
TaskRemainingTotal: parseInt(data[i].data.TaskRemainingTotal),
Release: release,
ScheduleState: data[i].data.ScheduleState,
DIteration: data[i].data.c_DIteration,
DPSI: data[i].data.c_DPSI,
Unscheduled: flag
};
list.push(element);
console.log("me count is ",me.count);
console.log("Found ",data[i].data);
me.count++;
}
}
console.log("Total Data Length is ",me.totalDataLength);
console.log("k is ",k," and length is ",length);
if(k==length-1){
console.log("Building store");
//once all the stories and feature data is computed
var myStore = Ext.create("Rally.data.custom.Store",{
data: list,
sortable: true,
pageSize: me.totalDataLength,
});
me._showGrid(myStore);
}
}
}
});
me.gridCounter++;
},
exportGrid: function(grid) {
if (Ext.isIE) {
this._ieToExcel(grid);
} else {
var data = this._getCSV(grid);
window.location = 'data:text/csv;charset=utf8,' + encodeURIComponent(data);
}
},
_escapeForCSV: function(string) {
if (string.match(/,/)) {
if (!string.match(/"/)) {
string = '"' + string + '"';
} else {
string = string.replace(/,/g, ''); // comma's and quotes-- sorry, just loose the commas
}
}
return string;
},
_getFieldText: function(fieldData) {
var text;
if (fieldData == null || fieldData == undefined) {
text = '';
} else if (fieldData._refObjectName && !fieldData.getMonth) {
text = fieldData._refObjectName;
} else if (fieldData instanceof Date) {
text = Ext.Date.format(fieldData, this.dateFormat);
} else if (!fieldData.match) { // not a string or object we recognize...bank it out
text = '';
} else {
text = fieldData;
}
return text;
},
_getFieldTextAndEscape: function(fieldData) {
var string = this._getFieldText(fieldData);
return this._escapeForCSV(string);
},
_getCSV: function (grid) {
var cols = grid.columns;
var store = grid.store;
var data = '';
console.log("Grid.Store is ",grid.store);
var that = this;
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
data += that._getFieldTextAndEscape(col.text) + ',';
}
});
data += "\n";
store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, index) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
data += that._getFieldTextAndEscape(text) + ',';
}
});
data += "\n";
});
return data;
},
_ieGetGridData : function(grid, sheet) {
var that = this;
var resourceItems = grid.store.data.items;
var cols = grid.columns;
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
console.log('header: ', col.text);
sheet.cells(1,colIndex + 1).value = col.text;
}
});
var rowIndex = 2;
grid.store.each(function(record) {
var entry = record.getData();
Ext.Array.each(cols, function(col, colIndex) {
if (col.hidden != true) {
var fieldName = col.dataIndex;
var text = entry[fieldName];
var value = that._getFieldText(text);
sheet.cells(rowIndex, colIndex+1).value = value;
}
});
rowIndex++;
});
},
_ieToExcel: function (grid) {
if (window.ActiveXObject){
var xlApp, xlBook;
try {
xlApp = new ActiveXObject("Excel.Application");
xlBook = xlApp.Workbooks.Add();
} catch (e) {
Ext.Msg.alert('Error', 'For the export to work in IE, you have to enable a security setting called "Initialize and script ActiveX control not marked as safe" from Internet Options -> Security -> Custom level..."');
return;
}
xlBook.worksheets("Sheet1").activate;
var XlSheet = xlBook.activeSheet;
xlApp.visible = true;
this._ieGetGridData(grid, XlSheet);
XlSheet.columns.autofit;
}
}
});
Please take a look at the code in this post, where Mark uses promises, and builds three separate Rally.data.wsapi.Stores and only after all the data is there _makeGrid(resultArray) is called.

Using buffered store + infinite grid with dynamic data

The goal is to use buffered store for the dynamic data set.
The workflow is below:
Some data is already present on server.
Clients uses buffered store & infinite grid to handle the data.
When the application runs the store is loading
and 'load' event scrolls the grid to the last message.
Some records are added to server.
Client gets a push notification and runs store reload.
topic.store.load({addRecords: true});
The load event runs and tries to scroll to the last message again but failes:
TypeError: offsetsTo is null
e = Ext.fly(offsetsTo.el || offsetsTo, '_internal').getXY();
Seems that the grid view doesn't refreshes and doesn't show the added records, only the white spaces on their places.
Any ideas how can I make the grid view refresh correctly?
The store initialization:
Ext.define('orm.data.Store', {
extend: 'Ext.data.Store',
requires: ['orm.data.writer.Writer'],
constructor: function (config) {
Ext.apply(this, config);
this.proxy = Ext.merge(this.proxy, {
type: 'rest',
batchActions: true,
reader: {
type: 'json',
root: 'rows'
},
writer: {
type: 'orm'
}
});
this.callParent(arguments);
}
});
Ext.define('akma.chat.model.ChatMessage', {
extend:'Ext.data.Model',
fields:[
{ name:'id', type:'int', defaultValue : undefined },
{ name:'createDate', type:'date', dateFormat:'Y-m-d\\TH:i:s', defaultValue : undefined },
{ name:'creator', type:'User', isManyToOne : true, defaultValue : undefined },
{ name:'message', type:'string', defaultValue : undefined },
{ name:'nameFrom', type:'string', defaultValue : undefined },
{ name:'topic', type:'Topic', isManyToOne : true, defaultValue : undefined }
],
idProperty: 'id'
});
Ext.define('akma.chat.store.ChatMessages', {
extend: 'orm.data.Store',
requires: ['orm.data.Store'],
alias: 'store.akma.chat.store.ChatMessages',
storeId: 'ChatMessages',
model: 'akma.chat.model.ChatMessage',
proxy: {
url: 'http://localhost:8080/chat/services/entities/chatmessage'
}
});
var store = Ext.create('akma.chat.store.ChatMessages', {
buffered: true,
pageSize: 10,
trailingBufferZone: 5,
leadingBufferZone: 5,
purgePageCount: 0,
scrollToLoadBuffer: 10,
autoLoad: false,
sorters: [
{
property: 'id',
direction: 'ASC'
}
]
});
Grid initialization:
Ext.define('akma.chat.view.TopicGrid', {
alias: 'widget.akma.chat.view.TopicGrid',
extend: 'akma.chat.view.grid.DefaultChatMessageGrid',
requires: ['akma.chat.Chat', 'akma.UIUtils', 'Ext.grid.plugin.BufferedRenderer'],
features: [],
hasPagingBar: false,
height: 500,
loadedMsg: 0,
currentPage: 0,
oldId: undefined,
forceFit: true,
itemId: 'topicGrid',
selModel: {
pruneRemoved: false
},
multiSelect: true,
viewConfig: {
trackOver: false
},
plugins: [{
ptype: 'bufferedrenderer',
pluginId: 'bufferedrenderer',
variableRowHeight: true,
trailingBufferZone: 5,
leadingBufferZone: 5,
scrollToLoadBuffer: 10
}],
tbar: [{
text: 'unmask',
handler: function(){
this.up('#topicGrid').getView().loadMask.hide();
}
}],
constructor: function (config) {
this.topicId = config.topicId;
this.store = akma.chat.Chat.getMessageStoreInstance(this.topicId);
this.topic = akma.chat.Chat.getTopic(this.topicId);
var topicPanel = this;
this.store.on('load', function (store, records) {
var loadedMsg = store.getTotalCount();
var pageSize = store.pageSize;
store.currentPage = Math.ceil(loadedMsg/pageSize);
if (records && records.length > 0) {
var newId = records[0].data.id;
if (topicPanel.oldId) {
var element;
for (var i = topicPanel.oldId; i < newId; i++) {
element = Ext.get(i + '');
topicPanel.blinkMessage(element);
}
}
topicPanel.oldId = records[records.length-1].data.id;
var view = topicPanel.getView();
view.refresh();
topicPanel.getPlugin('bufferedrenderer').scrollTo(store.getTotalCount()-1);
}
});
this.callParent(arguments);
this.on('afterrender', function (grid) {
grid.getStore().load();
});
var me = this;
akma.UIUtils.onPasteArray.push(function (e, it) {
if(e.clipboardData){
var items = e.clipboardData.items;
for (var i = 0; i < items.length; ++i) {
if (items[i].kind == 'file' && items[i].type.indexOf('image/') !== -1) {
var blob = items[i].getAsFile();
akma.chat.Chat.upload(blob, function (event) {
var response = Ext.JSON.decode(event.target.responseText);
var fileId = response.rows[0].id;
me.sendMessage('<img src="/chat/services/file?id=' + fileId + '" />');
})
}
}
}
});
akma.UIUtils.addOnPasteListener();
},
sendMessage: function(message){
if(message){
var topicGrid = this;
Ext.Ajax.request({
method: 'POST',
url: topicGrid.store.proxy.url,
params:{
rows: Ext.encode([{"message":message,"topic":{"id":topicGrid.topicId}}])
}
});
}
},
blinkMessage: function (messageElement) {
if (messageElement) {
var blinking = setInterval(function () {
messageElement.removeCls('red');
messageElement.addCls('yellow');
setTimeout(function () {
messageElement.addCls('red');
messageElement.removeCls('yellow');
}, 250)
}, 500);
setTimeout(function () {
clearInterval(blinking);
messageElement.addCls('red');
messageElement.removeCls('yellow');
}, this.showInterval ? this.showInterval : 3000)
}
},
columns: [ {
dataIndex: 'message',
text: 'Message',
renderer: function (value, p, record) {
var firstSpan = "<span id='" + record.data.id + "'>";
var creator = record.data.creator;
return Ext.String.format('<div style="white-space:normal !important;">{3}{1} : {0}{4}</div>',
value,
creator ? '<span style="color: #' + creator.chatColor + ';">' + creator.username + '</span>' : 'N/A',
record.data.id,
firstSpan,
'</span>'
);
}
}
]
});
upd: Seems that the problem is not in View. The bufferedrenderer plugin ties to scroll to the record.
It runs a callback function:
callback: function(range, start, end) {
me.renderRange(start, end, true);
targetRec = store.data.getRange(recordIdx, recordIdx)[0];
.....
store.data.getRange(recordIdx, recordIdx)[0]
tries to get the last record in the store.
....
Ext.Array.push(result, Ext.Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd));
getPage returns all records of the given page, but the last record is missing i.e. the store was not updated perfectly.
Any ideas how to fix?
The problem is that store.load() doesn't fill up store PageMap with the new data. The simplest fix is using store.reload() instead.
Maybe you are to early when listening to the load event. I am doing roughly the same in my application (not scrolling to the end, but to some arbitrary record after load). I do the view-refresh and bufferedrender-scrollTo in the callback of the store.load().
Given your code this would look like:
this.on('afterrender', function (grid) {
var store = grid.getStore();
store.load({
callback: function {
// snip
var view = topicPanel.getView();
view.refresh();
topicPanel.getPlugin('bufferedrenderer').scrollTo(store.getTotalCount()-1);
}
});
});

Getting geolocation variables to place in proxy

I am trying to add in the current position of the device into the proxy call. I have already added in variables with '+radius+' and '+values+'. I have used Ext.util.Geolocation and can see the geolocation in the console. How do i get a lat and long variable out of this so i can add to the proxy like '+lat+' and '+lon+'
Ext.define('FirstApp.controller.History', {
extend:'Ext.app.Controller',
config:{
refs:{
main:'main',
placesContainer:'placesContainer',
placesList:'placesContainer places list',
info:'info',
review:'review',
rss:'rss',
favourites:'favourites',
mapcontainer:'mapcontainer',
visit: '#visit',
radius: '#radius'
},
control:{
'placesContainer places list':{
itemtap:'recordDetailsNavigation'
},
'main':{
activeitemchange:'recordTabChanged'
},
'#home': {
// On the tap event, call onNewTap
tap: 'onHomeTap'
},
'#info': {
// On the tap event, call onNewTap
tap: 'onInfoTap'
}
,
'#search': {
// On the tap event, call onNewTap
tap: 'onSearchTap'
}
,
'#leaveReview': {
// On the tap event, call onNewTap
tap: 'onReviewTap'
}
},
routes: {
'placesContainer': 'gotoPlacesContainer',
'placesContainer/:id': 'gotoPlacesContainerDetails',
'info': 'gotoInfo',
'review': 'gotoReview',
'rss': 'gotoRss',
'favourites': 'gotoFavourites',
'mapcontainer': 'gotoMapContainer'
}
},
recordDetailsNavigation:function (list, index, target, record) {
console.log("recordDetailsNavigation");
this.getApplication().getHistory().add(Ext.create('Ext.app.Action', {
url:'placesContainer/' + record.get('id')
}),true);
},
recordTabChanged:function(tab,value, oldValue){
console.log("recordTabChanged "+value.xtype);
this.getApplication().getHistory().add(Ext.create('Ext.app.Action', {
url:value.xtype
}),true);
},
gotoPlacesContainer:function(){
console.log('Goto PlacesContainer');
this.getMain().setActiveItem(0);
var items = this.getPlacesContainer().getItems();
console.log("PlacesContainer has "+items.length+" children")
if(items.length>1){
this.getPlacesContainer().pop();
}
},
gotoReview:function(){
console.log('Goto Review');
this.getMain().setActiveItem(1);
Reviews.load();
},
gotoMapContainer:function(){
console.log('Goto MapContainer');
this.getMain().setActiveItem(2);
},
gotoPlacesContainerDetails:function(id){
console.log('Goto PlacesContainer Details for id '+id);
this.gotoPlacesContainer();
this.getPlacesList().refresh();
this.getPlacesList().on('refresh',function(){
var store = Ext.getStore('Places');
var index = store.find('id',id);
var record = store.getAt(index)
this.getPlacesContainer().push({
xtype:'details',
title:record.data.name,
data:record.data
})
this.getApplication().getHistory().add(Ext.create('Ext.app.Action', {
url:'placesContainer/' + record.get('id')
}),true);
},this);
},
gotoInfo:function(){
console.log('Goto Info');
this.getMain().setActiveItem(3);
},
gotoRss:function(){
console.log('Goto Rss');
this.getMain().setActiveItem(4);
},
gotoFavourites:function(){
console.log('Goto Favourites');
this.getMain().setActiveItem(5);
},
onHomeTap: function() {
Ext.Viewport.setActiveItem(Ext.create('FirstApp.view.Home'));
},
onReviewTap: function() {
Ext.Viewport.setActiveItem(Ext.create('FirstApp.view.CreateReview'));
},
onInfoTap: function() {
Ext.Viewport.setActiveItem(Ext.create('FirstApp.view.Info'));
},
onSearchTap: function() {
Ext.Viewport.setActiveItem(Ext.create('FirstApp.view.Main'));
var values = this.getVisit().getValue();
var radius = this.getRadius().getValue() * 1000;
console.log(values);
console.log(radius);
var proxy = {
type:'ajax',
id:'myPlaceProxy',
url:'https://maps.googleapis.com/maps/api/place/search/json? location=52.247983,-7.141113&radius='+radius+'&types='+values+'&name=&sensor=false&key=',
reader:{
type:'json',
rootProperty:'results'
}
}
var geo = new Ext.util.Geolocation({
autoUpdate: false,
allowHighAccuracy: true,
listeners: {
locationupdate: function(geo) {
lat = geo.getLatitude();
lon = geo.getLongitude();
var proxy = Ext.getCmp('myPlaceProxy');
proxy.setUrl('https://maps.googleapis.com/maps/api/place/search/json?location='+lat+','+lon+'&radius='+radius+'&types='+values+'&name=&sensor=false&key=AIzaSyAAStZvDJ_5ENODEdSCanLWgJBG6p6eXBQ');
console.log(proxy);
Ext.StoreMgr.get('Places').setProxy(proxy);
Ext.StoreMgr.get('Places').load();
}
}
});
geo.updateLocation();
}
});
Ext.define('FirstApp.store.Places',{
extend:'Ext.data.Store',
config:{
autoLoad:false,
model:'FirstApp.model.Place',
proxy:{
}
}
})
First of all give id to your proxy so that we can access it via id anytime
var proxy = {
type:'ajax',
id:'myPlaceProxy',
url:'https://maps.googleapis.com/maps/api/place/search/json?location=52.247983,-7.141113&radius='+radius+'&types='+values+'&name=&sensor=false&key=',
reader:{
type:'json',
rootProperty:'results'
}
}
then in you geo location's locationupdate event access the proxy
var geo = new Ext.util.Geolocation({
autoUpdate: true,
allowHighAccuracy: true,
listeners: {
locationupdate: function(geo) {
lat = geo.getLatitude();
lon = geo.getLongitude();
var proxy = Ext.getCmp('myPlaceProxy');
proxy.setUrl('https://maps.googleapis.com/maps/api/place/search/json?location='+lat+','+lon+'&radius='+radius+'&types='+values+'&name=&sensor=false&key='');
console.log(proxy);
Ext.StoreMgr.get('Places').setProxy(proxy);
Ext.StoreMgr.get('Places').load();
}
}
});
then fire locationupdate event of your geo when ever you want by calling this
geo.updateLocation();
EDIT:
but i strongly recomment that dont always create var geo
you just have to call this geo.updateLocation(); on tap once you create it globally, in that way you can manage it much better
Try this:
Ext.define('FirstApp.store.Places',{
extend:'Ext.data.Store',
id: 'myPlaces',
config:{
autoLoad:false,
model:'FirstApp.model.Place',
proxy:{
type:'ajax',
url:'https://maps.googleapis.com/maps/api/place/search/json? location=52.247983,-7.141113&radius='+radius+'&types='+values+'&name=&sensor=false&key=AIzaSyAAStZvDJ_5ENODEdSCanLWgJBG6p6eXBQ',
reader:{
type:'json',
rootProperty:'results'
}
}
});
after that you can set the url in the tap event as
onSearchTap: function() {
Ext.Viewport.setActiveItem(Ext.create('FirstApp.view.Main'));
var values = this.getVisit().getValue();
var radius = this.getRadius().getValue() * 1000;
console.log(values);
console.log(radius);
var geo = new Ext.util.Geolocation({
autoUpdate: false,
allowHighAccuracy: true,
listeners: {
locationupdate: function(geo) {
lat = geo.getLatitude();
lon = geo.getLongitude();
Ext.StoreMgr.get('Places').getProxy().setUrl('https://maps.googleapis.com/maps/api/place/search/json?location='+lat+','+lon+'&radius='+radius+'&types='+values+'&name=&sensor=false&key='');
Ext.StoreMgr.get('Places').load();
}
}
});
geo.updateLocation();
}

Resources