0

I'm trying to use the pushPage() function of the OnsenUI 2 (rc15 as of now). Alongside the OnsenUI, I'm using jQuery 3.

Here is my function that upon clicking certain elements should push a page:

$(".tile_handler").on("click", ".imalink", function () {
    var link = $(this).data().href;
    if(link != null){
        document.querySelector("#myNavigator").pushPage(link, { animation: "slide-ios" });
    }
})

When I push the page for the first time, it works fine. I use the iOS back button to return. Then I click it again and I get this error (and increasingly more as I repeat the process):

[index.js:450] Uncaught (in promise) pushPage is already running.

Here is another function that is supposed to load splitter pages:

$(".splitter_item").click(function () {
    var address = $(this).data('address');
    $('#content')[0].load(address).then(menu.close.bind($('#menu')[0]));
})

When I switch between two pages via Splitter it starts throwing this ( and more every time I switch between pages)

[undefined:1] Uncaught (in promise) Splitter side is locked.


What I assume is happening is that I load a page, leave it and when I access it again, it loads the page again. However that doesn't seem to be the behavior shown in OnsenUI examples such as this:

document.addEventListener('init', function(event) {
  var page = event.target;

  if (page.id === 'page1') {
    page.querySelector('#push-button').onclick = function() {
      document.querySelector('#myNavigator').pushPage('page2.html', {data: {title: 'Page 2'}});
    };
  } else if (page.id === 'page2') {
    page.querySelector('ons-toolbar .center').innerHTML = page.data.title;
  }
});

There is the popPage() function which should delete previously loaded page and would prevent this. But the references don't use it, so I assume I'm doing something wrong. But I don't know what.


UPDATE:

I managed to reproduce both problems in CodePen. Here is the Splitter error and here is the pushPage() one. It seems like the pushPage() one is a problem with my function as it adds a pushPage() request every time I click it, not sure why though.

Both errors only seem to happen in Ripple emulator (via VS2015). I don't seem to be able to reproduce them in Android Emulator (but the $(".tile_handler").on("click", ".imalink", function () { code fires incorreclty multiple times anyway). I'm testing this further.

rancor1223
  • 356
  • 2
  • 16
  • You can push the same page over and over again without issue. I did a quick codepen: https://codepen.io/anon/pen/NALzWZ that demonstrates that. The back button does execute the popPage() but as you can see, I push the same page2.html template to the navigation stack over and over without issue. Usually, when I get that error or see it, there is something else going on. – Munsterlander Aug 03 '16 at 02:54
  • Does this happen only on iOS, or in a browser too? Also does it happen if you use the`fade-ios` animation? – Ilia Yatchev Aug 03 '16 at 05:05
  • I'm little under-equiped, so I only have emulators to work with for now. This happens in Ripple Emulator on both Android and iOS. I added a CodePen reproducing the error. – rancor1223 Aug 03 '16 at 06:58

3 Answers3

2

Both errors which you mention are actually a safe measure from you pushing or doing some action twice by mistake if you double click a button/link for example.

They appear when you try to make an action while an animation is running. As you can see in the examples generally there is no problem to push a page twice or more, as long as you start the second push after the first one is finished.

Here are demos with your exact code for both the splitter and navigator.

So the error does not come from the code which you provided, its someplace elsewhere.

The only thing I can come up with is that the behaviour which you mentioned would be seen if for some reason the popPage method failed to finish correctly. Maybe if you provide us with your own codepen where the issue can be reproduced we could debug it further.

An alternative, though highly NOT recommended would be to force the state before you do your action. This however would not be solving the problem, rather only masking it. And of course as with all hacks - it may break in future versions.

myNavigator._isRunning = false;

UPDATE:

Here are the two updated pens which you gave in the comments: https://codepen.io/IliaSky/pen/YWOOkW?editors=1010 https://codepen.io/IliaSky/pen/QEVVGm?editors=1010

