Rally Lookback call in custome HTML app - rally

Rally Lookback API:
I need to query using the Rally Lookback API in a custom HTML App inside Rally.
I dont know how to call the Lookback API, in the overall HTML.
Could anyone please send me a sample App HTML ?

Lookback API works with App SDK 2.
Rally App SDK 2.0 RC1 documentation on Lookback API and a link to Lookback API manual are available here:
This app builds a grid of defects fixed between certain dates. You may directly modify the default query after you deploy the app in Rally.
<!DOCTYPE html>
<html>
<head>
<title>Find fixed defects within certain dates</title>
<script type="text/javascript" src="/apps/2.0rc1/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
layout: {
type: 'vbox',
align: 'stretch'
},
items:[
{
xtype: 'panel',
layout: 'anchor',
border: true,
fieldDefaults: {
labelWidth: 40
},
defaultType: 'textfield',
bodyPadding: 5,
items: [
{
fieldLabel: 'Query',
itemId: 'queryField',
anchor:'100%',
width: 700,
height: 100,
xtype: 'textarea',
value: '{\n'+
' "State":"Fixed",\n'+
'"_PreviousValues.State":{$gte:"Submitted"},\n'+
'"_ValidFrom":{$gte:"2013-06-01TZ",$lt:"2013-07-01TZ"}\n'+
'}'
},
{
fieldLabel: 'Fields',
itemId: 'fieldsField',
anchor: '100%',
width: 700,
value: "ObjectID, _ValidFrom, Name, State, Resolution"
},
{
fieldLabel: 'Sort',
itemId: 'sortField',
anchor: '100%',
width: 700,
value: "{'ObjectID' : -1, '_ValidFrom': 1}"
},
{
fieldLabel: 'Page Size',
itemId: 'pageSizeField',
anchor: '100%',
width: 700,
value: '10'
},
{
fieldLabel: 'Hydrate',
itemId: 'hydrate',
anchor: '100%',
width: 700,
value: "State, Resolution"
},
],
buttons: [
{
xtype: 'rallybutton',
text: 'Search',
itemId: 'searchButton'
}
]
},
{
xtype: 'panel',
itemId: 'gridHolder',
layout: 'fit',
height: 400
}
],
launch: function() {
var button = this.down('#searchButton');
button.on('click', this.searchClicked, this);
},
searchClicked: function(){
var queryField = this.down('#queryField');
var query = queryField.getValue();
var selectedFields = this.down('#fieldsField').getValue();
if(selectedFields){
if(selectedFields === 'true'){
selectedFields = true;
}
else{
selectedFields = selectedFields.split(', ');
}
}
var sort = this.down('#sortField').getValue();
var pageSize = this.down('#pageSizeField').getValue();
var parsedPageSize = parseInt(pageSize, 10);
// don't allow empty or 0 pagesize
pageSize = (parsedPageSize) ? parsedPageSize : 10;
var callback = Ext.bind(this.processSnapshots, this);
this.doSearch(query, selectedFields, sort, pageSize, callback);
},
createSortMap: function(csvFields){
var fields = csvFields.split(', ');
var sortMap = {};
for(var field in fields){
if(fields.hasOwnProperty(field)){
sortMap[field] = 1;
}
}
return sortMap;
},
doSearch: function(query, fields, sort, pageSize, callback){
var transformStore = Ext.create('Rally.data.lookback.SnapshotStore', {
context: {
workspace: this.context.getWorkspace(),
project: this.context.getProject()
},
fetch: fields,
find: query,
autoLoad: true,
hydrate: ["State","Resolution"],
listeners: {
scope: this,
load: this.processSnapshots
}
});
},
processSnapshots: function(store, records){
var snapshotGrid = Ext.create('Rally.ui.grid.Grid', {
title: 'Snapshots',
store: store,
columnCfgs: [
{
text: 'ObjectID',
dataIndex: 'ObjectID'
},
{
text: 'Name',
dataIndex: 'Name'
},
{
text: 'Project',
dataIndex: 'Project'
},
{
text: '_ValidFrom',
dataIndex: '_ValidFrom'
},
{
text: '_ValidTo',
dataIndex: '_ValidTo'
},
{
text: 'State',
dataIndex: 'State'
},
{
text: 'Resolution',
dataIndex: 'Resolution'
},
],
height: 400
});
var gridHolder = this.down('#gridHolder');
gridHolder.removeAll(true);
gridHolder.add(snapshotGrid);
}
});
Rally.launchApp('CustomApp', {
name: 'lbapi'
});
});
</script>
<style type="text/css">
.app {
/* Add app styles here */
}
</style>
</head>
<body></body>
</html>

