From 8cfa78b416ec5c9dd143d174f2aa656fd3420eb1 Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Tue, 16 Aug 2016 00:46:37 +0800 Subject: [PATCH 1/2] OPTIONALLY better handling of routing when stop is called from a trigger (both triggersEnter and triggersExit). Original behaviour of FlowRouter is preserved. stop has to be called with one argument (with value true; defaults to false, which generates the original behaviour) With this tweak, triggersExit fires again after another exit attempt. Using stop(true) in triggersExit results in a duplicate entry in the user's history will be created in the "next" slot. This is arguably a small price to pay for triggersExit firing again (on routing) after a stop. Using stop(true) in triggersEnter results in a new entry in the user's history in the "next" slot for the route for which entry was stopped. --- README.md | 107 +++++++++++++++++++++++++++++++++++++++++++++ client/router.js | 34 +++++++++++++- client/triggers.js | 50 +++++++++++++++++++-- 3 files changed, 186 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dc5218a..364a279 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,113 @@ function localeCheck(context, redirect, stop) { > **Note**: When using the stop function, you should always pass the second **redirect** argument, even if you won't use it. +#### New Behaviour of Stop, and the Invocation Context of Trigger Functions + +This segment outlines new behavior added to `stop`. + +From the forgoing discussion, we learnt that that triggers are invoked with three arguments: + 1. `context`: information about the route (specifically, the output of `FlowRouter.current()`) + 2. `redirect`: a function that may be used for redirection to other routes + 3. `stop`: a function that aborts routing when invoked + +`stop` now takes a single argument called `goBackOnStop` which defaults to `false` (its original behavior in FlowRouter). When set to `true`: + - After using the `stop` function in `triggersExit` function, a duplicate history item (in the "next" slot) will be created. (Sorry...) But `triggersExit` triggers will fire again when attempting to route away once more. The process is as follows: + 1. `stop` is called + 2. a "re-entrant" exit (more below) is made from the "new route" which was prevented from completing + 3. a "re-entrant" entry (more below) is made into the "old route" from which stop was called + - After using the `stop` function in `triggersEnter` function, a history item (in the "next" slot) will be created for the route on which "enter" was "stopped". + 1. `stop` is called + 2. a "re-entrant" exit (more below) is made from the "new route" which was prevented from completing + 3. a "re-entrant" entry (more below) is made into the "old route" that was exited before stop was called if it exists (i.e.: the failed entry is not into the first history item) + +However, to achieve that, the router will "exit" the routes whose routing attempts didn't successfully complete after being aborted with `stop`. It will then enter the original source route. This information is made available to all trigger functions in their invocation contexts. + +Additional information is available to entry/exit triggers in the form of their invocation context (i.e.: `this`). + +In particular, for entry triggers `this` takes the form: +```javascript +{ + type: "enter", + route: /* new route */, + newRoute: /* new route */, + oldRoute: /* previous route */, + router: /* essentially FlowRouter */, + stopped: /* whether stop has been called */, + isReentrant_followingStoppedExit: /* a boolean that indicates whether this + entry is a result of route entry/exit + after an exit is stopped using the stop + function (third argument of a trigger) + + what happens is: + (1) stop is called + (2) a "re-entrant" exit is made from the + "new route" which was prevented from + completing + (3) a "re-entrant" entry is made into + the "old route" from which stop was + called + */, + isReentrant_followingStoppedEnter:/* a boolean that indicates whether this + entry is a result of route entry/exit + after an enter is stopped using the stop + function (third argument of a trigger) + + what happens is: + (1) stop is called + (2) a "re-entrant" exit is made from the + "new route" which was prevented from + completing + (3) a "re-entrant" entry is made into + the "old route" that was exited + before stop was called if it exists + (i.e.: the failed entry is not into + the first history item) + */ +} +``` + +In particular, and for exit triggers `this` takes the form: +```javascript +{ + type: "exit", + route: /* current, soon to be former, route */, + oldRoute: /* current, soon to be former, route */, + router: /* essentially FlowRouter */, + stopped: /* whether stop has been called */ + isReentrant_followingStoppedExit: /* a boolean that indicates whether this + entry is a result of route entry/exit + after an exit is stopped using the stop + function (third argument of a trigger) + + what happens is: + (1) stop is called + (2) a "re-entrant" exit is made from the + "new route" which was prevented from + completing + (3) a "re-entrant" entry is made into + the "old route" from which stop was + called + */, + isReentrant_followingStoppedEnter:/* a boolean that indicates whether this + entry is a result of route entry/exit + after an enter is stopped using the stop + function (third argument of a trigger) + + what happens is: + (1) stop is called + (2) a "re-entrant" exit is made from the + "new route" which was prevented from + completing + (3) a "re-entrant" entry is made into + the "old route" that was exited + before stop was called if it exists + (i.e.: the failed entry is not into + the first history item) + */ +} +``` + + ## Not Found Routes You can configure Not Found routes like this: diff --git a/client/router.js b/client/router.js index ae91751..1cb9bd8 100644 --- a/client/router.js +++ b/client/router.js @@ -102,7 +102,22 @@ Router.prototype.route = function(pathDef, options, group) { }; var triggers = self._triggersEnter.concat(route._triggersEnter); - Triggers.runTriggers( + var triggersEnterInvocationContext = { + type: "enter", + route: route, + newRoute: route, + oldRoute: oldRoute, + router: self, + stopped: false, + isReentrant_followingStoppedExit: !!self.__is_reentrant_following_stop_exit__, + isReentrant_followingStoppedEnter: !!self.__is_reentrant_following_stop_enter__ + }; + delete self.__is_reentrant_following_stop_exit__; + delete self.__is_reentrant_following_stop_enter__; + + // triggers for entry aren't run when "going back to an old route" + // after stopping entry to another route + Triggers.runTriggers.call(triggersEnterInvocationContext, triggers, self._current, self._redirectFn, @@ -113,7 +128,22 @@ Router.prototype.route = function(pathDef, options, group) { // calls when you exit from the page js route route._exitHandle = function(context, next) { var triggers = self._triggersExit.concat(route._triggersExit); - Triggers.runTriggers( + var triggersExitInvocationContext = { + type: "exit", + route: route, + oldRoute: route, + router: self, + stopped: false, + isReentrant_followingStoppedExit: !!self.__is_reentrant_following_stop_exit__, + isReentrant_followingStoppedEnter: !!self.__is_reentrant_following_stop_enter__ + }; + + if (!self._current.path && !!self.__is_reentrant_following_stop_exit__) { + // patch up the path in a re-entrant exit + self._current.path = context.path; + } + + Triggers.runTriggers.call(triggersExitInvocationContext, triggers, self._current, self._redirectFn, diff --git a/client/triggers.js b/client/triggers.js index 7733332..b298be3 100644 --- a/client/triggers.js +++ b/client/triggers.js @@ -55,7 +55,7 @@ Triggers.createRouteBoundTriggers = function(triggers, names, negate) { matched = (negate)? matched * -1 : matched; if(matched === 1) { - originalTrigger(context, next); + originalTrigger.apply(this, arguments); } }; return modifiedTrigger; @@ -74,11 +74,53 @@ Triggers.runTriggers = function(triggers, context, redirectFn, after) { var inCurrentLoop = true; var alreadyRedirected = false; + var triggerInvocationContext = this; + var _goBackOnStop = false; + for(var lc=0; lc 0) { + router._page.back(); + } + }); + }); + } + + if (triggerInvocationContext.type === "enter") { + Meteor.defer(function() { + router.__is_reentrant_following_stop_enter__ = true; + router._page.back(); + }); + } + } + return; } } @@ -106,7 +148,9 @@ Triggers.runTriggers = function(triggers, context, redirectFn, after) { redirectFn(url, params, queryParams); } - function doStop() { + function doStop(goBackOnStop = false) { + _goBackOnStop = goBackOnStop; abort = true; + triggerInvocationContext.stopped = abort; } }; \ No newline at end of file From 438cfd8d4cc2bcf11eb1093068254f5aac65ca8c Mon Sep 17 00:00:00 2001 From: Jeremy Chen Date: Sun, 6 Nov 2016 20:08:56 +0800 Subject: [PATCH 2/2] added ecmascript --- package.js | 1 + 1 file changed, 1 insertion(+) diff --git a/package.js b/package.js index 10a6e0c..e47806e 100644 --- a/package.js +++ b/package.js @@ -14,6 +14,7 @@ Npm.depends({ Package.onUse(function(api) { configure(api); + api.use('ecmascript'); api.export('FlowRouter'); });