How to get filtered rows from GridX? - dojo

I'm using Dojo GridX with many modules, including filter:
grid = new Grid({
cacheClass : Cache,
structure: structure,
store: store,
modules : [ Sort, ColumnResizer, Pagination, PaginationBar, CellWidget, GridEdit,
Filter, FilterBar, QuickFilter, HiddenColumns, HScroller ],
autoHeight : true, autoWidth: false,
paginationBarSizes: [25, 50, 100],
paginationBarPosition: 'top,bottom',
}, gridNode);
grid.filterBar.applyFilter({type: 'all', conditions: [
{colId: 'type', condition: 'equal', type: 'Text', value: 'car'}
]})
I've wanted to access the items, that are matching the filter that was set. I've travelled through grid property in DOM explorer, I've found many store references in many modules, but all of them contained all items.
Is it possible to find out what items are visible in grid because they are matching filter, or at least those that are visible on current page? If so, how to do that?

My solution is:
try {
var filterData = [];
var ids = grid.model._exts.clientFilter._ids;
for ( var i = 0; i < ids.length; ++i) {
var id = ids[i];
var item = grid.model.store.get(id);
filterData.push(item);
}
var store = new MemoryStore({
data : filterData
});
} catch (error) {
console.log("Filter is not set.");
}

I was able to obtain filtered gridX data rows using gridX Exporter. Add this Exporter module to your grid. This module does exports the filtered data. Then, convert CSV to Json. There are many CSV to Json conversion javasripts out there.
this.navResult.grid.exporter.toCSV(args).then(this.showResult, this.onError, null)

Based on AirG answer I have designed the following solution. Take into account that there are two cases, with or without filter and that you must be aware of the order of rows if you have applied some sort. At least this works for me.
var store = new Store({
idProperty: "idPeople", data: [
{ idPeople: 1, name: 'John', score: 130, city: 'New York', birthday: '31/02/1980' },
{ idPeople: 2, name: 'Alice', score: 123, city: 'Wáshington', birthday: '07/12/1984' },
{ idPeople: 3, name: 'Lee', score: 149, city: 'Shanghai', birthday: '8/10/1986' },
...
]
});
gridx = new GridX({
id: 'mygridx',
cacheClass: Cache,
store: store,
...
modules: [
...
{
moduleClass: Dod,
defaultShow: false,
useAnimation: true,
showExpando: true,
detailProvider: gridXDetailProvider
},
...
],
...
}, 'gridNode');
function gridXDetailProvider (grid, rowId, detailNode, rendered) {
gridXGetDetailContent(grid, rowId, detailNode);
rendered.callback();
return rendered;
}
function gridXGetDetailContent(grid, rowId, detailNode) {
if (grid.model._exts.clientFilter._ids === undefined || grid.model._exts.clientFilter._ids === 0) {
// No filter, with or without sort
detailNode.innerHTML = 'Hello ' + grid.row(grid.model._cache._priority.indexOf(rowId)).item().name + " with id " +
grid.row(grid.model._cache._priority.indexOf(rowId)).item().idPeople;
} else {
// With filter, with or without sort
detailNode.innerHTML = 'Hello ' + grid.row(grid.model._exts.clientFilter._ids.indexOf(rowId)).item().name + " with id " +
grid.row(grid.model._exts.clientFilter._ids.indexOf(rowId)).item().idPeople;
}
}
Hope that helps,
Santiago Horcajo

function getFilteredData() {
var filteredIds = grid.model._exts.clientFilter._ids;
return grid.store.data.filter(function(item) {
return filteredIds.indexOf(item.id) > -1;
});
}

Related

How to change the columns collection set of a kendo TreeList dynamically?