Related

ExtJS 4 and display PDF from BLOB

I try to display PDF stream in separate window and this stream is from database BLOB field. My code is the following:
Ext.define('MyApp.view.ViewPDF', {
extend: 'Ext.window.Window',
alias: 'widget.mywindow',
requires: [
'Ext.toolbar.Toolbar',
'Ext.button.Button'
],
config: {
title: '',
source: ''
},
itemId: 'SHOW_PDF',
closable: false,
resizable: true,
modal: true,
onClose: function (clsbtn) {
clsbtn.up('window').destroy();
},
initComponent: function () {
var my = this;
Ext.apply(my, {
items: [
{
xtype: 'panel',
layout: 'fit',
width: 640,
height: 780,
items: [
{
items: {
xtype: 'component',
align: 'stretch',
autoEl: {
tag: 'iframe',
loadMask: 'Creating report...please wait!',
style: 'height: 100%; width: 100%; border: none',
src: 'data:application/pdf;base64,' + tutaj.getSource()
}
}
}
]
}
],
dockedItems: [
{
xtype: 'toolbar',
dock: 'bottom',
height: 30,
items: [
'->',
{
xtype: 'button',
baseCls: 'x-btn-default-large',
cls: 'cap-btn',
handler: my.onClose,
width: 55,
margin: '0, 10, 0, 0',
style: 'text-align: center',
text: 'Close'
}
]
}
]
});
my.callParent();
my.title = my.getTitle();
}
});
and it's working only via FireFox browser. In Chrome I can see empty window. So two questions (can't answer myself):
how to correct above to display this PDF stream in other browsers
how to display text in mask because loadMask in code above can't
work; just display text starting with window open and finishing when PDF is displayed
Be so kind as to prompt me what's wrong in this code.
I have created a FIDDLE using filefield, BLOB and FileReader. I have tested in chrome and Fire Fox. I hope this FIDDLE will help you or guid you to solve your problem.
CODE SNIPPET
Ext.create('Ext.form.Panel', {
renderTo: Ext.getBody(),
height: window.innerHeight,
title: 'Iframe Example for PDF',
bodyPadding: 10,
items: [{
xtype: 'fileuploadfield',
buttonOnly: true,
buttonText: 'Choose PDF and show via BLOB',
listeners: {
afterrender: function (cmp) {
cmp.fileInputEl.set({
accept: '.pdf'
});
},
change: function (f) {
var file = document.getElementById(f.fileInputEl.id).files[0];
this.up('form').doSetPDF(file);
}
}
}, {
xtype: 'fileuploadfield',
buttonOnly: true,
buttonText: 'Choose PDF and show via BASE64 ',
listeners: {
afterrender: function (cmp) {
cmp.fileInputEl.set({
accept: '.pdf'
});
},
change: function (f) {
var file = document.getElementById(f.fileInputEl.id).files[0];
this.up('form').doSetViaBase64(file);
}
}
}, {
xtype: 'box',
autoEl: {
tag: 'iframe',
loadMask: 'Creating report...please wait!',
width: '100%',
height: '100%'
}
}],
//Show pdf file using BLOB and createObjectURL
doSetPDF: function (file) {
if (file) {
var form = this,
blob, file;
if (Ext.isIE) {
this.doSetViaBase64(file)
} else {
blob = new Blob([file], {
type: 'application/pdf'
}),
file = window.URL.createObjectURL(blob);
form.getEl().query('iframe')[0].src = file;
}
}
},
//Show pdf file using BASE64
doSetViaBase64: function (file) {
var form = this,
reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
form.getEl().query('iframe')[0].src = this.result;
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
});

Existing Custom Rally App is producign results as expected

I got the below reference of CustomHTML App for Rally and added into my custom report page in my project workspace. UI Worked, but somehow whatever simple query I give in, there is no result shown. Please review and correct me if I am doing any wrong.
Find fixed defects within certain dates
<script type="text/javascript" src="/apps/2.0rc1/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
layout: {
type: 'vbox',
align: 'stretch'
},
items:[
{
xtype: 'panel',
layout: 'anchor',
border: true,
fieldDefaults: {
labelWidth: 40
},
defaultType: 'textfield',
bodyPadding: 5,
items: [
{
fieldLabel: 'Query',
itemId: 'queryField',
anchor:'100%',
width: 700,
height: 100,
xtype: 'textarea',
value: '{\n'+
' "_TypeHierarchy": "Defect",\n'+
'"__At": "2016-10-14T00:00:00Z"'+
'}'
},
{
fieldLabel: 'Fields',
itemId: 'fieldsField',
anchor: '100%',
width: 700,
value: "ObjectID, _ValidFrom, Name, State, Resolution"
},
{
fieldLabel: 'Sort',
itemId: 'sortField',
anchor: '100%',
width: 700,
value: "{'ObjectID' : -1, '_ValidFrom': 1}"
},
{
fieldLabel: 'Page Size',
itemId: 'pageSizeField',
anchor: '100%',
width: 700,
value: '10'
},
{
fieldLabel: 'Hydrate',
itemId: 'hydrate',
anchor: '100%',
width: 700,
value: "State, Resolution"
},
],
buttons: [
{
xtype: 'rallybutton',
text: 'Search',
itemId: 'searchButton'
}
]
},
{
xtype: 'panel',
itemId: 'gridHolder',
layout: 'fit',
height: 400
}
],
launch: function() {
var button = this.down('#searchButton');
button.on('click', this.searchClicked, this);
},
searchClicked: function(){
var queryField = this.down('#queryField');
var query = queryField.getValue();
var selectedFields = this.down('#fieldsField').getValue();
if(selectedFields){
if(selectedFields === 'true'){
selectedFields = true;
}
else{
selectedFields = selectedFields.split(', ');
}
}
var sort = this.down('#sortField').getValue();
var pageSize = this.down('#pageSizeField').getValue();
var parsedPageSize = parseInt(pageSize, 10);
// don't allow empty or 0 pagesize
pageSize = (parsedPageSize) ? parsedPageSize : 10;
var callback = Ext.bind(this.processSnapshots, this);
this.doSearch(query, selectedFields, sort, pageSize, callback);
},
createSortMap: function(csvFields){
var fields = csvFields.split(', ');
var sortMap = {};
for(var field in fields){
if(fields.hasOwnProperty(field)){
sortMap[field] = 1;
}
}
return sortMap;
},
doSearch: function(query, fields, sort, pageSize, callback){
var transformStore = Ext.create('Rally.data.lookback.SnapshotStore', {
context: {
workspace: this.context.getWorkspace(),
project: this.context.getProject()
},
fetch: fields,
find: query,
autoLoad: true,
hydrate: ["State","Resolution"],
listeners: {
scope: this,
load: this.processSnapshots
}
});
},
processSnapshots: function(store, records){
var snapshotGrid = Ext.create('Rally.ui.grid.Grid', {
title: 'Snapshots',
store: store,
columnCfgs: [
{
text: 'ObjectID',
dataIndex: 'ObjectID'
},
{
text: 'Name',
dataIndex: 'Name'
},
{
text: 'Project',
dataIndex: 'Project'
},
{
text: '_ValidFrom',
dataIndex: '_ValidFrom'
},
{
text: '_ValidTo',
dataIndex: '_ValidTo'
},
{
text: 'State',
dataIndex: 'State'
},
{
text: 'Resolution',
dataIndex: 'Resolution'
},
],
height: 400
});
var gridHolder = this.down('#gridHolder');
}
gridHolder.removeAll(true);
gridHolder.add(snapshotGrid);
});
Rally.launchApp('CustomApp', {
name: 'lbapi'
});
});
</script>
<style type="text/css">
.app {
/* Add app styles here */
}
</style>
</head>
<body></body>
</html>
There were a couple syntax errors in the posted code above. It was also written using a super old version of the sdk. I updated to the latest 2.1 and it seems to work pretty well now. More than likely most of the issues with the old app was the Lookback API request timing out. The new SDK has a longer default timeout.
<!DOCTYPE html>
<html>
<head>
<title>Lookback API Query</title>
<script type="text/javascript" src="/apps/2.1/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
layout: {
type: 'vbox',
align: 'stretch'
},
items: [{
xtype: 'panel',
layout: 'anchor',
border: true,
fieldDefaults: {
labelWidth: 40
},
defaultType: 'textfield',
bodyPadding: 5,
items: [{
fieldLabel: 'Query',
itemId: 'queryField',
anchor: '100%',
width: 700,
height: 100,
xtype: 'textarea',
value: '{\n' +
' "_TypeHierarchy": "Defect",\n' +
'"__At": "2016-10-14T00:00:00Z"\n' +
'}'
}, {
fieldLabel: 'Fields',
itemId: 'fieldsField',
anchor: '100%',
width: 700,
value: "ObjectID, _ValidFrom, Name, State, Resolution"
}, {
fieldLabel: 'Sort',
itemId: 'sortField',
anchor: '100%',
width: 700,
value: "{'ObjectID' : -1, '_ValidFrom': 1}"
}, {
fieldLabel: 'Page Size',
itemId: 'pageSizeField',
anchor: '100%',
width: 700,
value: '10'
}, {
fieldLabel: 'Hydrate',
itemId: 'hydrate',
anchor: '100%',
width: 700,
value: "State, Resolution"
}, ],
buttons: [{
xtype: 'rallybutton',
text: 'Search',
itemId: 'searchButton'
}]
}, {
xtype: 'panel',
itemId: 'gridHolder',
layout: 'fit',
height: 400
}],
launch: function() {
var button = this.down('#searchButton');
button.on('click', this.searchClicked, this);
},
searchClicked: function() {
var queryField = this.down('#queryField');
var query = queryField.getValue();
var selectedFields = this.down('#fieldsField').getValue();
if (selectedFields) {
if (selectedFields === 'true') {
selectedFields = true;
} else {
selectedFields = selectedFields.split(', ');
}
}
var sort = this.down('#sortField').getValue();
var pageSize = this.down('#pageSizeField').getValue();
var parsedPageSize = parseInt(pageSize, 10);
// don't allow empty or 0 pagesize
pageSize = (parsedPageSize) ? parsedPageSize : 10;
var callback = Ext.bind(this.processSnapshots, this);
this.doSearch(query, selectedFields, sort, pageSize, callback);
},
createSortMap: function(csvFields) {
var fields = csvFields.split(', ');
var sortMap = {};
for (var field in fields) {
if (fields.hasOwnProperty(field)) {
sortMap[field] = 1;
}
}
return sortMap;
},
doSearch: function(query, fields, sort, pageSize, callback) {
var transformStore = Ext.create('Rally.data.lookback.SnapshotStore', {
context: {
workspace: this.context.getWorkspace(),
project: this.context.getProject()
},
fetch: fields,
pageSize: pageSize,
find: query,
autoLoad: true,
hydrate: ["State", "Resolution"],
listeners: {
scope: this,
load: this.processSnapshots
}
});
},
processSnapshots: function(store, records) {
var snapshotGrid = Ext.create('Rally.ui.grid.Grid', {
title: 'Snapshots',
store: store,
columnCfgs: [{
text: 'ObjectID',
dataIndex: 'ObjectID'
}, {
text: 'Name',
dataIndex: 'Name'
}, {
text: 'Project',
dataIndex: 'Project'
}, {
text: '_ValidFrom',
dataIndex: '_ValidFrom'
}, {
text: '_ValidTo',
dataIndex: '_ValidTo'
}, {
text: 'State',
dataIndex: 'State'
}, {
text: 'Resolution',
dataIndex: 'Resolution'
}, ],
height: 400
});
var gridHolder = this.down('#gridHolder');
gridHolder.removeAll(true);
gridHolder.add(snapshotGrid);
}
});
Rally.launchApp('CustomApp', {
name: 'lbapi'
});
});
</script>
<style type="text/css">
.app {
/* Add app styles here */
}
</style>
</head>
<body></body>
</html>

