Durandal Modals and navigation (i.e. back button) - durandal

This issue is easily seen in the durandal sample application:
1) Navigate to "Modal Dialogs" demo
2) Click the button to show the modal
3) Click the browser back button
When doing this, the page navigates to the previous screen while keeping the dialog open, which is almost never what you want.
Is there a best practice here for either closing dialogs or preventing navigation if one is open?

You can tie into the canDeactivate callback. Check if the modal is open and either prevent it or if they want to leave, close it

While I don't expect this answer to be immediately applicable to many out there, I wanted to throw it out there in case it helps someone.
We use typescript to create our viewmodels, and we have a base viewmodel to encapsulate common functionality (as well as expose the dataservice, pub/sub, etc). In this viewmodel this approach seems to work well:
public ShowDialog(viewModel: any) {
// create a subscription to the router so that we can forcibly close the dialog in the event of a routing call
var subscription = router.on('router:route:activating');
subscription.then(() => {
// we will hit this block if the router is activating while this dialog is open
var moduleId = viewModel.__moduleId__ || '[No Id]';
system.log('Navigation detected while dialog is open - the dialog will now be forcibly closed. (' + moduleId + ')', viewModel);
dialog.close(viewModel);
});
var promise = App.showDialog(viewModel);
promise.then(() => {
subscription.off(); // kill the router subscription since the dialog is closed at this point
});
return promise;
}
Consumers can use this function as a direct substitution to durandals' dialog to opt-in to the auto-close functionality.

Related

Prevent 'Leave site?' dialog for Google Forms inside iframe?

My Vue component uses Google Forms inside an iframe. The problem is, that when user tries to navigate to another page, she will get 'Leave site? Changes that you made may not be saved.' dialog. How can I disable this in my component?
EDIT: I noticed that this caused by the fact, that one field is prefilled on the google forms. Is there a way to bypass the dialog, if there a prefilled fields?
I am spitballing here but I believe that the internal page inside the iframe sets an event handler for the window.beforeunload event, if your page does not use this event, you could
window.onbeforeunload = function () {
return undefined;
};
I found one solution. Add v-if the the iframe:
<iframe v-if=!hideGoogleForm" ...>
Then to mounted():
window.onbeforeunload = () => {
this.hideGoogleForm = true;
};
The dialog is still shortly shown but then automatically closed when the component is destroyed.

Closing bootstrap vue modal doesn't unbind event

I have a bootstrap vue modal on a view page. When save is clicked, the save function emits an event. Works fine. When i close the modal and open it again then click on save, the save function is handled as expected emitting the function, however, it emits it twice (once for each time the modal was opened and closed. If i open and close the modal 5 times then click save, it calls the save function once but emits the function 5 times. I'm not sure how i can unbind the event when the modal closes using either typescript, vue, or bootstrap (any way other than jQuery :). Can anyone advise?
save() {
EventBus.$emit(MyEvents.RequestItemDetails);
}
// EventBus.ts
export const EventBus = new Vue();
export enum MyEvents{
RequestItemDetails = "request-item-details"
}
You've provided very little code for us to know what the problem actually is, but I'll take a guess.
If you're using a global event bus and you subscribe to an event on that bus from within a component, you need to make sure you unsubscribe from that event when the component is destroyed, otherwise your event handler function will be called multiple times because it gets registered multiple times on the bus.
For example:
import bus from './bus.js'
export default {
created() {
bus.$on('request-item-details', this.onRequestItemDetails)
},
destroyed() {
bus.$off('request-item-details', this.onRequestItemDetails)
},
methods: {
onRequestItemDetails() {
// Handle event
}
}
}
Your reply helped me find the solution. In my close method, all i needed to do was add "EventBus.$off('request-item-details')". That took care of it. Guilty of Overthinking again.
Thanks!

How to navigate to a different view that is not a child?

I am new to react native and new to iOS (not programming) so please excuse me if this question is a simple one. I am trying to navigate from one view to another (with a transition), however they are not related so I do not need the back navigation. I actually do not have a navigation bar at all. When using the Navigator component it seems to not support this at all. I am not sure if there is a separate way to do this but I am not able to figure it out without implementing my own hack.
If I use the navigator component and keep pushing on the views then it just keeps them all in memory and I do not want that. I can transition from one view to another and then pop but I may end up going to the wrong view in that case. I can also replace the view but it seems that does not allow for transitions.
To give you a scenario think of it like this:
Application starts and loads a "Loading" screen.
When initial loading is complete it will then go to the "Login" screen.
There is a button on the "Login" screen to "Register" or "Retrieve Password".
If they click "Register" it will take them there with a button back to "Login".
If they click "Retrieve Password" it will take them to a page with buttons to go back to "Login" or "Register".
So by this example you can see that there is no way to pop because if you were on the login screen and went to the register screen and then wanted to go the retrieve password screen then pop just simply wouldn't work. I do not want any navigation controls on the screen I just want to be able to do a smooth transition between these screens.
Now I was able to find a way to do this but I had to add a method to the Navigator class and hack code in using some of there core methods which seems like its not a good idea at all but here is the code (note this is really just a hack to see if it would work):
Navigator.prototype.pushWithUnmount = function(route) {
var activeLength = this.state.presentedIndex + 1;
var activeStack = this.state.routeStack.slice(0, activeLength);
var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
var nextStack = activeStack.concat([route]);
var destIndex = nextStack.length - 1;
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
this.props.configureScene(route),
]);
this._emitWillFocus(nextStack[destIndex]);
this.setState({
routeStack: nextStack,
sceneConfigStack: nextAnimationConfigStack,
}, () => {
this._enableScene(destIndex);
this._transitionTo(
destIndex,
null, // default velocity
null, // no spring jumping
() => {
this.replaceAtIndex(nextStack[destIndex], 0);
this.setState({
presentedIndex: 0,
});
}
);
});
}
By using the code provided above I am now able to do:
this.props.navigator.pushWithUnmount({ component: SomeComponent });
With this code the views are pushed onto the stack with a transition and the old views are unmounted when its finished.
Please tell me that I am doing something wrong and that there is a better way to do this?
The default router with React Native is pretty limited. I'd check out React Native Router Flux. We just switched to it a few weeks ago in our product and have really liked it. It does exactly what you want.

