scrollmagic how to off 'update' event when fire 'progress' event - scrollmagic

now I want to listen somewhere out of 'progress' event.(between red line and blue line)scene
I know that 'update' event can listen it, but the 'progress' event will also listen it.
.on('update', function (e) {
console.log('=== update 1111');
})
.on('progress', function (e) {
console.log('progress === 2222');
scene2.off('update', function(){
console.log('===================');
});
})
I try to use 'off' func,but it makes no sense.
Thanks!

Related

SocketIO duplicate events with AngularJS

I've ran into issues in the past wherein SocketIO will emit duplicate events when using AngularJS. There are two common reasons as to why this occurs:
Creating your event listeners inside the connect event listener.
Event listeners being dynamically created multiple times (usual case with AngularJS).
Luckily, there are two common solutions that match with the above scenarios. Please see the accepted answer to understand these solutions.
Creating your event listeners inside the connect event listener
This bug occurs if you create listeners inside your connect event listener. Simply move these listeners outside the connect event listener.
Before:
socket.on('connection', _ => {
console.log('Connected');
socket.on('someOtherEvent', data => {
console.log(data);
});
});
After:
socket.on('connection', _ => {
console.log('Connected');
});
socket.on('someOtherEvent', data => {
console.log(data);
});
Event listeners being dynamically created multiple times (usual case with AngularJS).
This is a common problem if you register your event listeners inside a controller. To fix this, edit your service like so:
angular.module('socketIO', []).factory('socket', $rootScope => {
class SocketHandler {
constructor(url) {
this._socket = false;
this._url = url;
this._events = {};
this._build();
}
_build() {
// Change for your configuration options below.
this._socket = io.connect(this._URL);
}
on(event, callback) {
if(this._events.hasOwnProperty(event))
return this._events[event];
return this._events[event] = this._socket.on(event, (...args) => {
$rootScope.$apply(_ => {
callback.apply(this._socket, args);
});
});
}
emit(event, data, callback = false) {
return this._socket.emit(event, data, (...args) => {
if(!callback)
return;
$rootScope.$apply(_ => {
callback.apply(this._socket, args);
});
});
}
}
return { build: SocketHandler };
});
You can then use this in your constructor:
angular.module('someController', socketIO => {
const socket = socketIO.build('your socket url');
});
The reason this fix works is because when you switch pages in AngularJS (with the default router) it recreates an instance of the controller. This means that multiple event listeners are now bound to a single event. We prevent this from occurring by caching our event listeners in SocketHandler._events, and returning the cached listener if we have it cached.

$scope.$on('$stateChangeStart') and $modal dialog

I have an AngularJs application that is detecting the change of the state (using ui.router) to present the user with the option to save unsaved changes. Now I'm doing this with a confirm dialog:
$scope.$on('$stateChangeStart', () => {
if (self.changed && confirm('There are unsaved changes. Do you want to save them?'))
this.save();
});
I wanted to change it to using the $modal dialog from the bootstrap ui library. The problem I have is that as the $modal.open() call returns inmediatelly (being asynchronous) the state changes before opening the dialog and it's never opened.
$scope.$on('$stateChangeStart', () => {
if (self.changed)
this.$dialog.open({...}).result.then(()=>{
this.save();
});
});
Is there a way to overcome this problem or am I stuck to using the plain javascript confirm dialog?
This is how I solved the problem. In my application, I am using an AppCtrl (parent) to handle navigation, dirty state etc.
function AppCtrl($rootScope, events, modalDialog) {
var vm = this,
handlingUnsavedChanges = false;
function isDirty() {
return $rootScope.$broadcast(events.CAN_DEACTIVATE).defaultPrevented;
}
function onStateChangeStart(event, toState, toParams) {
if (handlingUnsavedChanges) {
// if the dirty state has already been checked then continue with the state change
return;
}
// check for dirty state
if (isDirty()) {
// cancel navigation
event.preventDefault();
modalDialog
.confirmNavigation()
.then(function () {
// ignore changes
handlingUnsavedChanges = true;
$state.go(toState.name, toParams);
});
}
// Else let the state change
}
$rootScope.$on('$stateChangeStart', onStateChangeStart);
}
<div ng-controller="AppCtrl as app">
<div ui-view />
</div>
Then you can add an event handler for CAN_DEACTIVATE event in your route controller to check for dirty state, for example
function UserDetailCtrl($scope, events) {
function isDirty() {
// Your logic, return a boolean
}
function canDeactivate(e) {
if (isDirty()) {
e.preventDefault();
}
}
$scope.$on(events.CAN_DEACTIVATE, canDeactivate);
}
You can do it perfectly fine with ui-router and settings in run section of your app.
The crucial part is basically watching the $rootScope.$on('$stateChangeStart') event.
I present the whole solution with the plunker: http://plnkr.co/edit/RRWvvy?p=preview The main part is in scipt.js, and it goes as follows:
routerApp.run(function($rootScope, $uibModal, $state) {
$rootScope.modal = false; // this has to be set so the modal is not loaded constantly, and skips the 1st application entrance
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams) {
if ($rootScope.modal) {
event.preventDefault(); //this stops the transition
$rootScope.modal = false;
var modalInstance = $uibModal.open({
templateUrl: 'modal.html'
});
modalInstance.result.then(function(selectedItem) {
console.log('changing state to:'+ toState.name);
$state.go(toState, {}, {reload:true});
}, function() {
console.log('going back to state:'+ fromState.name);
$state.go(fromState, {}, {reload:true});
});
} else {
$rootScope.modal = true;
}
});
});
You should listen the $locationChangeStart event and if you have unsaved changes then do event.prventDefault() and continue on with your logic for the confirmation dialog with ui-bootstrap modal. The thing is that the order of the events is slightly changed in the newer angular versions and the $stateChangeStart happens a little too late to do your logic there. If you get stuck I can provide you with working example. For now, here is sample code:
$scope.$on('$locationChangeStart', () => {
if (self.changed) {
event.preventDefault();
this.$dialog.open({...}).result.then(()=>{
this.save();
// your logic for the redirection if needed after the save
});
}
});
If you want to work with state change event , it would be better to apply the event on app and use $modal service. You can find a detailed explanation of this here
http://brewhouse.io/blog/2014/12/09/authentication-made-simple-in-single-page-angularjs-applications.html
However this gives an example that works for each state change whenever state changes but you can make it working for the single or specified states like this :
app.run(function ($rootScope) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
if(toState.name =="desiredState") {
event.preventDefault();
// perform your task here
}
});
});
Since I'm not very comfortable with using broadcast I used below approach.
I simply cancel current event (state change) before showing my JQuery Dialog. And if user selects 'yes', then i trigger $state.go, if selects 'cancel/no'- we don't need to do anything, we already cancelled event.
$rootScope.$on('$stateChangeStart',function (event, toState, toParams, fromState, fromParams, options) {
console.log('$stateChangeStart- fromState= '+fromState.name +'-toState= '+toState.name);
/*If user starts to change state, show him a confirmation dialog
* If user selects 'Yes' - continue with state change and go in pause mode for collection.
* If user selects 'No'- stop state change.
*/
if (/*some condition*/) {
/*stateChangeStart gets triggered again by $state.go initiated from our modal dialog-
hence avoid extra cycle of listening to stateChangeStart*/
if (service.stateChangeTriggeredByDialog) {
service.stateChangeTriggeredByDialog = false;
return;
}
if (fromParams.paramName !== toParams.paramName) {
event.preventDefault();
Dialog.confirm({
dialogClass: 'my-customDialog',
template: $translate.instant('STATE_CHANGE_MSG'),
resizable: false,
width: 550
}).then(function () {
console.log('User choose to continue state change');
service.stateChangeTriggeredByDialog = true;
$state.go(toState, toParams, { inherit: false });
}, function () {
console.log('User choose to stop state change');
});
}
}});