How to show task revisions in custom grid?

I have a custom grid that displays open tasks filtered by (Owner = some-user#company.com).
I would like to include the last revision for each task in a custom grid, but the Revision column is not available on the settings dialog. How to traverse from Revision History to individual revisions?
It can't be done with a custom grid, but can be done with a custom code. Here is an app example that populates a grid based on a selection in the UserSearchComboBox , and then displays the last revision of a selected task on a click event.
You may copy the html file into a custom page from this github repo:
Here is the js file:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
var context = this.getContext ();
var currentProject = context.getProject()._ref;
var panel = Ext.create('Ext.panel.Panel', {
layout: 'hbox',
itemId: 'parentPanel',
componentCls: 'panel',
items: [
{
xtype: 'rallyusersearchcombobox',
fieldLabel: 'SELECT USER:',
project: currentProject,
listeners:{
ready: function(combobox){
this._onUserSelected(combobox.getRecord());
},
select: function(combobox){
if (this.down('#c').html !== 'No task is selected') {
Ext.getCmp('c').update('No task is selected');
}
this._onUserSelected(combobox.getRecord());
},
scope: this
}
},
{
xtype: 'panel',
title: 'Tasks',
width: 600,
itemId: 'childPanel1'
},
{
xtype: 'panel',
title: 'Last Revision',
width: 600,
itemId: 'childPanel2'
}
],
});
this.add(panel);
this.down('#childPanel2').add({
id: 'c',
padding: 10,
maxWidth: 600,
maxHeight: 400,
overflowX: 'auto',
overflowY: 'auto',
html: 'No task is selected'
});
},
_onUserSelected:function(record){
var user = record.data['_ref'];
if(user){
var filter = Ext.create('Rally.data.QueryFilter', {
property: 'Owner',
operator: '=',
value: user
});
filter = filter.and({
property: 'State',
operator: '<',
value: 'Completed'
});
filter.toString();
Ext.create('Rally.data.WsapiDataStore', {
model: 'Task',
fetch: [ 'DragAndDropRank','FormattedID','Name','State','RevisionHistory'],
autoLoad: true,
filters : [filter],
sorters:[
{
property: 'DragAndDropRank',
direction: 'ASC'
}
],
listeners: {
load: this._onTaskDataLoaded,
scope: this
}
});
}
},
_onTaskDataLoaded: function(store, data) {
var customRecords = [];
Ext.Array.each(data, function(task, index) {
customRecords.push({
_ref: task.get('_ref'),
FormattedID: task.get('FormattedID'),
Name: task.get('Name'),
RevisionID: Rally.util.Ref.getOidFromRef(task.get('RevisionHistory')),
});
}, this);
this._updateGrid(store,data);
},
_updateGrid: function(store, data){
if (!this.down('#g')) {
this._createGrid(store,data);
}
else{
this.down('#g').reconfigure(store);
}
},
_createGrid: function(store,data){
var that = this;
var g = Ext.create('Rally.ui.grid.Grid', {
id: 'g',
store: store,
enableRanking: true,
columnCfgs: [
{text: 'Formatted ID', dataIndex: 'FormattedID'},
{text: 'Name', dataIndex: 'Name'},
{text: 'State', dataIndex: 'State'},
{text: 'Last Revision',
renderer: function (v, m, r) {
var id = Ext.id();
Ext.defer(function () {
Ext.widget('button', {
renderTo: id,
text: 'see',
width: 50,
handler: function () {
console.log('r', r.data);
that._getRevisionHistory(data, r.data);
}
});
}, 50);
return Ext.String.format('<div id="{0}"></div>', id);
}
}
],
height: 400,
});
this.down('#childPanel1').add(g);
},
_getRevisionHistory: function(taskList, task) {
this._task = task;
this._revisionModel = Rally.data.ModelFactory.getModel({
type: 'RevisionHistory',
scope: this,
success: this._onModelCreated
});
},
_onModelCreated: function(model) {
model.load(Rally.util.Ref.getOidFromRef(this._task.RevisionHistory._ref),{
scope: this,
success: this._onModelLoaded
});
},
_onModelLoaded: function(record, operation) {
record.getCollection('Revisions').load({
fetch: true,
scope: this,
callback: function(revisions, operation, success) {
this._onRevisionsLoaded(revisions, record);
}
});
},
_onRevisionsLoaded: function(revisions, record) {
var lastRev = _.first(revisions).data;
console.log('_onRevisionsLoaded: ',lastRev.Description, lastRev.RevisionNumber, lastRev.CreationDate );
this._displayLastRevision(lastRev.Description,lastRev.RevisionNumber, lastRev.CreationDate );
},
_displayLastRevision:function(desc, num, date){
Ext.getCmp('c').update('<b>' + this._task.FormattedID + '</b><br/><b>Revision CreationDate: </b>' + date +'<br /><b>Description:</b>' + desc + '<br /><b>Revision Number:</b>' + num + '<br />');
}
});

