결함을 기반으로하는 사용자 정의 그리드 쿼리를 사용할 때 나는 종종 연결된 사용자 스토리의 태그를 기준으로 필터링합니다. 실제 기능 계층 구조를 사용하고 싶습니다. 예 : 링크 된 기사가 주어진 기능이나 이니셔티브 아래에있는 모든 결함을 보여줍니다. 문서를 보면서이 작업을 수행 할 수 없습니다.특정 기능에 대한 요구 사항과 결함 위치를 쿼리하는 방법
1
A
답변
0
태그 속성은 Requirement가 상속하는 Artifact 객체에 존재하므로 Requirement.Tags를 탐색 할 수 있습니다. 지형지 물 속성이 요구 사항에 없습니다. 요구 사항을 상속 한 HierarchicalRequirement에 있으므로 Requirement.Feature를 통과 할 수 없습니다. 이 컨텍스트에서는 사용자 지정 그리드가 적합한 선택이 아닐 수 있습니다. 하지만 모든 관계를 보여주는 맞춤 앱을 작성할 수 있습니다. 다음은 두 가지 콤보 상자가있는 맞춤 앱입니다. 출시와 기능. 기능 콤보 상자는 릴리스 콤보 상자의 선택 내용을 기반으로 채워집니다. 두 번째 콤보 상자에서 지형지 물을 선택하면 그 지형지 물에 연결된 지형지 물의 하위 사례 및 결함 (있는 경우)이 모눈에 채워집니다. 전체 코드 in this repo이 표시되고 html file을 사용자 정의 페이지로 복사 할 수 있습니다.
Ext.define('CustomApp', {
extend: 'Rally.app.TimeboxScopedApp',
componentCls: 'app',
scopeType: 'release',
comboboxConfig: {
fieldLabel: 'Select a Release:',
labelWidth: 100,
width: 300
},
addContent: function() {
this._makeCombobox();
},
onScopeChange: function() {
this._makeCombobox();
},
_makeCombobox: function() {
if (this.down('#features')) {
this.down('#features').destroy();
}
var features = Ext.create('Rally.ui.combobox.ComboBox',{
id: 'features',
storeConfig: {
model: 'PortfolioItem/Feature',
fetch: ['FormattedID','Name','Release', 'UserStories'],
pageSize: 100,
autoLoad: true,
filters: [this.getContext().getTimeboxScope().getQueryFilter()]
},
fieldLabel: 'select Feature',
listeners:{
ready: function(combobox){
if (combobox.getRecord()) {
console.log('ready',combobox.getRecord().get('_ref'));
this._onFeatureSelected(combobox.getRecord());
}
else{
console.log('selected release has no features');
if (this.down('#grid')) {
this.down('#grid').destroy();
}
}
},
select: function(combobox){
if (combobox.getRecord()) {
console.log('select',combobox.getRecord().get('_ref'));
this._onFeatureSelected(combobox.getRecord());
}
},
scope: this
}
});
this.add(features);
},
_onFeatureSelected:function(feature){
console.log('feature', feature.get('Name'));
var f = {
FormattedID: feature.get('FormattedID'),
Name: feature.get('Name'),
_ref: feature.get("_ref"),
UserStories: []
};
var collection = feature.getCollection('UserStories', {fetch: ['Name','FormattedID','Owner', 'Defects']});
var that = this;
var count = collection.getCount();
console.log(count);
var stories = [];
var pendingStories = count;
collection.load({
callback: function(records, operation, success){
Ext.Array.each(records, function(story){
var s = {
FormattedID: story.get('FormattedID'),
Name: story.get('Name'),
_ref: story.get("_ref"),
DefectCount: story.get('Defects').Count,
Defects: []
};
var defects = story.getCollection('Defects');
var defectcount = defects.getCount();
var pendingDefects = defectcount;
defects.load({
fetch: ['FormattedID'],
callback: function(records, operation, success){
Ext.Array.each(records, function(defect){
s.Defects.push({_ref: defect.get('_ref'),
FormattedID: defect.get('FormattedID')
});
}, this);
--pendingDefects;
if (pendingDefects === 0) {
console.log(story.get('FormattedID') + ' - ' + story.get('Name'));
--pendingStories;
if (pendingStories === 0) {
console.log('stories inside callback',stories);
}
}
console.log('makeGrid');
that._makeGrid(stories);
},
scope: this
});
stories.push(s);
}, this);
}
});
},
_makeGrid: function(stories) {
var c = Ext.create('Ext.Container', {
layout: {
type: 'absolute'
},
x: 400
});
this.add(c);
this._store = Ext.create('Rally.data.custom.Store', {
data: stories,
pageSize: 100,
remoteSort:false
});
if (!this.down('#grid')){
c.add({
xtype: 'rallygrid',
itemId: 'grid',
store: this._store,
columnCfgs: [
{
text: 'Formatted ID', dataIndex: 'FormattedID', xtype: 'templatecolumn',
tpl: Ext.create('Rally.ui.renderer.template.FormattedIDTemplate')
},
{
text: 'Name', dataIndex: 'Name'
},
{
text: 'Defect Count', dataIndex: 'DefectCount'
},
{
text: 'Defects', dataIndex: 'Defects',
renderer: function(value) {
var html = [];
Ext.Array.each(value, function(defect){
html.push('<a href="' + Rally.nav.Manager.getDetailUrl(defect) + '">' + defect.FormattedID + '</a>')
});
return html.join(', ');
}
}
]
});
}
else{
this.down('#grid').reconfigure(this._store);
}
}
});