表示できるようにハックする方法を見つけました。それが価値があるかどうか、および追加/削除のために何をしたいのかはわかりませんが、これは正しい道を歩むのに役立つかもしれません.
要約すると、問題は cardboard/column クラスが単一値フィールドで動作するように設計されており、作成された各列が列構成に基づいて Rally に対して個別のクエリを実行することです。rallycardboard と rallycolumn をオーバーライドする必要があります。以下に貼り付け可能な完全な html を示しますが、一度に 1 つずつ見ていきましょう。
少なくとも、これは rally クラスのソース コードを取得して、それらをオーバーライドする何かを作成する方法の良い例かもしれません。
データ:
既存の cardboard にレコード タイプとフィールドが与えられ、フィールドの有効な値ごとに列が作成されます。この情報は、Rally にストーリーと属性定義を照会することによって取得されます。ただし、データを少し異なる方法で使用したいので、別のデータ ストアを作成してフィードする必要があります。したがって、wsapidatastore を使用して、要求したレコードを取得します (この例では、 先代後継者がいるUS37という話があります)。ある意味、これは Excel でクロス集計を行うようなものです。1 つのレコード (37) を別のレコードに関連付ける代わりに、すべてのストーリーのデータ セットを作成し、"_column" と呼ばれる新しいフィールドでそれらの関係を定義したいと考えています。このような:
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 cardboard でニーズに対応できます。しかし、問題を引き起こしている関数が 1 つあるため、それをオーバーライドする必要があります: _retrieveModels. この関数は通常、レコード タイプ (ユーザー ストーリーなど) を受け取り、それに基づいて Rally 定義に基づくデータ モデルを作成します。ただし、UserStory レコードを直接使用しているわけではありません。「_columns」フィールドを追加できるように、独自のモデルを定義する必要がありました。そこで、新しい定義を作成します (上記の「DependencyCardboard」の create ステートメントで使用します)。
(タイトルをクリックするだけで、API 内のすべての Rally オブジェクトのソース コードを表示できるので、以下のメソッドを基本クラスのメソッドと比較できることを思い出してください。)
Rally カードボードが行うすべてのことを保持し、次のようにして 1 つのメソッドのみをオーバーライドできます。
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;
}
終了
したがって、これらすべてのピースを一緒に追加すると、パネルにプッシュできる 1 つの長い 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>