CCSequence like CCSpawn use functions from different classes - cocos2d-iphone

I try to use a few functions in my GameLayer. First - from another class, second - from GameLayer class, but CCSequence run like CCSpawn, not sequence. Separately they both work perfect in GameLayer.
in GameLayer
[self runAction:[CCSequence actions:
[CCCallFuncN actionWithTarget:self.rolypoly selector:#selector(jumpToDeath:)],
[CCCallFuncND actionWithTarget:self selector:#selector(goToGameOverLayer:tagName:)data:(int)TagGameOverLose],
nil]];
in rolypoly class
-(void)jumpToDeath:(id)sender
{
[self.sprite stopAllActions];
id actionSpaw = [CCSpawn actions:
[CCMoveTo actionWithDuration:0.5f position:ccp(self.sprite.position.x, self.sprite.position.y+self.sprite.contentSize.height)],
[CCBlink actionWithDuration:1.0f blinks:4],
nil];
[self.sprite runAction:[CCSequence actions:
[CCCallFuncND actionWithTarget:self selector:#selector(setJumpingToDeath:withValue:)data:(void*)1],
actionSpaw,
[CCHide action],
[CCCallFunc actionWithTarget:self selector:#selector(moveSpriteDeath)],
nil]];
}

The problems is that the runAction is not a blocking method. Meaning that when you use a function call action (like CCCallFunc) the action after it in the sequence will be executed when the function call returns. In your case jumpToDeath runs actions but does not wait for them to be finished and when it returns the second action in your main sequence is executed (before the sequence inside jumpToDeath is finished)
Try to rearrange your actions. If you need more help let me know.
EDIT : My suggestion :
[self runAction:[CCSequence actions:
[CCCallFuncN actionWithTarget:self.rolypoly selector:#selector(jumpToDeath:)], nil]];
-(void)jumpToDeath:(id)sender
{
[self.sprite stopAllActions];
id actionSpaw = [CCSpawn actions:
[CCMoveTo actionWithDuration:0.5f position:ccp(self.sprite.position.x, self.sprite.position.y+self.sprite.contentSize.height)],
[CCBlink actionWithDuration:1.0f blinks:4],
nil];
[self.sprite runAction:[CCSequence actions:
[CCCallFuncND actionWithTarget:self selector:#selector(setJumpingToDeath:withValue:)data:(void*)1],
actionSpaw,
[CCHide action],
[CCCallFunc actionWithTarget:self selector:#selector(moveSpriteDeath)],
[CCCallFuncND actionWithTarget:self selector:#selector(goToGameOverLayer:tagName:)data:(int)TagGameOverLose],
nil]];
}
As you can see I moved the last call function action to be the last in the jumpToDeath method which insures it will be executed last after all other actions are done.
I do not know what is the implementation of moveSpriteDeath but if it does not contain actions than it will be ok, otherwise show its implementation or try doing the same

Related

How to send an action from google maps click event listener?

I have multiple pins rendering in google maps. When user clicks in any of the pins I want to be able to trigger the _activeRoute action. For some reason when I do the below nothing happens.
action:{
_activeRoute:fuction(){
console.log('hello world');
},
google.maps.event.addListener(marker, 'click', function(mapService) {
this.sendAction('_activeRoute');
});
}
this inside function callback isn't pointing to component or other Ember object instance. You have to use another variable to call method sendAction of correct object:
let that = this;
google.maps.event.addListener(marker, 'click', function(mapService) {
that.sendAction('_activeRoute');
});

Reject a transition based on user input, without confirm()

EmberJS: reject an upcoming transition
How would I reject an upcoming transition via willTransition, but using a custom pop up or jQuery .dialog box such as this:
willTransition: function(transition) {
//Some conditional logic would go here to determine if they need to be prompted to save
$("#confirmTransition").dialog({
resizable: false,
modal: true,
draggable: false,
buttons: {
"Yes": function() {
self.transitionTo(transition.targetName);
$(this).dialog( "close" );
},
"No": function() {
transition.abort();
$(this).dialog( "close" );
}
}
});
}
Rather than using confirm() which would actually wait until the user enters something. I can use a .then, but it fires after the transition completes. Would I be better off using beforeModel on all routes to handle a conditional abort, and then rely on willTransition to provide the popup to confirm?
Updated per accepted question:
willTransition: function(transition) {
//Some conditional logic would go here to determine if they need to be prompted to save
var self = this;
if(!self._transitioning){
$("#confirmTransition").dialog({
resizable: false,
modal: true,
draggable: false,
buttons: {
"Yes": function() {
self._transitioning = true;
transition.retry().then(self._transitioning = false);
$(this).dialog( "close" );
},
"No": function() {
transition.abort();
$(this).dialog( "close" );
}
}
});
}
}
You could use the same approach to animate route transitions: You can take a look at the relatebase blog (as well as the jsbin example referred by the blog).
Essentially you handle a little state machine in the willTransition action: You abort the original transition and as soon as the user closes the dialog you retry the original transition.
Hope it helps!

ember.js - call base action from overriden action

I want to call a base action from an overriden action like in this jsbin.
I know if I return true then it will propagate to the action, the problem is that I want the action to execute and then do something in the overriden action. Is this possible?
As #fanta noted you aren't exactly overriding the action because you'd have to extend from your parent route, but aside from that:
If you return true from the action handler the action will bubble up to any parent routes. Simple as that you can specify to call the parent's route action handler or just let the action die in your handler
App.IndexRoute = Ember.Route.extend({
actions:{
doSomething: function(){
//do things
if (something) {
//I got this, no need for my parent
return true;
} else if (otherThing) {
//I can't do this, let my parent route handle it
return false;
}
return defaultOption;
}
}
});
I can't think of a scenario in which using action bubbling you couldn't (implicitly) call your parent's route action handler. Do you?
EDIT
If you were to wait for a promise in the remove method of the object you want to remove you can use something like this:
App.IndexRoute = Ember.Route.extend({
actions:{
removeConfirmed: function(){
// do some handling
console.log("I may transitionTo");
self.transitionToRoute('otherRoute', 'someParam');
}
}
});
App.IndexController = Ember.Controller.extend({
actions:{
remove: function(){
var self = this;
this.get('content').remove().then(function() {
// you could transition from the controller
self.transitionToRoute('otherRoute', 'someParam');
// or if you need some handling in the route
self.send('removeConfirmed');
});
// you could let the action remove action bubble up
return true;
}
}
});
If there's no need to wait for a promise then I don't see why returning true and letting the action bubble wouldn't work, since the action in the route would be called.
The important thing to note is that the controller can send actions to itself. If they are not handled by controller itself the action will bubble up to its route and any subsequent parent routes
I hope this helps you!

Binded variable is not synced in time

How is possible, that binding have latency?
App.SomeRoute = Ember.Route.extend
actions:
someAction: ->
console.log #controllerFor('some').get('foo')
Ember.run.later (=> console.log #controllerFor('some').get('foo')), 10
App.SomeController = Ember.ObjectController.extend()
App.OtherController = Ember.ObjectController.extend
needs: ['some']
fooBinding: 'controllers.some.foo'
action:
changeIt: ->
#set('foo', 'bar')
#send('someAction')
If action changeIt is evoked (from view for example) console output will be undefined, but after a little delay it will be bar.
What I am doing wrong?
This is the expected behavior, when you change a bound property, the syncronization isn't performed immediatelly, it's just scheduled and called later.
This is important because if we have a fooBinding: 'controllers.some.foo'. Doesn't matter how many times the foo is changed, the controllers.some.foo property just need to be updated once with the final result. With this, we avoid to trigger uneeded observers, and save processing.
You can force the scheduled syncronization queue to flush using Ember.run.sync(), with the following code:
App.SomeRoute = Ember.Route.extend
actions:
someAction: ->
Ember.run.sync()
# now you can see the updated value
console.log #controllerFor('some').get('foo')
App.SomeController = Ember.ObjectController.extend()
App.OtherController = Ember.ObjectController.extend
needs: ['some']
fooBinding: 'controllers.some.foo'
action:
changeIt: ->
#set('foo', 'bar')
#send('someAction')
Keep in mind that this is just an example, for you see the updated value. The use of Ember.run.sync() isn't recommended.

Why can't this Emberjs controller set a key

Below is a reduced Ember controller. jQuery is used to make a remote call, and action needs to be taken in the controller in the callback.
In the callback, this refers to the GenericController correctly, I can read a value with this.get('someKey') but this.set('someKey', '') will not set the value. This works when the this.transitionTo method is removed. Any help on how to get the set to work with a transition present would be super helpful.
App.GenericController = Ember.Controller.extend({
someAction: function() {
var jqxhr = jQuery.getJSON(this._endpoint, {someKey: this.get('someKey')});
jqxhr.done(this._someActionComplete.bind(this));
},
_endpoint: '/some/generic.json',
_someActionComplete: function(json, textStatus, jqxhr) {
this.set('someKey', '');
this.transitionToRoute('reference', reference);
}
});
It' all about async.
When you invoke your method this correctly references the controller as you already noticed, this is why you can use this.get('someValue'), but by the time the ayncronous call returns (wenn done is invoked for example) this no longer references your controller anymore but the object which invoked the done function, so you have to safe the correct reference to this before you make the request, this way you can use it to pass it to the bind function:
App.GenericController = Ember.Controller.extend({
someAction: function() {
var _this = this;
var jqxhr = jQuery.getJSON(this._endpoint, {someKey: this.get('someKey')});
jqxhr.done(_this._someActionComplete(_this));
},
_endpoint: '/some/generic.json',
_someActionComplete: function(json, textStatus, jqxhr) {
this.set('someKey', '');
}
});
Update in response to your last comment
I don't know what you are doing differently, but take a look at this simple Demo that it works as expected. Click the button "Some action" to execute the request.
Hope it helps.
Because this in jqxhr.done does not reference the GenericController, it references the object that invoked done. This is how I solve it:
App.GenericController = Ember.Controller.extend({
someAction: function() {
var self = this;
var jqxhr = jQuery.getJSON(this._endpoint, {someKey: this.get('someKey')});
jqxhr.done(self._someActionComplete.bind(self));
}
}
Now you still have a reference to this you can use to call other functions, including set, on the controller.