I have already referred to this: create/update user story using rally app sdk
Here's my code:
_update_iteration_of_parent: function(pOID, iteration){ //iteration is the OID of iteration to be added.
console.log("Updating Iteration ",'/iteration/'+iteration);
var me = this;
Rally.data.ModelFactory.getModel({
type: 'User Story',
success: function (model){
var that = this;
//console.log("objectid #",objectid," latestpsi ",latestpsi);
this.model = model;
var id = pOID;
console.log("_readRecord ",id);
this.model.load(id,{
fetch: ['Name','Iteration'],
callback: function (record, operation){
//console.log('name .. ', record.get('Name'));
if(operation.wasSuccessful()){
console.log('Iteration ',record.get('Iteration'));
record.set('Iteration','/iteration/'+iteration);
record.save({
callback: function(record,operation){
if(operation.wasSuccessful()){
console.log("Operation Successful");
}
else
console.log("Not");
},
scope: this,
});
}
},
scope: this
});
}
});
}
I am not able to update the iteration, and it always logs "Not" indicating it was not a success when record.set('Iteration','/iteration/'+iteration) is called. There is no problem in getting the values for pOID and iteration.
Your method is called _update_iteration_of_parent which indicates that you are trying to set Iteration on a parent story. If this is true, that explains the failure to set iteration. It is not possible to schedule a parent (epic) story for Iteration in UI or API.
Related
I am implementing a "copy on write" CRUD system meaning i never overwrite a database entry but mark as inactive and write a new record. When editing an existing record this means i write to the old record deactivating then create a new record. My controller code is below:
$scope.save = function() {
if(!$scope.newDevice){
var editDevice = $scope.device;
$scope.delete(editDevice);
$scope.device = {name: editDevice.name, type: editDevice.type, hash: editDevice.hash};
}
var newDevice = new DeviceService($scope.device);
newDevice = newDevice.$save(function(newDevice, putResponseHeaders) {
DeviceService.query({active : true}, function(devices){
$scope.devices = devices;
});
});
};
When i call to get the list of active devices with DeviceService.query({active : true} I still get the old record as active since it executes and returns before the delete method has been processed and returned.
I think i should be using promise maybe. How do i write this code to be better and work?
thanks
Yes, you want to use promises. You have two options:
Use the success/failure callbacks that all $resource methods supply. Note you're using this when you call $save. You could do the same when you call $delete on the resource, so that your remaining code only executes when the $delete() succeeds. These callbacks are automatically invoked when the $resource's built-in promise is resolved or rejected.
Make your $scope.delete() function return a promise. It sounds like this might be better, because you do not always want to make the delete request.
The code for #2 might look like this:
// this function use the '$q' service, which you need to inject
// in your controller
$scope.delete = function(item) {
var deferred = $q.defer();
item.$delete({},
function(response) {
// the delete succeeded, resolve the promise
deferred.resolve(response);
},
function(error) {
// failed, reject the promise
deferred.reject(error);
}
);
return deferred.promise;
}
$scope.save = function() {
if(!$scope.newDevice){
var editDevice = $scope.device;
$scope.delete(editDevice).then(function(response) {
$scope.device = {name: editDevice.name, type: editDevice.type, hash: editDevice.hash};
// now trigger the code to save the new device (or whatever)
$scope.doTheActualSave();
},
function(error) { });
} else {
// there was nothing to delete, just trigger the code to save
$scope.doTheActualSave();
}
};
$scope.doTheActualSave = function() {
var newDevice = new DeviceService($scope.device);
newDevice = newDevice.$save(function(newDevice, putResponseHeaders) {
DeviceService.query({active : true}, function(devices){
$scope.devices = devices;
});
});
}
I've made view to listen to model changes. When there is change in model render function will be called and alert window will be prompted. But it is coming twice that means render function is calling twice because of two change events.
WineDetails View
app.WineView = Backbone.View.extend({
template:_.template($('#tpl-wine-details').html()),
initialize:function () {
this.model.bind("change", this.render, this);
},
render:function (eventName) {
if(eventName)alert("changed")
$(this.el).html(this.template(this.model.toJSON()));
return this;
},
events:{
"change input":"change",
"click .save":"saveWine",
"click .delete":"deleteWine"
},
change:function (event) {
var target = event.target;
console.log('changing ' + target.id + ' from: ' + target.defaultValue + ' to: ' + target.value);
// You could change your model on the spot, like this:
// var change = {};
// change[target.name] = target.value;
// this.model.set(change);
},
saveWine:function () {
this.model.set({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
});
if (this.model.isNew()) {
var self = this;
app.router.wineList.create(this.model,{wait:true,success:function(){
app.router.navigate('wines/'+self.model.id,false);
}});//add event,request event on collection will be triggered
} else {
this.model.save();//change event,request event on model will be triggered
}
return false;
},
onClose:function()
{
alert("onclose");
this.model.unbind("change",this.render);
}
And its not because of zombie view because i've this following code
Backbone.View.prototype.close=function()
{
alert("closing view "+this);
if(this.beforeClose){
this.beforeClose();
}
this.remove();
this.unbind();
if(this.onClose){
this.onClose();
}
}
please tell me what is wrong in this code. Thank u :)
So, as you didn't provide the information regarding your Model#save call, I'll assume it's the one within your view. I'll also assume the problem doesn't come from zombie views because you're following an outdated method for that. I'll make a guess here about what's probably happening:
this.model.set({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
});
// ...
this.model.save();
Ok, the first part (the set method) will trigger a first change event.
The second part, the save method may trigger another change. Another set will indeed be done with the attributes sent back from the server.
Possible solution to a possible problem:
save can be passed attributes, and a wait flag to postpone the use of the set method until the server responds:
this.model.save({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
}, {wait: true});
You can also try it by creating always a new instance of your model like :
var wine = new WineModel({
name:$('#name').val(),
grapes:$('#grapes').val(),
country:$('#country').val(),
region:$('#region').val(),
year:$('#year').val(),
description:$('#description').val()
});
And then save it like :
wine.save(null, success: function(model){
// do your call action on call back
},
beforeSend: function() {
// before save
}
error: function(model, errors) {
// on error occurred
});
I am exploring the BBCloneMail demo application for MarionetteJS, but I am not seeing how the events are triggering the rendering actions. I saw some global 'show' event here:
https://github.com/marionettejs/bbclonemail/blob/master/public/javascripts/bbclonemail/components/appController.js#L25
show: function(){
this._showAppSelector("mail");
Marionette.triggerMethod.call(this, "show");
},
But I don't see, where/how the Marionette.triggerMethod results into rendering the Mail component. I was trying to call the triggerMethod for my case, but I get a 'cannot call apply for undefined'. Why is the call above working for the BBcloneMail application.
The Application controller for my case:
MA.AppController = Marionette.Controller.extend({
initialize: function(){
_.bindAll(this, "_showGenres");
},
show: function() {
if (MA.currentUser) {
MA.navbar.show(new MA.Views.Items.LogoutNavbar({model: MA.currentUser}));
}
else
{
MA.navbar.show(new MA.Views.Items.LoginNavbar());
}
this._showGenres();
},
_showGenres: function() {
var categoryNav = new MA.Navigation.Filter({
region: MA.filter
});
this.listenTo(categoryNav, "genre:selected", this._categorySelected);
categoryNav.show();
MA.main.show(MA.composites.movies);
},
showMovieByGenre: function(genre){
var movies = new MA.Controllers.MoviesLib();
that = this;
$.when(movies.getByCategory(genre)).then(that._showMovieList);
Backbone.history.navigate("#movies/genres/" + genre);
},
_showMovieList: function(movieList){
var moviesLib = new MA.Controllers.MoviesLib({
region: MA.main,
movies: movieList
});
Marionette.triggerMethod.call(this, "show");
}
});
I init the application controller in a init.js with:
app = new MA.AppController();
Looking at the source for triggerMethod, this is a way of both triggering an event (the string being passed in), and additionally (if it exists) running a method on the object that has an 'on' prefix.
In your case the error relates to line 560, specifically that there is no method apply on undefined. Based on the code its (in your case) trying to call the equivilent of this.trigger('show') - but AppController doesn't have a method called trigger.
In which case I'm guessing that in the BBCloneMail example this (being bassed into triggerMethod.call) is not actually the controller, but instead the view that is to be shown.
I defined the add handler on my collection:
var BookmarksCollection = Backbone.Collection.extend({
initialize: function() {
this.on('add', function(obj) {
console.log(obj.get('title') + ' added');
});
},
url: '/bookmarks',
model: Bookmark
});
And it's being called from one of my views:
var AddView = Backbone.View.extend({
el: $('#entry'),
events: {
'click #submit': 'submitHandler'
},
submitHandler: function(event) {
if($('input').val()) {
var newBookmark = new Bookmark;
newBookmark.set({url: $('input').val()});
console.log('Save!');
newBookmark.save({
success: function(newBookmark) {
console.log('Success!');
myBookmarks.add(newBookmark);
myBookmarks.trigger('add', newBookmark);
}
});
}
}
});
However the add handler on my collection is never triggered. In fact, not even the success function in the view seems to be called.
But the newBookmark gets saved anyway, so no errors happening.
What am I missing?
NodeJS respond with 201 (Created) when a new bookmark is posted.
Maybe backbone expects a 200 (OK) to trigger success?
EDIT: I tried triggering the event from outside the success function and it works.
How can I tweak backbone to react to a 201 (Created)?
I think the issue is that he first argument of save should be the data you are trying to save. The second is the options which can include the success and error callbacks.
newBookmark.save({dataiwanttosave: "hi"}, {
success: function(newBookmark) {
console.log('Success!');
myBookmarks.add(newBookmark);
myBookmarks.trigger('add', newBookmark);
}
});
My ultimate goal is to append the JSON data to ul#tweets, each as individual hidden list items. They will then, one by one over time, become visible/shown on the screen, and then be removed from the ul#tweets list.
Once the number of hidden items drops below a certain amount, I want to re-append the JSON data. When this happens, I am not worried about duplicate items.
I tried to setup a test by creating a function with a timeout so that every 5 seconds it would append the JSON data to the list.
However, though my app loads the initial data on pageload fine, when I create a function to be run within $(document).ready({}) - it won't work.
I do know, however, that I can append the JSON data manually in the console after page load (same code as below without wrapping it in the function or the doc.ready).
Thanks for the help!
Function:
$(document).ready(function(){
updateTweets = function() {
newTweets = new Tweets();
newTweets.fetch();
newTweets.each( function(tweet) {
console.log('test'); // this doesn't work
view = new TweetView({ model:tweet });
$('#tweets').append(view.render().el);
});
setTimeout(updateTweets, 5000);
};
updateTweets();
});
Here is my Code
// MODEL
window.Tweet = Backbone.Model.extend({});
// COLLECTION
window.Tweets = Backbone.Collection.extend({ model: Tweet, url: '/tweets' });
// SET GLOBAL VARIABLE FOR NEW TWEETS COLLECTION
window.tweetList = new Tweets();
$(document).ready(function() {
// MODEL VIEW
window.TweetView = Backbone.View.extend({
tagName: 'li',
className: 'tweet',
initialize: function() {
_.bindAll(this, 'render');
this.model.bind('change', this.render);
this.template = _.template($('#tweet-template').html());
},
render: function(){
var renderedTweets = this.template(this.model.toJSON());
$(this.el).html(renderedTweets);
return this;
}
});
// COLLECTION VIEW
window.TweetListView = Backbone.View.extend({
template: _.template($('#tweet-list-template').html()),
initialize: function() {
_.bindAll(this, 'render');
this.collection.bind('reset', this.render);
},
render: function() {
var $tweets,
collection = this.collection;
$(this.el).html(this.template({}));
$tweets = this.$('#tweets');
collection.each(function(tweet){
var view = new TweetView({
model: tweet,
collection: collection
});
$tweets.append(view.render().el);
});
return this;
}
});
// ROUTER
window.TweetListDisplay = Backbone.Router.extend({
routes: {
'': 'home'
},
initialize: function(){
this.tweetListView = new TweetListView({
collection: window.tweetList
});
},
home: function() {
var $container = $('#container');
$container.empty();
$container.append(this.tweetListView.render().el);
},
});
// DECLARE AND START APP
window.app = new TweetListDisplay();
Backbone.history.start();
}); // close $(document).ready({});
You call fetch here
newTweets.fetch();
And then right after start processing the collection as if it has been populated, here
newTweets.each( function(tweet) {
console.log('test'); // this doesn't work
view = new TweetView({ model:tweet });
$('#tweets').append(view.render().el);
});
fetch is an ASYNCHRONOUS operation, which means that after you fire it, the rest of the program will continue to execute immediately after, regardless if the ajax-call launched by the fetch has returned or not. So when you start processing the collection, your fetch hasn't yet returned and the collection is still empty.
There are 2 ways you can correct this situation. Let's start by making a function processCollection that does to the collection exactly what you want:
var processCollection = function () {
newTweets.each( function(tweet) {
console.log('test'); // this doesn't work
view = new TweetView({ model:tweet });
$('#tweets').append(view.render().el);
});
};
1 The callback function (I don't like these)
newTweets.fetch(success: processCollection);
Now processCollection will be called right after the fetch has succeeded.
2 Bind to events (I prefer this)
newTweets.on('reset', processCollection);
newTweets.fetch();
When the fetch returns successfully, it will populate the collection and fire a reset -event. This is a good place to tie your processing event, because you know that now the collection is populated. Also I find that there is slightly less scoping problems with events than with callbacks.
Hope this helps!
You cant call;
newTweets.fetch();
And then immediately start processing the collection as if its ready to use.. it takes time.. the fetch call is asynchronous.. the reason it works in console is that it takes time to prep the output for console.. and the fetch does indeed finish..
You should provide a success callback for the fetch like this:
newTweets.fetch({success: function(){//process collection}});