Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent user from navigating away?

In my SAPUI5 application I'm using a sap.tnt.Toolpage as the main layout container. The SideNavigation shows a list of application links and the mainContents is dynamically loaded using the sap.ui.core.routing.Router.

To navigate to a detail page I'm using

var router = sap.ui.core.UIComponent.getRouterFor(this);
router.navTo("MyDetailRoute", {
    detailItemId: oItem.getBindingContext("oDataSourceName").getProperty("DETAILID")
});

This loads a detail view which then binds it's controls to the data from my data source.

The problem arises when I try to remember the user to save his changes back to the server. I basically want to interrup the navigation to aks the user if he'd like to a) Cancel his changes, b) Save his changes or c) Stay on the detail page. While a) and b) are easy to implement, I don't know how to prevent the navigation from happening.

I've tried so far by subscribing to onBeforeHide of my view:

var view = this.getView();
var that = this;
view.addEventDelegate({
    onBeforeHide: function (evt) {
        var oModel = that.getModel("oDataSourceName");
        if (oModel.hasPendingChanges()) {
            // How to prevent leaving the page?
        }
    }
});

Things I've tried that are not working:

  1. evt.preventDefault();
  2. evt.stopPropagation();
  3. throw 'something';

Option 3) actually prevents the navigation but leads to later problems because the URL will change nevertheless...

Does anyone have an idea?

Edit: Thanks to the answer from cschuff I was able to get it working. For anyone who has the same problem in the future:

// In onInit: Subscribe to onBeforeHide in order to un-register event handler for hashchange

onInit: function () {        
    this.getRouter().getRoute("MyDetailRoute").attachPatternMatched(this.onNavigated, this);

    var view = this.getView();
    var that = this;

    view.addEventDelegate({
        onBeforeHide: function (evt) {
            $(window).off('hashchange');
        }
    });
}


// Then in onNavigate stoppen the router, saving the current window location and subscribing to hashchange (only once)
// In hashchange use helper variable m_bSelfURLSetting to prevent the function from running when triggered by URL restore (above)
// In the handler checking for pending changes and either resore the old URL or starting the router again if the user wants to quit or if there are no changes

onNavigated: function (oEvent) {            

    this.getRouter().stop();
    this.m_strHref = window.location.href;
    var that = this;

    $(window).off('hashchange').on('hashchange', function (e) {
        if (that.m_bSelfURLSetting) {
            that.m_bSelfURLSetting = false;
            return;
        }

        var oModel = that.getModel("oDataSourceName");
        if (oModel.hasPendingChanges()) {
            var leave = confirm('There are unsaved changes.\r\n\r\nDo you really want to leave the page?');
            if (!leave) {
                // re-set the URL
                that.m_bSelfURLSetting = true;
                window.location.href = that.m_strHref;
            }
            else {
                that.getRouter().initialize(false); 
                oModel.resetChanges();
            }
        } else {
            that.getRouter().initialize(false);
        }
    });
}

This is definitively not the kind of code one is proud of...

like image 526
Alexander Bartz Avatar asked Nov 15 '17 09:11

Alexander Bartz


People also ask

How do I stop navigate from a page?

To prevent a webpage from navigating away using JavaScript, we can set the window. onbeforeunload property to a function that returns any value. window. onbeforeunload = () => { return ""; };

What is window Onbeforeunload?

The onbeforeunload event occurs when the document is about to be unloaded. This event allows you to display a message in a confirmation dialog box to inform the user whether he/she wants to stay or leave the current page. The default message that appears in the confirmation box, is different in different browsers.


1 Answers

Solution #1: You could temporarily deactivate routing completely by using the Router methods stop and initialize. This will stop listening to changes in the url hash and thereby ignore all routing triggers.

From your controller:

var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.stop();

// later
// passing true will interpret and navigate to the current hash (since 1.48.0)
oRouter.initialize(true); 

Solution #2: Interrupt routing itself using sap.ui.core.routing.HashChanger events. hashChanged and hashReplaced are internally used by Router. You could also try if sap.ui.core.routing.Router routeMatched or beforeRouteMatched are cancelable (Docs are not giving any hint).

Solution #3: A less restrictive solution would be to just interrupt all actions that trigger routing from that particular view. Be aware that browser-back or changing the url-hash manually will still work here (the native JS events beforeunload and hashchange might help here).

BR Chris

like image 127
cschuff Avatar answered Dec 06 '22 04:12

cschuff