我找到了破解它的方法,以便您可以进行显示。不确定它是否值得以及您想为添加/删除做什么,但这可能有助于您走上正确的道路。
总之,问题在于纸板/列类被设计为使用单值字段,并且创建的每一列都会根据列配置对 Rally 进行单独的查询。您需要覆盖 rallycardboard 和 rallycolumn。我会给出完整的 html,你可以在下面粘贴,但让我们一次打一个。
如果不出意外,这可能是一个很好的例子,说明如何获取集会类的源代码并制作一些东西来覆盖它们。
数据:
现有的纸板被赋予一个记录类型和字段,并为该字段的每个有效值创建一个列。它通过向 Rally 查询故事和属性定义来获取此信息。但是我们想以稍微不同的方式使用我们的数据,所以我们必须创建一个不同的数据存储并将其输入。所以我们想使用 wsapidatastore 继续获取我们要求的记录(在这个例子中,我有个故事叫US37,有前有后继)。在某种程度上,这就像在 Excel 中做一个交叉表。我们希望创建一个包含所有故事的数据集,并在我称之为“_column”的新字段中定义它们的关系,而不是拥有一条与其他记录相关的记录 (37)。像这样:
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 );
} );
我们将数据推送到一个对象数组中,每个对象都有一个新字段来定义它应该进入哪一列。但这还不够,因为我们需要将数据放入存储中。商店需要一个模型——我们必须以卡片渲染器知道如何访问数据的方式定义字段。似乎没有一种简单的方法可以将字段定义添加到现有的拉力模型中,所以应该这样做(拉力模型具有唯一的字段信息和一个名为 getField() 的方法,因此我们必须添加:
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
});
现在,我们将创建一个纸板,但不是集会纸板,而是从我们将在下面定义的类('DependencyCardboard')中制作一个。此外,我们将传递一个新的列定义,我们还将在下面定义('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
}]
});
纸板:
大多数情况下,现有的 Rally 纸板可以满足我们的需求,因为所有查询都在列本身中完成。但是我们仍然必须重写它,因为有一个函数给我们带来了问题:_retrieveModels。该函数通常采用记录类型(例如,用户故事),并在此基础上创建基于 Rally 定义的数据模型。但是,我们没有直接使用 UserStory 记录;我们必须定义自己的模型,以便添加“_columns”字段。因此,我们创建了一个新定义(我们在上面的“DependencyCardboard”创建语句中使用它)。
(请记住,我们只需单击标题即可查看 API 中所有 Rally 对象的源代码,因此我们可以将以下方法与基类中的方法进行比较。)
我们可以保留 Rally 纸板所做的所有事情,并且只通过这样做覆盖一个方法:
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 );
}
}
});
柱子:
每一列通常都会转到 Rally 并说“给我所有具有与列名称相同的字段的故事”。但是我们将 store 传递给 cardboard,所以我们需要覆盖 _queryForData。此外,当我们这样做时,关于定义列高会发生一些事情(我不知道为什么!)所以我不得不在 getColumnHeightFromCards() 方法中添加一个小问题。
_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;
}
结束
因此,如果您将所有这些部分加在一起,您会得到一个长 html 文件,我们可以将其推送到面板中(并且您可以继续研究如何覆盖拖动结果并为第一个项目添加选择器面板。 (并且你可以更好地抽象成它自己的类))。
完整的东西:
<!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>