How to popup a modal whenever someone tries to navigate away from a particular page by clicking on navigation menu or any link within the page?

*I am using this code in my controller, inserting this controller dependency is breaking the whole code. *
$state.get('shop').onExit = function(){
modalCtrl.openModal(modalViewUrl,null);
//calling a controller which has the functions to open $modal
//handle modal submitHandler
}
1.this event will be invoked when user navigates away from the page:
$scope.$on('$locationChangeStart',function(event){
//action to be performed
event.preventDefault();
$('#modalName').modal();
});

WinJS NavBar event handler crashing Windows 8.1 HTML5/JS app when navigating to another page

I am developing an HTML/JS app for Windows 8.1 and am having trouble debugging a crash that is ocurring on within a handler attached to the top NavBar object when the user navigates from the page the handler is attached to.
The functionality is pretty simple: when the user lands on the screen in question, I am automatically displaying a WinJS Flyout using it's .show() method. Now, when the user invokes the top NavBar object, I have a handler that hides the Flyout object. I also have another handler that .shows() the Flyout when the NavBar is dismissed.
The problem occurs when the user navigates to another page. Here is my code for the screen in question:
var appBar = class.that.constructs.NavBar;
ready : function (element, options) {
var self = this;
...
appBar.topControl.onbeforeshow = self.hideFlyout;
appBar.topControl.onbeforehide = self.showFlyout;
$('#flyout').addClass('activated');
$('#flyout')[0].winControl._sticky = true;
$('#flyout')[0].winControl.show();
},
hideFlyout: function() {
$('#flyout').topControl.winControl.hide();
},
showFlyout: function() {
$('#flyout').topControl.winControl.show();
},
unload: function () {
appBar.topControl.onbeforeshow = null;
appBar.topControl.onaftershow = null;
}
As you can see, I am removing the event handlers upon unloading the page, but that doesn't seem to do the trick. I still get this crash error:
JavaScript runtime error: Unable to get property 'classList' of undefined or null reference
It crashes on the showFlyout handler. Does anybody have any suggestions as to how to avoid the crash upon navigating to a new page?
The show methos of the flyout wants an element as a mandatory parameter, it is the element it will be attacched to.
in your case you have to find an element and pass it to your function, for example:
var myButton= document.getElementById("myButton");
$('#flyout').topControl.winControl.show(myButton);
Check this page for a more extensive example: http://msdn.microsoft.com/en-us/library/windows/apps/br211726.aspx