Basically you were adding the handlers on init event, which is fired whenever a page is added. so with your logic you are adding more and more handlers with each page. Just make sure you add them only once and you will be fine.

Add things like:

if (e.target.id == 'pagename') ...

or simply

$(document).on("init", '#dashboard_page', function(){ ... }
Ilia Yatchev
  • 1,254
  • 7
  • 9
  • Ah, that helps. It seems it's my function that keeps adding on more and more request to `pushPage()` every time I press the tile. I have no idea why though. And I don't think it explains the Splitter bahavior I'm seeing as I use `load()` there (I will try to replicate that in a CodePen too). – rancor1223 Aug 03 '16 at 07:08
  • CodePen for the Splitter added. – rancor1223 Aug 03 '16 at 07:15
  • ok I know the reason - I will update my answer shortly. basically you're adding more and more event handlers which each page you add. So it's just that you're calling `load` and `pushPage` an increasing amount of times. :) – Ilia Yatchev Aug 03 '16 at 07:52
  • Done - feel free to check out the codepens in the update. Also as a general rule you should be careful about these things not only with onsen but with any javascript code. It's a common issue, but a little caution can help a lot in these situations. – Ilia Yatchev Aug 03 '16 at 08:07
  • That did the trick, but I can't say I understand very well what's going on. Are the listeners somehow persistent? My line of thought was to have the listener on every page since every page will have a hamburger button (for example). Therefore I omitted the page specification and had it run on every `init`. Do you happen to know of any article/book that wuld explain this behavior? Thanks for the help though! – rancor1223 Aug 03 '16 at 08:21
  • Well unfortunately most of the links I was able to find are focused on a little more advanced level. I guess [this](https://www.smashingmagazine.com/2013/11/an-introduction-to-dom-events/) is a more basic tutorial. Though I think I can also just explain the problem myself. Since discussing it in the comments is not nice I will add another answer explaining the situation better. – Ilia Yatchev Aug 03 '16 at 09:37
2

Basically each time you push a page, that page fires an init event. However Onsen still keeps the initial page in the dom.

Navigator example (the same logic applies to the splitter one):

<ons-navigator>
  <ons-page id="dashboard">
    <div class="imalink" data-href="request_list.html"></div>
  </ons-page>
</ons-navigator>

You have an init event for dashboard. Then you click the tile and go to another page. Then request_list fires its own init event. However our initial page is still in the dom.

<ons-navigator>
  <ons-page id="dashboard" style="display: none">
    <div class="imalink" data-href="request_list.html"></div>
  </ons-page>
  <ons-page id="request_list">
    ...
  </ons-page>
</ons-navigator>

You have something like this. However the following is called a second time:

$(".tile_handler").on("click", ".imalink", function () {
  ...
})

Which adds the listeners once more. $el.on("click") like an alias of addEventListener, meaning you are adding more and more listeners.

So whenever you navigate you keep adding them because the initial page was never removed from the dom.

Alternative solutions:

  1. Use only the current page (e.target)

    $('selector') // instead of this   
    $(e.target).find('selector')  // use this
    

    That way you limit to only finding elements within the page which you just created.

  2. Enable the handlers from the start. Since you are using jQuery there is actually a simpler way to do the things without relying on the init event.

    Just do this outside of any init handler:

    $(document).on("click", ".tile_handler .imalink", function () { ... })
    

    This actually means that the handler is attached to the document itself and the handler will be called only when the target is .tile_handler .imalink - so it works with any future imalinks which you create.

    This may not be the most efficient approach, but is definitely one of the simplest.

Ilia Yatchev
  • 1,254
  • 7
  • 9
0

Take a look at .one() from jQuery - the event handler will only execute once per element in order to prevent the error: Uncaught (in promise) pushPage is already running

$(".tile_handler").one("click", ".imalink", function () {
var link = $(this).data().href;
if(link != null){
    document.querySelector("#myNavigator").pushPage(link, { animation: "slide-ios" });
}})
mboeckle
  • 938
  • 13
  • 29