Try to change the columns list dynamically via a query ...
When I construct the TreeList, I call for columns :
$("#treelist").kendoTreeList({
columns: AnalyseCenterSKUService.getKPIColumnList($scope)
If I return a simple array with the fields, it's working ..
If I call a $http.get (inside my getKPIColumnList(..) function) which add some columns to the existing array of columns, the TreeList is not constructed correctly.
Any suggestion will be really appreciated ! :)
EDIT 22-10-2019 09:00
Treelist init
$("#treelist").kendoTreeList({
columns: AnalyseCenterSKUService.getKPIColumnList($scope),
scrollable: true,
columnMenu : {
columns : true
},
height: "100%",
dataBound: function (e) {
ExpandAll();
},
dataSource: {
schema: {
model: {
id: "id",
parentId: "parentId",
fields: {
id: { type: "number" },
parentId: { type: "number", nullable: true },
fields: {
id: { type: "number" },
parentId: { type: "number", nullable: false }
}
}
}
},
transport: {
read: {
url: "/api/AnalyseCenter/GetWorkOrderTree/0",
dataType: "json"
}
}
}
The getKPIColumnList return an static array + some push with dynamic columns (from DB)
angular.module('AnalyseCenterDirectives')
.service ('AnalyseCenterSKUService', function ($http) {
var toReturn = [ {field: "Name", title: "Hiérachie SKU", width: "30%" }, ..., ..., .... ];
I try in this function to push DB result
return $http.get("/api/AnalyseCenter/GetWorkOrderHistorianAdditonalColumns?equipmentName=" + equipmentName)
.then(function (result) {
var data = result.data;
if (data && data !== 'undefined') {
var fromDB = data;
angular.forEach(fromDB, function (tag) {
var tagName = tag.replace(".","_");
toReturn.push({
field: tagName, title: tag, width: '10%',
attributes: { style: "text-align:right;"} })
})
The stored procedure GetWorkOrderHistorianAdditonalColumns returns a list of string (future column)
That is because ajax is async, that means your tree list is being initialized before the request finishes. A classic question for JavaScript newcomers. I suggest you take a while to read about ajax, like How does AJAX works for instance.
Back to your problem. You need to create your tree list inside the success callback(I can't give you a more complete solution since I don't know what you're doing inside your function or which framework you're using to open that ajax request) with the result data, which is probably your columns. Then it would work as if you're initializing it with arrays.

Vue.js with iView Table - accessing Elasticsearch search response object

I am using the iView UI kit table in my Vue.js application that consumes an Elasticsearch API with axios. My problem is that I just can't seem to get to access the nested search response object, which is an array list object. I only get to access the 1st level fields, but not the nested ones. I don't know how to set the table row key in the iView table.
This is how my axios call and mapper methods look like:
listObjects(pageNumber){
const self = this
self.$Loading.start()
self.axios.get("/api/elasticsearch/")
.then(response => {
self.ajaxTableData = self.mapObjectToArray(response.data);
self.dataCount = self.ajaxTableData.length;
if(self.ajaxTableData.length < self.pageSize){
self.tableData = self.ajaxTableData;
} else {
self.tableData = self.ajaxTableData.slice(0,self.pageSize);
}
self.$Loading.finish()
})
.catch(e => {
self.tableData = []
self.$Loading.error()
self.errors.push(e)
})
},
mapObjectToArray(data){
var mappedData = Object.keys(data).map(key => {
return data[key];
})
return mappedData
},
The iView table columns look like this:
tableColumns: [
{
title: 'Study Date',
key: 'patientStudy.studyDate',
width: 140,
sortable: true,
sortType: 'desc'
},
{
title: 'Modality',
key: "generalSeries.modality",
width: 140,
sortable: true
},
...
]
The (raw) Elasticsearch documents look like this:
[
{ "score":1, "id":"3a710fa2c1b3f6125fc168c9308531b59e21d6b3",
"type":"dicom", "nestedIdentity":null, "version":-1, "fields":{
"highlightFields":{
},
"sortValues":[
],
"matchedQueries":[
],
"explanation":null,
"shard":null,
"index":"dicomdata",
"clusterAlias":null,
"sourceAsMap":{
"generalSeries":[
{
"seriesInstanceUid":"999.999.2.19960619.163000.1",
"modality":"MR",
"studyInstanceUid":"999.999.2.19960619.163000",
"seriesNumber":"1"
}
],
"patientStudy":[
{
"studyDate":"19990608"
}
]
}
}
]
And this is how the consumed object looks like:
As you can see, the fields I need to access are within the "sourceAsMap" object, and then nested in arrays.
How can I provide the iView table cell key to access them?
UPDATE:
I now "remapped" my Elasticsearch object before displaying it in the Vue.js table, and it works now. However, I don't think that the way I did it is very elegant or clean....maybe you can help me to do it in a better way. This is my method to remap the object:
getData(data){
let jsonMapped = []
for(let i = 0; i < data.length; i++){
let id = {}
id['id'] = data[i].id
let generalData = data[i]['sourceAsMap']['generalData'][0]
let generalSeries = data[i]['sourceAsMap']['generalSeries'][0]
let generalImage = data[i]['sourceAsMap']['generalImage'][0]
let generalEquipment = data[i]['sourceAsMap']['generalEquipment'][0]
let patient = data[i]['sourceAsMap']['patient'][0]
let patientStudy = data[i]['sourceAsMap']['patientStudy'][0]
let contrastBolus = data[i]['sourceAsMap']['contrastBolus'][0]
let specimen = data[i]['sourceAsMap']['specimen'][0]
jsonMapped[i] = Object.assign({}, id, generalData, generalSeries, generalImage, generalEquipment, patient,
patientStudy, contrastBolus, specimen)
}
return jsonMapped
},
The result is this:
Even though it now works, but how can I optimize this code?
A few functions can help you with your situation
let data = [{
key1: ['k1'],
key2: ['k2'],
key3: [{
subKey1: 'sk1',
subKey2: ['sk2'],
subObject: [{ name: 'John', surname: 'Doe' }],
items: [1, 2, 3, 5, 7]
}]
}];
function mapIt(data) {
if (isSingletonArray(data)) {
data = data[0];
}
for(const key in data) {
if (isSingletonArray(data[key])) {
data[key] = mapIt(data[key][0]);
} else {
data[key] = data[key];
}
}
return data;
}
function isSingletonArray(obj) {
return typeof obj === 'object' && Array.isArray(obj) && obj.length === 1;
}
console.log(mapIt(data));
Outputs:
{
key1: 'k1',
key2: 'k2',
key3: {
subKey1: 'sk1',
subKey2: 'sk2',
subObject: { name: 'John', surname: 'Doe' },
items: [ 1, 2, 3, 5, 7 ]
}
}
You can check it in your browser. mapIt unwraps the singleton arrays into objects or primitives.
But I recommend you to watch out special elastic client libraries for javascript. It could save you from writing extra code.

How to chain get and map the result with lodash?

I've got a list I'm trying to pull an object from using _.get but following that selection I need to loop over the object to create a new property. So far I've been successful using a combination of _.get and _.map as shown below but I'm hoping I can use _.chain in some way.
var selected = _.get(results, selectedId);
return _.map([selected], result => {
var reviews = result.reviews.map(review => {
var reviewed = review.userId === authenticatedUserId;
return _.extend({}, review, {reviewed: reviewed});
});
return _.extend({}, result, {reviews: reviews});
})[0];
Is it possible to do a transform like this using something other than map (as map required me to break this up/ creating an array with a solo item inside it). Thank you in advance!
I can see that you're creating unnecessary map() calls, you can simply reduce all those work into something like this:
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
The defaults() method is similar to extend() except once a property is set, additional values of the same property are ignored.
var selectedId = 1;
var authenticatedUserId = 1;
var results = {
1: [
{ userId: 1, text: 'hello' },
{ userId: 2, text: 'hey' },
{ userId: 1, text: 'world?' },
{ userId: 2, text: 'nah' },
]
};
var output = {
reviews: _.map(results[selectedId], function(review) {
return _.defaults({
reviewed: review.userId === authenticatedUserId
}, review);
})
};
document.body.innerHTML = '<pre>' + JSON.stringify(output, 0, 4) + '</pre>';
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Rally Cumulative Flow Diagram with Points

I'm looking to try and do a cumulative flow diagram by story points in rally with their newer API/SDK and found some sample code on their GitHub page RallyAnalytics GitHub
So after some work I have it working to some degree but don't understand or can find any documentation for how to configure this more. It looks like the report being generated is doing count and not the PlanEstimate which I tried to add in fieldsToSum. How can I get it to sum the PlanEstimate field by c_KanbanState and not just give me a count of stories that matched the c_KanbanState for that week? Sample code below minus the minified code from GitHub.
var userConfig = {
title: 'Cumulative Flow Diagram',
debug: false,
trace: false,
// asOf: "2012-11-01", // Optional. Only supply if want a specific time frame. Do not send in new Date().toISOString().
granularity: 'week',
fieldsToSum: ['PlanEstimate'],
scopeField: "Project", // Supports Iteration, Release, Tags, Project, _ProjectHierarchy, _ItemHierarchy
scopeValue: 'scope',
scopeData: {
StartDate: new Date("2012-12-01T07:00:00.000Z"),
EndDate: new Date(new Date()),
Name: ""
},
//fieldNames: ['count', 'PlanEstimate']
kanbanStateField: 'c_KanbanState',
chartSeries: [
{name: 'To Do'},
{name: 'Dev Ready'},
{name: 'In Dev'},
{name: 'Peer Review'},
{name: 'QA Ready'},
{name: 'QA Done'},
{name: 'Accepted'}
]
}
(function() {
var charts = {};
var visualizer;
var nameToDisplayNameMap;
createVisualization = function(visualizationData) {
if (typeof visualizationData !== "undefined" && visualizationData !== null) {
categories = visualizationData.categories;
series = visualizationData.series;
charts.lowestValueInLastState = visualizationData.lowestValueInLastState;
charts.chart = new Highcharts.Chart({
chart: {
renderTo: 'chart-container',
defaultSeriesType: 'column',
zoomType: 'x'
},
legend: {
enabled: true
},
credits: {
enabled: false
},
title: {
text: userConfig.title
},
subtitle: {
text: userConfig.scopeData.Name
},
xAxis: {
categories: categories,
tickmarkPlacement: 'on',
tickInterval: Math.floor(categories.length / 12) + 1,
title: {
text: userConfig.granularity.slice(0, 1).toUpperCase() + userConfig.granularity.slice(1) + 's'
}
},
yAxis: [
{
title: {
text: 'Total Points',
},
min: charts.lowestValueInLastState
}
],
tooltip: {
formatter: function() {
point = this.point
s = point.series.name + ': <b>' + point.y + '</b><br \>';
if (point.x == point.series.data.length - 1) {
s += point.category.slice(0, point.category.length - 1) + ' to-date';
} else {
s += point.category;
}
return s;
}
},
plotOptions: {
series: {
events: {
legendItemClick: function(event) {
if (this.chart.series.length == this.index + 1) {
if (!this.visible) {
this.chart.yAxis[0].setExtremes(charts.lowestValueInLastState);
} else {
this.chart.yAxis[0].setExtremes(0);
};
};
return true;
}
}
}
},
series: series
}); // end of chart
} else {
// Put a spinner in the chart containers until first fetch returns
$('#chart-container')
.html('<img height="20px" src="https://rally1.rallydev.com/slm/js-lib/ext/2.2/resources/images/default/grid/loading.gif"></img>')
.attr("style", "text-align:center");
};
};
$(document).ready(function() {
visualizer = new CFDVisualizer(charts, userConfig, createVisualization);
});
})();
You may be using a slightly older version because the latest doesn't have the fieldsToSum parameter in the config, but you can upgrade the chart to sum PlanEstimate by chaging a few lines in the CFDVisualizer.coffee to this:
#config.lumenizeCalculatorConfig.metrics = [
{f: 'groupBySum', field: 'PlanEstimate', groupByField: #config.kanbanStateField, allowedValues: allowedValues}
]
from:
#config.lumenizeCalculatorConfig.metrics = [
{f: 'groupByCount', groupByField: #config.kanbanStateField, allowedValues: allowedValues}
]
You should probably also change the axis label in the cfd.html.
If this proves too difficult to accomplish (CoffeeScript may be unfamiliar), let me know and I'll post a new version to GitHub.

Using Rally Cardboard UI to Display Predecessor/Successor Hierarchy

I'm currently trying to write a Rally app that will display the Predecessor/Successor hierarchy for a selected User Story. To illustrate, the user will select a User Story from a Chooser UI element. Then, a three-column Cardboard UI element will be generated--the leftmost column will contain all of the selected User Story's Predecessors (in card form), the middle column will contain the selected User Story's card, and the rightmost column will contain all of the selected User Story's Successors (in card form). From there, the Predecessor and Successor cards can be removed (denoting that they won't be Predecessors or Successors for the selected User Story) and new Predecessor/Successor cards can be added (denoting that they will become new Predecessors/Successors for the selected User Story).
However, my issue is this: the Cardboard UI was designed to display sets of different values for one particular attribute, but "Predecessor" and "Successor" don't fall into this category. Is there a possible way for me to display a User Story and then get its Predecessors and Successors and populate the rest of the board? I realize that it will take a substantial amount of modifications to the original board.
I've found a way to hack it so that you can do the display. Not sure if it's worth it or not and what you want to do for adding/removing, but this might help set you on the right path.
In summary, the problem is that the cardboard/column classes are designed to work with single value fields and each column that's created does an individual query to Rally based on the column configuration. You'll need to override the rallycardboard and the rallycolumn. I'll give the full html that you can paste in below, but let's hit this one piece at a time.
If nothing else, this might be a good example of how to take the source code of rally classes and make something to override them.
Data:
The existing cardboard is given a record type and field and runs off to create a column for each valid value for the field. It gets this information by querying Rally for the stories and for the attribute definitions. But we want to use our data in a slightly different way, so we'll have to make a different data store and feed it in. So we want to use a wsapidatastore to go ahead and get the record we asked for (in this example, I have a story called US37 that has predecessors and successors). In a way, this is like doing a cross-tab in Excel. Instead of having one record (37) that's related to others, we want to make a data set of all the stories and define their relationship in a new field, which I called "_column." Like this:
Ext.create('Rally.data.WsapiDataStore', {
model: "hierarchicalrequirement",
autoLoad: true,
fetch: ['Name','Predecessors','Successors','FormattedID','ObjectID','_ref'],
filters: [ {
property: 'FormattedID', operator: 'contains', value: '37'
} ] /* get the record US37 */,
listeners: {
load: function(store,data,success) {
if ( data.length === 1 ) {
var base_story = data[0].data;
var modified_records = [];
base_story._column = "base";
modified_records.push( base_story );
Ext.Array.each( base_story.Predecessors, function( story ) {
story._column = "predecessor";
modified_records.push( story );
} );
Ext.Array.each( base_story.Successors, function(story) {
story._column = "successor";
modified_records.push( story );
} );
We push the data into an array of objects with each having a new field to define which column it should go in. But this isn't quite enough, because we need to put the data into a store. The store needs a model -- and we have to define the fields in a way that the cardrenders know how to access the data. There doesn't seem to be an easy way to just add a field definition to an existing rally model, so this should do it (the rally model has unique field information and a method called getField(), so we have to add that:
Ext.define('CardModel', {
extend: 'Ext.data.Model',
fields: [
{ name: '_ref', type: 'string' },
{ name: 'ObjectID', type: 'number'},
{ name: 'Name', type: 'string', attributeDefinition: { AttributeType: 'STRING'} },
{ name: 'FormattedID', type: 'string'},
{ name: '_column', type: 'string' },
{ name: 'ScheduleState', type: 'string' } ] ,
getField: function(name) {
if ( this.data[name] ) {
var return_field = null;
Ext.Array.each( this.store.model.getFields(), function(field) {
if ( field.name === name ) {
return_field = field;
}
} );
return return_field;
} else {
return null;
}
}
});
var cardStore = Ext.create('Ext.data.Store',{
model: 'CardModel',
data: modified_records
});
Now, we'll create a cardboard, but instead of the rally cardboard, we'll make one from a class we're going to define below ('DependencyCardboard'). In addition, we'll pass along a new definition for the columns that we'll also define below ('dependencycolumn').
var cardboard = Ext.create('DependencyCardboard', {
attribute: '_column',
store: cardStore, /* special to our new cardboard type */
height: 500,
columns: [{
xtype: 'dependencycolumn',
displayValue: 'predecessor',
value: 'predecessor',
store: cardStore
},
{
xtype: 'dependencycolumn',
displayValue: 'base',
value: 'base',
store: cardStore
},
{
xtype: 'dependencycolumn',
displayValue: 'successor',
value: 'successor',
store: cardStore
}]
});
Cardboard:
Mostly, the existing Rally cardboard can handle our needs because all the querying is done down in the columns themselves. But we still have to override it because there is one function that is causing us problems: _retrieveModels. This function normally takes the record type(s) (e.g., User Story) and based on that creates a data model that is based on the Rally definition. However, we're not using the UserStory records directly; we've had to define our own model so we could add the "_columns" field. So, we make a new definition (which we use in the create statement above for "DependencyCardboard").
(Remember, we can see the source code for all of the Rally objects in the API just by clicking on the title, so we can compare the below method to the one in the base class.)
We can keep all of the stuff that the Rally cardboard does and only override the one method by doing this:
Ext.define( 'DependencyCardboard', {
extend: 'Rally.ui.cardboard.CardBoard',
alias: 'widget.dependencycardboard',
constructor: function(config) {
this.mergeConfig(config);
this.callParent([this.config]);
},
initComponent: function() {
this.callParent(arguments);
},
_retrieveModels: function(success) {
if ( this.store ) {
this.models = [ this.store.getProxy().getModel() ];
success.apply( this, arguments );
}
}
});
Column:
Each column normally goes off to Rally and says "give me all of the stories that have a field equal to the column name". But we're passing in the store to the cardboard, so we need to override _queryForData. In addition, there is something going on about defining the column height when we do this (I don't know why!) so I had to add a little catch in the getColumnHeightFromCards() method.
_queryForData: function() {
var allRecords = [];
var records = this.store.queryBy( function( record ) {
if ( record.data._column === this.getValue() ) { allRecords.push( record ); }
}, this);
this.createAndAddCards( allRecords );
},
getColumnHeightFromCards: function() {
var contentMinHeight = 500,
bottomPadding = 30,
cards = this.query(this.cardConfig.xtype),
height = bottomPadding;
for(var i = 0, l = cards.length; i < l; ++i) {
if ( cards[i].el ) {
height += cards[i].getHeight();
} else {
height += 100;
}
}
height = Math.max(height, contentMinHeight);
height += this.down('#columnHeader').getHeight();
return height;
}
Finish
So, if you add all those pieces together, you get one long html file that we can push into a panel (and that you can keep working on to figure out how to override dragging results and to add your chooser panel for the first item. (And you can make better abstracted into its own class)).
Full thing:
<!DOCTYPE html>
<html>
<head>
<title>cardboard</title>
<script type="text/javascript" src="/apps/2.0p3/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function() {
/*global console, Ext */
Ext.define( 'DependencyColumn', {
extend: 'Rally.ui.cardboard.Column',
alias: 'widget.dependencycolumn',
constructor: function(config) {
this.mergeConfig(config);
this.callParent([this.config]);
},
initComponent: function() {
this.callParent(arguments);
},
_queryForData: function() {
var allRecords = [];
var records = this.store.queryBy( function( record ) {
if ( record.data._column === this.getValue() ) { allRecords.push( record ); }
}, this);
this.createAndAddCards( allRecords );
},
getColumnHeightFromCards: function() {
var contentMinHeight = 500,
bottomPadding = 30,
cards = this.query(this.cardConfig.xtype),
height = bottomPadding;
for(var i = 0, l = cards.length; i < l; ++i) {
if ( cards[i].el ) {
height += cards[i].getHeight();
} else {
height += 100;
}
}
height = Math.max(height, contentMinHeight);
height += this.down('#columnHeader').getHeight();
return height;
}
});
/*global console, Ext */
Ext.define( 'DependencyCardboard', {
extend: 'Rally.ui.cardboard.CardBoard',
alias: 'widget.dependencycardboard',
constructor: function(config) {
this.mergeConfig(config);
this.callParent([this.config]);
},
initComponent: function() {
this.callParent(arguments);
},
_retrieveModels: function(success) {
if ( this.store ) {
this.models = [ this.store.getProxy().getModel() ];
success.apply( this, arguments );
}
}
});
/*global console, Ext */
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
items: [ { xtype: 'container', itemId: 'outer_box' }],
launch: function() {
Ext.create('Rally.data.WsapiDataStore', {
model: "hierarchicalrequirement",
autoLoad: true,
fetch: ['Name','Predecessors','Successors','FormattedID','ObjectID','_ref'],
filters: [ {
property: 'FormattedID', operator: 'contains', value: '37'
} ],
listeners: {
load: function(store,data,success) {
if ( data.length === 1 ) {
var base_story = data[0].data;
var modified_records = [];
base_story._column = "base";
modified_records.push( base_story );
Ext.Array.each( base_story.Predecessors, function( story ) {
story._column = "predecessor";
modified_records.push( story );
} );
Ext.Array.each( base_story.Successors, function(story) {
story._column = "successor";
modified_records.push( story );
} );
Ext.define('CardModel', {
extend: 'Ext.data.Model',
fields: [
{ name: '_ref', type: 'string' },
{ name: 'ObjectID', type: 'number'},
{ name: 'Name', type: 'string', attributeDefinition: { AttributeType: 'STRING'} },
{ name: 'FormattedID', type: 'string'},
{ name: '_column', type: 'string' },
{ name: 'ScheduleState', type: 'string' } ] ,
getField: function(name) {
if ( this.data[name] ) {
var return_field = null;
Ext.Array.each( this.store.model.getFields(), function(field) {
if ( field.name === name ) {
return_field = field;
}
} );
return return_field;
} else {
return null;
}
}
});
var cardStore = Ext.create('Ext.data.Store',{
model: 'CardModel',
data: modified_records
});
var cardboard = Ext.create('DependencyCardboard', {
attribute: '_column',
store: cardStore,
height: 500,
columns: [{
xtype: 'dependencycolumn',
displayValue: 'predecessor',
value: 'predecessor',
store: cardStore
},
{
xtype: 'dependencycolumn',
displayValue: 'base',
value: 'base',
store: cardStore
},
{
xtype: 'dependencycolumn',
displayValue: 'successor',
value: 'successor',
store: cardStore
}]
});
this.down('#outer_box').add( cardboard );
}
},
scope: this
}
});
}
});
Rally.launchApp('CustomApp', {
name: 'cardboard'
});
});
</script>
<style type="text/css">
.app {
/* Add app styles here */
}
</style>
</head>
<body></body>
</html>