6

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...

cschuff
  • 5,502
  • 7
  • 36
  • 52

3 Answers3

5

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

cschuff
  • 5,502
  • 7
  • 36
  • 52
  • 1
    Solution #2 does not work (at least not on my end), preventDefault() and cancelBubble() did nothing to stop the navigation. – zypA13510 Jan 24 '19 at 07:09
2

I just had to solve the same problem and I found that there is an API for this. However it only works when the app is running in Fiori Launchpad.

sap.ushell.Container.setDirtyFlag(bFlag);

Here bFlag is a boolean. If it's true the user will have to confirm navigating away. Set it to false to allow navigation without this message.

Navigation is interrupted whenever the hash changes so this includes

  • Routing within your app
  • Changing the hash in browser's address bar
  • Navigating within Fiori Launchpad
  • Navigating to another site

Source: Implementing Data Loss Protection

Lennart M.
  • 21
  • 3
0

Try "onBeforeUnload" event on the window object.

Andrew Naumovich
  • 1,441
  • 1
  • 12
  • 15