Rally SDK 2.0 Display User Story Parent (Feature) and Sort by Parent in rallygrid

Having trouble showing parent feature of user stories and sorting by that same parent field. Here's my code. I see empty value in Parent column unless the parent is another user story. And I am not able to sort by Parent field.
Your help would be greatly appreciated!
Thanks!
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items: [
{
xtype: 'container',
itemId: 'iterationFilter'
},
{
xtype: 'container',
itemId: 'grid',
width: 800
}
],
launch: function() {
this.down('#iterationFilter').add({
xtype: 'rallyiterationcombobox',
cls: 'filter',
model: 'UserStory',
field: 'Iteration',
listeners: {
ready: this._onIterationComboBoxLoad,
select: this._onIterationComboBoxSelect,
scope: this
}
});
},
_onIterationComboBoxLoad: function(comboBox) {
this.iterationComboBox = comboBox;
Rally.data.ModelFactory.getModel({
type: 'UserStory',
success: this._onModelRetrieved,
scope: this
});
},
_getFilter: function() {
var filter = [];
filter.push({
property: 'Iteration',
operator: '=',
value: this.iterationComboBox.getValue()
});
return filter;
},
_onIterationComboBoxSelect: function() {
this._onSettingsChange();
},
_onSettingsChange: function() {
this.grid.filter(this._getFilter(), true, true);
},
_onModelRetrieved: function(model) {
this.grid = this.down('#grid').add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
'Plan Estimate',
'Parent',
'Schedule State',
'StoryType'
],
storeConfig: {
context: this.context.getDataContext(),
filters: this._getFilter()
},
showPagingToolbar: true,
enableEditing: false
});
}
});
});
Rally.launchApp('CustomApp', {
name: 'Defect Dashboard'
});
</script>
User Stories have two different fields for their Parent. If the Parent is another story it uses Parent. In the case where the Parent is a Portfolio Item like a Feature the parent will be called PortfolioItem.
You can see the fields on user story by looking at our webservice docs.
In your example you would have to change your column configs to include PorfolioItem
columnCfgs: [
'FormattedID',
'Name',
'Plan Estimate',
'PortfolioItem',
'Schedule State',
'StoryType'
],
I was at least able to show the name of feature for each user story. I am still having an issue to make it sortable though. :(
<!DOCTYPE html>
<html>
<head>
<title>Grid Example</title>
<script type="text/javascript" src="/apps/2.0p4/sdk.js?wsapiVersion=1.38"></script>
<script type="text/javascript">
Rally.onReady(function() {
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items: [
{
xtype: 'container',
itemId: 'iterationFilter'
},
{
xtype: 'container',
itemId: 'grid'
//width: 800
}
],
launch: function() {
this.down('#iterationFilter').add({
xtype: 'rallyiterationcombobox',
cls: 'filter',
model: 'UserStory',
field: 'Iteration',
listeners: {
ready: this._onIterationComboBoxLoad,
select: this._onIterationComboBoxSelect,
scope: this
}
});
},
_onIterationComboBoxLoad: function(comboBox) {
this.iterationComboBox = comboBox;
Rally.data.ModelFactory.getModel({
type: 'UserStory',
success: this._onModelRetrieved,
scope: this
});
},
_getFilter: function() {
var filter = [];
filter.push({
property: 'Iteration',
operator: '=',
value: this.iterationComboBox.getValue()
});
return filter;
},
_onIterationComboBoxSelect: function() {
this._onSettingsChange();
},
_onSettingsChange: function() {
this.grid.filter(this._getFilter(), true, true);
},
_onModelRetrieved: function(model) {
this.grid = this.down('#grid').add({
xtype: 'rallygrid',
model: model,
columnCfgs: [
'FormattedID',
'Name',
'PlanEstimate',
{
text: 'Feature',
dataIndex: 'PortfolioItem',
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
if (value != null) {
return value.Name;
}
return '';
}
},
'ScheduleState',
'StoryType'
],
storeConfig: {
context: this.context.getDataContext(),
remoteSort: false,
filters: this._getFilter()
},
showPagingToolbar: true,
enableEditing: false
});
}
});
});
Rally.launchApp('CustomApp', {
name: 'Defect Dashboard'
});
</script>
<style type="text/css">
.filter {
float: left;
margin: 5px 5px;
vertical-align: middle;
}
</style>
</head>
<body></body>
</html>
Looking at your revised solution with the renderer, all you need to add is a doSort method to the custom column you have set up. Make sure you set remoteSort to false on the store, then you can override the sorting with a method:
doSort: function(state) {
var ds = this.up('grid').getStore();
var field = this.getSortParam();
ds.sort({
property: field,
direction: state,
sorterFn: function(v1, v2){
v1 = v1.get(field);
v2 = v2.get(field);
return v1.length > v2.length ? 1 : (v1.length < v2.length ? -1 : 0);
}
});
}
This sorter happens to sort by the length of the the field, but you can change it to do what you want. See Ext js sorting custom column by contents

