I feel like I am missing something here, but I would like to know the MMF for a story. I tried pulling info from the Feature and PortfolioItem fields, but I only end up seeing the feature for the story.
Ext.create('Rally.data.WsapiDataStore', {
model: 'User Story',
autoLoad: true,
fetch: ['Name', "Iteration", "PlanEstimate", "PortfolioItem", "Feature"],
filters: filter,
listeners: {
load: function(store, records, success) {
this._handleData(store);
},
scope: this
}
});
Is there a way I can use the feature to see what its MMF is?
In Rally object model there is not an object called MMF.
If a Minimum Marketable Feature (MMF) is defined as the smallest piece of functionality which must be delivered in order for the customer to get value, that can be a Rally user story.
Related
I'm assembling a custom Rally app based on the SDK (v2). I have a list of stories (stories that my team is doing on behalf of some other team) - and I want to get the PortfolioItem/feature to which those stories are parented.
Looking for ideas on the optimal (performance) way to approach that. I suppose I could retrieve one feature at a time (how?) - though that causes network traffic per feature - but is there a way create a collection/model and fetch all the data in the collection at once? (something like "get me all the features in my list of of featureIds").
TIA
So first you'll start with getting the story model:
Rally.data.ModelFactory.getModel({
type: 'userstory',
success: this._onStoryModelRetrieved,
scope: this
});
And then with the model you can load individual stories like so:
_onStoryModelRetrieved: function(model) {
model.load(storyOid, {
fetch: ['FormattedID', 'Name', 'Feature'],
callback: this._onStoryLoaded,
scope: this
});
}
But you're right, there are perf issues with that. So alternatively you can create a store and create an or'ed together filter query of objectid's:
var storyObjectIDs = [12345, 23456, 34567];
Ext.create('Rally.data.wsapi.Store', {
model: 'userstory'
fetch: ['FormattedID', 'Name', 'Feature'],
filters: _.map(storyObjectIDs, function(storyOid) {
return {
property: 'ObjectID',
value: storyOid
};
}),
autoLoad: true,
listeners: {
load: function(store, records) {
//process records
}
}
});
I want to create a custom grid and only show userstories that have failed test cases. I checked the API docs and could not work it out.
I tried (TestCaseStatus < Complete) but did not get any results i also tried the same thing with defect status.
What is the specific syntax that i need to use?
In WS API documentation
TestCaseStatus attribute on HierarcicalRequirement object shows allowed values:
"NONE", "NONE_RUN", "SOME_RUN_SOME_NOT_PASSING", "SOME_RUN_ALL_PASSING", "ALL_RUN_NONE_PASSING", "ALL_RUN_ALL_PASSING"
If for example your custom app extends Rally.app.TimeboxScopedApp, and filters user stories by iteration, you may add another filter to exclude ALL_RUN_ALL_PASSING as follows:
onScopeChange: function() {
var filter = this.getContext().getTimeboxScope().getQueryFilter();
filter = filter.and({
property: 'TestCaseStatus',
operator: '<',
value: 'ALL_RUN_ALL_PASSING'
});
Ext.create('Rally.data.WsapiDataStore', {
model: 'UserStory',
fetch: ['FormattedID','Name','TestCases', 'TestCaseStatus'],
pageSize: 100,
autoLoad: true,
filters: [filter],
listeners: {
load: this._onDataLoaded,
scope: this
}
});
},
The full code is available in this github repo.
I'd like to put together a StoryMap app using Initiative (second level) portfolio items as the backbone (columns). To do this the app needs to query for all the second level portfolio items, and then use each PI as a column header in the rallycardboard.
I've gotten the cardboard to display the column headers correctly, but I have not been able to get it to display the cards, which should be the first level of portfolio items (PortfolioItem/Feature).
Here is my code so far:
launch: function() {
this._getInitiativeStore();
},
_getInitiativeStore: function() {
this.initiativeStore = Ext.create('Rally.data.wsapi.Store', {
model: 'PortfolioItem/Initiative',
fetch: ['Name', 'Children'],
autoLoad: true,
listeners: {
load: this._createCardBoard,
scope: this
}
});
},
_createCardBoard: function(store, records) {
var initiativeColumns = [];
Ext.each(records, function(record) {
initiativeColumns.push({
xtype: 'rallycardboardcolumn',
columnHeaderConfig: {
xtype: 'rallycardboardcolumnheader',
fieldToDisplay: 'Name',
record: record,
},
cardConfig: {
xtype: 'rallycard',
record: 'PortfolioItem/Feature'
},
fields: ['Name', 'Parent'],
valueField: 'Parent',
value: record.get('_ref') // BUG FIXED HERE. Was: record.get('Parent')
});
}, this);
var cardBoardConfig = {
xtype: 'rallycardboard',
types: ['PortfolioItem/Feature'],
columns: initiativeColumns,
attribute: 'Parent',
};
var cardBoard = this.add(cardBoardConfig);
console.log('cardboard', cardBoard);
}
I realize I am using this perhaps a bit differently than the authors have planned for, but I'm willing to extend the rallycardboard and rallycolumnheader objects with Ext.define if that's what it takes. I'm starting to look at the Rally source code but its slow going so far.
I was able to figure out the problem by using Ext.define() to override the cardboardcolumn getStoreFilter function to print out its filter value. Probably for somebody good with a browser debugger that would not have been necessary, but I'm not and it pinpointed the problem right away: The "value" field of the initiativeColumn configs should have been record.get('_ref'), not record.get('Parent'). I'll edit the code above so it works.
Now the basic board works great as a story map with portfolio items! Next step is to see if I can incorporate the concept of releases into the map.
Also, I think I found a bug in the 'rallycardboard' constructor-- if I pass it a context reference like: context: { project: 'project/XXX'} where XXX is an OID, it crashes. Instead I need to instantiate a context object and pass that. But that's inconsistent from other items like the wsapi store. Workaround is easy, but it is a bit annoying.
I've been thinking about this problem for a while, and I can't seem to come up with a reasonable solution. What I would like to do is create getters/setters for a textfield/its value in my view. I realize that the preferred Ext JS way is using a reference within the controller and getting it that way, but that doesn't feel very object-oriented to me. I'd also have to wrap these getters and setters because I want to output a message if the getter returns undefined. What I'd like to do is create my own getters/setters or somehow override the default getters/setters. Here are some ways I was thinking of accomplishing this.
I was thinking I could use the config {}, but that appears to only work for variables I want to define. I then was thinking of using an id somehow, but the community seems split on whether that's a good practice or not. Which leads to my current solution... wrapping. Here's my code:
LoginWindow
Ext.define('MyApp.view.LoginWindow', {
extend: 'Ext.window.Window',
alias: 'widget.loginWindow',
autoShow: true,
closable: false,
border: 0,
plain: true,
allowBlank: false,
title: "Enter your username",
modal: true,
config: {
buttons: [{
text: "Ok"
}],
items: [{
xtype: 'textfield',
fieldLabel: 'Username',
id: 'loginUserInput',
name: 'loginUserInput',
msgTarget: 'under',
validator: function(value) {
if (Ext.isEmpty(value)) {
return "You need to enter a username.";
}
return true;
}
}]
},
constructor: function(config) {
this.callParent(config);
},
getButton: function() {
console.log('here');
}
});
MyController
Ext.define('MyApp.controller.Chat', {
extend: 'Ext.app.Controller',
requires: [
'Views.ChatModule.view.LoginWindow'
],
refs: [{
ref: 'loginWindow',
selector: 'loginWindow',
xtype: 'loginWindow',
autoCreate: true
}, {
ref: 'loginUserInput',
selector: '#loginUserInput'
}],
init: function() {
// The events controller oversees
this.control({
'loginWindow button[text="Ok"]': {
'click': this.onSubmitLoginWindow
}
});
},
getLoginUserInputValue: function() {
var loginUserInput = this.getLoginUserInput();
if (loginUserInput) {
var username = loginUserInput.getValue();
if (username) {
console.log(username);
} else {
console.warn("username is undefined");
}
}
console.warn("loginUserInput is undefined");
},
onSubmitLoginWindow: function(button, event, eOpts) {
this.getLoginUserInputValue();
}
});
This works, and I realize it's a very nit-picky thing, but it just doesn't feel right to have the getter in the controller. I feel like it'd be more object-oriented if it was in the Window. However, if I put it in the Window, I believe my only option is to lean on ids or manually create the textfield in the Window's initComponent--which would involve saving off a reference of the textfield in there, but that seems a bit inefficient... as I would have to make a call to doLayout as well.
Just to reiterate, I'd love to have the getters/setters in the Window, and I'm looking for a quick way to reference it, similar to how the controller references objects. I believe the main answer will be to use ids and making a call to Ext.ComponentQuery.query('#loginUserInput') in the Window, but I'd like to know if there were any better approaches out there... like overriding the auto generated getters/setters or adding a simple getter/setter for an input's value.
Cross-post from the Sencha forums.
Edit
I guess I was a bit unclear with what I want. As a more general statement, instead of jamming all things related to my view in the controller, I'd like to store it all in the view itself, which includes things like getters/setters. One of these getters/setters just so happens to be the loginUserInput getter.
Using a model is an interesting idea, but I feel like that would be a whole lot of overhead for singleton values. I'm basically looking for something like Java's setters/getters in the LoginWindow view... and hopefully something as simple as (or close to) Java's.
The idea of including (encapsulating) it in the view makes the controller a bit cleaner, and if I delete the view, I'm deleting its functions as well, so I don't have to go hunting for the functions in the controller... all I have to worry about is removing the references (which should be minimal).
I think that the "OO" way that you're looking is to work with a Ext.data.Model for your form. If you look at the Ext.form.Basic you have methods to manipulate a model (called record) and also get the object with the values of your view. So you need:
When you create your form, use loadRecord() to bind your form to a Model.
At any time you need, use getValues() to retrieve the values of your form fields.
When submiting your form, use getRecord() and getValues() to sync your record.
Ext.define('MyApp.model.Login',{
fields : [{
name: 'username',
type: 'string'
},{
name: 'password',
type: 'string'
}]
});
Ext.define('MyApp.controller.Login',{
...
refs : [{
selector: 'window form',
ref: 'formPanel'
}],
...
openForm : function() {
//load your form and then bind the new record
var formPanel = this.getFormPanel(), //Ext.form.Panel
form = formPanel.getForm(); //Ext.form.Basic
form.loadRecord(Ext.create('MyApp.model.Login'));
},
save : function() {
//get the values in the view
var form = this.getFormPanel().getForm(),
vals = form.getValues(),
record = form.getRecord();
console.log(vals); //see the object representation of your view here
record.set(vals); //update your model
//do whatever you need with your model
}
...
});
This is an good example when you need to save the form data. In the login I think you can work directly with getValues() without binding it to a Ext.data.Model.
I am not quite certain what problem you are trying to solve to be honest with you.
If you do not like controllers listening to buttons within your window, you can have button handlers witin your view definition fire custom events that controllers can listen on. Use fireEvent method. And by the way initConfig is a recommended way to setup your views. You can break it up into methods if you wish, 'this' reference is available and is the View component being instantiated.
If you need to find inner components within the View there are many methods available from up /down to nextSibling and query .
For Components:
• Ext.getCmp(id)
• Ext.ComponentQuery.query()
• up()
• down()
• nextSibling()
• previousSibling()
• child()
• previousNode()
plus various find.. Methods
EDIT
I think I understood what you mean by getter and setters. Ext forms have the fields finders to make it easy to get and set data to individual fields. See these SO questions: Best way to access adjacent components / fields and EXT.JS getting a list of items from a fieldset
Also like Sergio said there is getRecord getValues and setRecord methods on the form to deal with data binding. Thats it.
EDIT2
The best starting point guide that shows clear and claen MVC patterns as well as form handling. http://docs.sencha.com/extjs/4.1.3/#!/guide/application_architecture
My thoughts are something like this:
...
items: [],
constructor: function(config) {
this.loginUserInput = Ext.create('Ext.form.field.Text', {
fieldLabel: 'Username',
id: 'loginUserInput',
name: 'loginUserInput',
msgTarget: 'under',
validator: function(value) {
if (Ext.isEmpty(value)) {
return "You need to enter a username.";
}
return true;
}
});
this.items.push(this.loginUserInput);
this.callParent(config);
},
getLoginUserInput: function() {
var loginUserInput = this.loginUserInput;
if (!loginUserInput) {
console.warn("LoginWindow::getLoginUserInput: loginUserInput is undefined");
}
return loginUserInput;
}
So instead of letting Ext do its magic, I am now instantiating the object on my own, which then allows me to store away a reference of it, so I can easily access it in my getter. I just wonder if this is creating any sort of performance hit. It doesn't seem like it'd be that much worse... it actually seems like it'd be a bit better because I'm not referencing this object by its ID, and I don't have to go searching for it when I need it.
When attepming to use a Rally.data.BulkRecordUpdater, I ran into a few problems. First of all, the documentation is incorrect. The example provided:
Rally.data.BulkRecordUpdater({
records: [record1, record2],
propertiesToUpdate: {
Parent: '/hierarchicalrequirement/123.js'
},
success: function(readOnlyRecords){
//all updates finished, except for given read only records
},
scope: this
});
should be:
Rally.data.BulkRecordUpdater.updateRecords({
records: [record1, record2],
propertiesToUpdate: {
Parent: '/hierarchicalrequirement/123.js'
},
success: function(readOnlyRecords){
//all updates finished, except for given read only records
},
scope: this
});
Secondly, when trying to use this method to update records I keep getting an error for being unable to call the method 'get' - I am assuming that this is because the records I am providing are not in the correct format. I am simply calling this on records I pull from a wsapi query. I have tried putting the object inside of another object:
{data: record}
but it still does not seem to help. Any ideas would be greatly appreciated!
Thanks for pointing out the doc issue. I'm filing a defect to fix this.
The records should be instances of Rally.domain.WsapiModel. If you are using Rally.data.WsapiDataStore to retrieve them then you should be all set. Are you doing something different?
For what it's worth, we're also currently working on another, better way to do batch updates of records. Look for it in a future SDK!
See this example if it helps
_justFunction: function(_childObj) {
records = ["abc", "xyz"];
var store = Ext.create('Rally.data.custom.Store', {
data: records,
listeners: {
load: that._updateAll,
scope: that
},
});
},
_updateAll: function(store,data) {
Rally.data.BulkRecordUpdater.updateRecords({
records: data,
propertiesToUpdate: {
Parent: _newParent.get("_ref")
},
success: function(readOnlyRecords){
//all updates finished, except for given read only records
},
scope: that
});
},