How to disable the automatic preventDefault and stopPropagate in Backbone.Marionette?

I know Marionette calls preventDefault and stopPropagation by default, however, this prevents a menu of mine from closing.
Is there a way to re-enable it?
got lucky with a google query.
Backbone.Marionette.CompositeView.extend({
triggers: {
"click .do-something": {
event: "something:do:it",
preventDefault: true, // this param is optional and will default to true
stopPropagation: false
}
}
});

Karma Unit: Testing keypress with escape button

I have such code inside directive :
$document.bind('keydown', function ($event) {
if ($event && $scope.visible && $event.which === escapeKey) {
$scope.toggle();
$scope.$apply();
}
});
I want to test if user click escape toggle will run. At moment I have such test:
it('should toggle window visibility to false when keypress escape', function () {
var doc,
$event;
$httpBackend.when(method, url)
.respond(template);
$event = {
event: 'keydown'
};
directive = createDirective();
$httpBackend.flush();
$isolateScope = directive.isolateScope();
$isolateScope.toggle();
$document.triggerHandler('keydown');
});
But how can I pass that certain key was pressed thought triggerHandler. Don't want to use any jQuery . Is there another way of testing this?
element.triggerHandler({type: 'keydown', which: escapeKey});

How to stop $broadcast events in AngularJS?

Is there a built in way to stop $broadcast events from going down the scope chain?
The event object passed by a $broadcast event does not have a stopPropagation method (as the docs on $rootScope mention.) However this merged pull request suggest that $broadcast events can have stopPropagation called on them.
Snippets from angularJS 1.1.2 source code:
$emit: function(name, args) {
// ....
event = {
name: name,
targetScope: scope,
stopPropagation: function() {
stopPropagation = true;
},
preventDefault: function() {
event.defaultPrevented = true;
},
defaultPrevented: false
},
// ....
}
$broadcast: function(name, args) {
// ...
event = {
name: name,
targetScope: target,
preventDefault: function() {
event.defaultPrevented = true;
},
defaultPrevented: false
},
// ...
}
As you can see event object in $broadcast not have "stopPropagation".
Instead of stopPropagation you can use preventDefault in order to mark event as "not need to handle this event". This not stop event propagation but this will tell the children scopes: "not need to handle this event"
Example: http://jsfiddle.net/C8EqT/1/
Since broadcast does not have the stopPropagation method,you need to use the defaultPrevented property and this will make sense in recursive directives.
Have a look at this plunker here:Plunkr
$scope.$on('test', function(event) {
if (!event.defaultPrevented) {
event.defaultPrevented = true;
console.log('Handle event here for the root node only.');
}
});
I implemented an event thief for this purpose:
.factory("stealEvent", [function () {
/**
* If event is already "default prevented", noop.
* If event isn't "default prevented", executes callback.
* If callback returns a truthy value or undefined,
* stops event propagation if possible, and flags event as "default prevented".
*/
return function (callback) {
return function (event) {
if (!event.defaultPrevented) {
var stopEvent = callback.apply(null, arguments);
if (typeof stopEvent === "undefined" || stopEvent) {
event.stopPropagation && event.stopPropagation();
event.preventDefault();
}
}
};
};
}]);
To use:
$scope.$on("AnyEvent", stealEvent(function (event, anyOtherParameter) {
if ($scope.keepEvent) {
// do some stuff with anyOtherParameter
return true; // steal event
} else {
return false; // let event available for other listeners
}
}));
$scope.$on("AnyOtherEvent", stealEvent(function (event, anyOtherParameter) {
// do some stuff with anyOtherParameter, event stolen by default
}));

Resources