Remove a record from a Grid Locally

I have a Grid in a Window. When i click on a row of that window and click on the remove button, that row should get removed from the window. (I think i will have to remove it from Store and reload the grid, so the changes will be picked)
I am unable to get the click event and remove the row from the store. I have added the button for this but not able to get the Grid record and remove it from store and reloading it.
My code is as follows;
Ext.define('MyApp.view.Boy', {
extend: 'Ext.window.Window',
alias: 'widget.boy',
height: 800,
width: 900,
layout: {
type: 'absolute'
},
initComponent: function() {
var me = this;
Ext.applyIf(me, {
items: [
{
xtype: 'gridpanel',
height: 500,
width: 800,
title: 'My Grid Panel',
store: 'School',
viewConfig: {
},
columns: [
{
xtype: 'gridcolumn',
dataIndex: 'Id',
text: 'id'
},
{
xtype: 'gridcolumn',
dataIndex: 'name',
text: 'name'
}
]
},
{
xtype: 'button',
height: 40,
width: 150,
text: 'Remove',
listeners: {
click: {
fn: me.removebuttonclick,
scope: me
}
}
}
]
});
me.callParent(arguments);
},
removebuttonclick: function(button, e, options) {
console.log('removebuttonclick');
}
});
Something like:
Ext.define('MyApp.view.Boy', {
extend: 'Ext.window.Window',
alias: 'widget.boy',
height: 800,
width: 900,
layout: {
type: 'absolute'
},
initComponent: function() {
var me = this;
var this.grid = Ext.widget({
xtype: 'gridpanel',
height: 500,
width: 800,
title: 'My Grid Panel',
store: 'School',
viewConfig: {
},
columns: [
{
xtype: 'gridcolumn',
dataIndex: 'Id',
text: 'id'
},
{
xtype: 'gridcolumn',
dataIndex: 'name',
text: 'name'
}
]
});
Ext.applyIf(me, {
items: [
this.grid, //oopsie
{
xtype: 'button',
height: 40,
width: 150,
text: 'Remove',
listeners: {
click: {
fn: me.removebuttonclick,
scope: me
}
}
}
]
});
me.callParent(arguments);
},
removebuttonclick: function(button, e, options) {
var me = this;
Ext.Array.each(this.grid.getSelectionModel().selected,function(record, index,selectedRows){
me.grid.getStore().remove(record);
});
}
});