Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rejected return to #45

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ v0.0.6 🎉 - by [@thekitze](http://kitze.io)
- The current URL params and query params are accessible directly in the store ```store.router.params``` / ```store.router.queryParams``` so basically they're available everywhere without any additional wrapping or HOC.
- Navigating to another view happens by calling the ```goTo``` method on the router store, and the changes in the url are reflected automatically. So for example you can call ```router.goTo(views.book, {id:5, page:3})``` and after the change is made in the store, the URL change will follow. You never directly manipulate the URL or the history object.
- ```<Link>``` component which also populates the href attribute and works with middle click or ```cmd/ctrl``` + click
- If a ```beforeEnter``` was rejected, the rejected context (view, params, queryParams) are remembered, and a subsequent call to a ```returnTo``` method will retry the route. This is useful for implementing pass-thru authentication on protected routes. See below.

### Implementation
```js
Expand Down Expand Up @@ -119,6 +120,41 @@ const views = {
export default views;
```

### Pass Through Authentication

If you protect some routes with a ```isUserAuthenticated``` by providing a ```beforeEnter``` hook, you'd like to be able to go off to a login view, which will (easily) return back to the original failed route. You might
do this with the following ```views``` configuration:

```javascript
const views = {
home: new Route({
path: '/home',
component: <Home />,
beforeEnter: (route, params, store, queryParams ) => {
if ( store.user.isAuthenticated ) return true;
store.router.goTo( routes.login );
return false;
}
}),
login: new Route({
path: '/login',
component: <Login />
})
};
```

In the ```<Login />``` component, after authenticating the user, then do this:

```javascript
store.router.returnTo( store, {
view: views.home, // default view to redirect to, if there is not a pending rejection
params: null, // default params to pass to default view
queryParams: null // default queryParams to pass to default view
});
```

That way, after a successful login, the user will be either directed to the original attempted route, or a default route.

### ToDo
- [ ] Add async support for the ```beforeEnter``` and ```beforeExit``` hooks
- [ ] Add array support for the hooks so they can execute multiple methods. A sample usage of this would be having one ```isUserAuthenticated``` method that can be just plugged in as one of the callbacks triggered from the hook.
88 changes: 53 additions & 35 deletions dist/mobx-router.es2015.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ import { action, autorun, computed, observable, toJS } from 'mobx';
import queryString from 'query-string';
import { Router } from 'director/build/director';
import React from 'react';
import { observer } from 'mobx-react';
import { inject, observer } from 'mobx-react';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};











var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
Expand Down Expand Up @@ -237,7 +243,7 @@ var Route = function () {
Example: if url is /book/:id/page/:pageId and object is {id:100, pageId:200} it will return /book/100/page/200
*/
value: function replaceUrlParams(params) {
var queryParams = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var queryParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

params = toJS(params);
queryParams = toJS(queryParams);
Expand All @@ -247,11 +253,10 @@ var Route = function () {
var newPath = this.originalPath;

getRegexMatches(this.originalPath, paramRegex, function (_ref) {
var _ref2 = slicedToArray(_ref, 3);

var fullMatch = _ref2[0];
var paramKey = _ref2[1];
var paramKeyWithoutColon = _ref2[2];
var _ref2 = slicedToArray(_ref, 3),
fullMatch = _ref2[0],
paramKey = _ref2[1],
paramKeyWithoutColon = _ref2[2];

var value = params[paramKeyWithoutColon];
newPath = value ? newPath.replace(paramKey, value) : newPath.replace('/' + paramKey, '');
Expand All @@ -271,11 +276,10 @@ var Route = function () {

var params = [];
getRegexMatches(this.originalPath, paramRegex, function (_ref3) {
var _ref4 = slicedToArray(_ref3, 3);

var fullMatch = _ref4[0];
var paramKey = _ref4[1];
var paramKeyWithoutColon = _ref4[2];
var _ref4 = slicedToArray(_ref3, 3),
fullMatch = _ref4[0],
paramKey = _ref4[1],
paramKeyWithoutColon = _ref4[2];

params.push(paramKeyWithoutColon);
});
Expand Down Expand Up @@ -353,9 +357,15 @@ var RouterStore = (_class = function () {
_initDefineProp(this, 'currentView', _descriptor3, this);

this.goTo = this.goTo.bind(this);
this.returnTo = this.returnTo.bind(this);
}

createClass(RouterStore, [{
key: 'returnTo',
value: function returnTo(store, defaultView) {
if (this.rejectedView) this.goTo(this.rejectedView, this.rejectedParams, store, this.rejectedQueryParams);else if (defaultView) this.goTo(defaultView.view, defaultView.params, store, defaultView.queryParams);else console.error('no rejected view to return to and no default view provided');
}
}, {
key: 'goTo',
value: function goTo(view, paramsObj, store, queryParamsObj) {

Expand All @@ -375,8 +385,18 @@ var RouterStore = (_class = function () {
return;
}

var beforeEnterResult = rootViewChanged && view.beforeEnter ? view.beforeEnter(view, currentParams, store, currentQueryParams) : true;
var nextParams = toJS(paramsObj);
var nextQueryParams = toJS(queryParamsObj);

this.rejectedParams = null;
this.rejectedQueryParams = null;
this.rejectedView = null;

var beforeEnterResult = rootViewChanged && view.beforeEnter ? view.beforeEnter(view, nextParams, store, nextQueryParams) : true;
if (beforeEnterResult === false) {
this.rejectedParams = toJS(paramsObj);
this.rejectedQueryParams = toJS(queryParamsObj);
this.rejectedView = view;
return;
}

Expand All @@ -385,8 +405,6 @@ var RouterStore = (_class = function () {
this.currentView = view;
this.params = toJS(paramsObj);
this.queryParams = toJS(queryParamsObj);
var nextParams = toJS(paramsObj);
var nextQueryParams = toJS(queryParamsObj);

rootViewChanged && view.onEnter && view.onEnter(view, nextParams, store, nextQueryParams);
!rootViewChanged && this.currentView && this.currentView.onParamsChange && this.currentView.onParamsChange(this.currentView, nextParams, store, nextQueryParams);
Expand All @@ -411,7 +429,7 @@ var RouterStore = (_class = function () {
}), _descriptor3 = _applyDecoratedDescriptor(_class.prototype, 'currentView', [observable], {
enumerable: true,
initializer: null
}), _applyDecoratedDescriptor(_class.prototype, 'goTo', [action], Object.getOwnPropertyDescriptor(_class.prototype, 'goTo'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'currentPath', [computed], Object.getOwnPropertyDescriptor(_class.prototype, 'currentPath'), _class.prototype)), _class);
}), _applyDecoratedDescriptor(_class.prototype, 'returnTo', [action], Object.getOwnPropertyDescriptor(_class.prototype, 'returnTo'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'goTo', [action], Object.getOwnPropertyDescriptor(_class.prototype, 'goTo'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'currentPath', [computed], Object.getOwnPropertyDescriptor(_class.prototype, 'currentPath'), _class.prototype)), _class);

var createDirectorRouter = function createDirectorRouter(views, store) {
new Router(_extends({}, viewsForDirector(views, store))).configure({
Expand Down Expand Up @@ -441,26 +459,26 @@ var MobxRouter = function MobxRouter(_ref) {
router.currentView && router.currentView.component
);
};
var MobxRouter$1 = observer(['store'], MobxRouter);
var MobxRouter$1 = inject('store')(observer(MobxRouter));

var Link = function Link(_ref) {
var view = _ref.view;
var className = _ref.className;
var _ref$params = _ref.params;
var params = _ref$params === undefined ? {} : _ref$params;
var _ref$queryParams = _ref.queryParams;
var queryParams = _ref$queryParams === undefined ? {} : _ref$queryParams;
var _ref$store = _ref.store;
var store = _ref$store === undefined ? {} : _ref$store;
var _ref$refresh = _ref.refresh;
var refresh = _ref$refresh === undefined ? false : _ref$refresh;
var _ref$style = _ref.style;
var style = _ref$style === undefined ? {} : _ref$style;
var children = _ref.children;
var _ref$title = _ref.title;
var title = _ref$title === undefined ? children : _ref$title;
var _ref$router = _ref.router;
var router = _ref$router === undefined ? store.router : _ref$router;
var view = _ref.view,
className = _ref.className,
_ref$params = _ref.params,
params = _ref$params === undefined ? {} : _ref$params,
_ref$queryParams = _ref.queryParams,
queryParams = _ref$queryParams === undefined ? {} : _ref$queryParams,
_ref$store = _ref.store,
store = _ref$store === undefined ? {} : _ref$store,
_ref$refresh = _ref.refresh,
refresh = _ref$refresh === undefined ? false : _ref$refresh,
_ref$style = _ref.style,
style = _ref$style === undefined ? {} : _ref$style,
children = _ref.children,
_ref$title = _ref.title,
title = _ref$title === undefined ? children : _ref$title,
_ref$router = _ref.router,
router = _ref$router === undefined ? store.router : _ref$router;

if (!router) {
return console.error('The router prop must be defined for a Link component to work!');
Expand Down
86 changes: 52 additions & 34 deletions dist/mobx-router.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@ var mobxReact = require('mobx-react');
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};











var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
Expand Down Expand Up @@ -243,7 +249,7 @@ var Route = function () {
Example: if url is /book/:id/page/:pageId and object is {id:100, pageId:200} it will return /book/100/page/200
*/
value: function replaceUrlParams(params) {
var queryParams = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
var queryParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

params = mobx.toJS(params);
queryParams = mobx.toJS(queryParams);
Expand All @@ -253,11 +259,10 @@ var Route = function () {
var newPath = this.originalPath;

getRegexMatches(this.originalPath, paramRegex, function (_ref) {
var _ref2 = slicedToArray(_ref, 3);

var fullMatch = _ref2[0];
var paramKey = _ref2[1];
var paramKeyWithoutColon = _ref2[2];
var _ref2 = slicedToArray(_ref, 3),
fullMatch = _ref2[0],
paramKey = _ref2[1],
paramKeyWithoutColon = _ref2[2];

var value = params[paramKeyWithoutColon];
newPath = value ? newPath.replace(paramKey, value) : newPath.replace('/' + paramKey, '');
Expand All @@ -277,11 +282,10 @@ var Route = function () {

var params = [];
getRegexMatches(this.originalPath, paramRegex, function (_ref3) {
var _ref4 = slicedToArray(_ref3, 3);

var fullMatch = _ref4[0];
var paramKey = _ref4[1];
var paramKeyWithoutColon = _ref4[2];
var _ref4 = slicedToArray(_ref3, 3),
fullMatch = _ref4[0],
paramKey = _ref4[1],
paramKeyWithoutColon = _ref4[2];

params.push(paramKeyWithoutColon);
});
Expand Down Expand Up @@ -359,9 +363,15 @@ var RouterStore = (_class = function () {
_initDefineProp(this, 'currentView', _descriptor3, this);

this.goTo = this.goTo.bind(this);
this.returnTo = this.returnTo.bind(this);
}

createClass(RouterStore, [{
key: 'returnTo',
value: function returnTo(store, defaultView) {
if (this.rejectedView) this.goTo(this.rejectedView, this.rejectedParams, store, this.rejectedQueryParams);else if (defaultView) this.goTo(defaultView.view, defaultView.params, store, defaultView.queryParams);else console.error('no rejected view to return to and no default view provided');
}
}, {
key: 'goTo',
value: function goTo(view, paramsObj, store, queryParamsObj) {

Expand All @@ -381,8 +391,18 @@ var RouterStore = (_class = function () {
return;
}

var beforeEnterResult = rootViewChanged && view.beforeEnter ? view.beforeEnter(view, currentParams, store, currentQueryParams) : true;
var nextParams = mobx.toJS(paramsObj);
var nextQueryParams = mobx.toJS(queryParamsObj);

this.rejectedParams = null;
this.rejectedQueryParams = null;
this.rejectedView = null;

var beforeEnterResult = rootViewChanged && view.beforeEnter ? view.beforeEnter(view, nextParams, store, nextQueryParams) : true;
if (beforeEnterResult === false) {
this.rejectedParams = mobx.toJS(paramsObj);
this.rejectedQueryParams = mobx.toJS(queryParamsObj);
this.rejectedView = view;
return;
}

Expand All @@ -391,8 +411,6 @@ var RouterStore = (_class = function () {
this.currentView = view;
this.params = mobx.toJS(paramsObj);
this.queryParams = mobx.toJS(queryParamsObj);
var nextParams = mobx.toJS(paramsObj);
var nextQueryParams = mobx.toJS(queryParamsObj);

rootViewChanged && view.onEnter && view.onEnter(view, nextParams, store, nextQueryParams);
!rootViewChanged && this.currentView && this.currentView.onParamsChange && this.currentView.onParamsChange(this.currentView, nextParams, store, nextQueryParams);
Expand All @@ -417,7 +435,7 @@ var RouterStore = (_class = function () {
}), _descriptor3 = _applyDecoratedDescriptor(_class.prototype, 'currentView', [mobx.observable], {
enumerable: true,
initializer: null
}), _applyDecoratedDescriptor(_class.prototype, 'goTo', [mobx.action], Object.getOwnPropertyDescriptor(_class.prototype, 'goTo'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'currentPath', [mobx.computed], Object.getOwnPropertyDescriptor(_class.prototype, 'currentPath'), _class.prototype)), _class);
}), _applyDecoratedDescriptor(_class.prototype, 'returnTo', [mobx.action], Object.getOwnPropertyDescriptor(_class.prototype, 'returnTo'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'goTo', [mobx.action], Object.getOwnPropertyDescriptor(_class.prototype, 'goTo'), _class.prototype), _applyDecoratedDescriptor(_class.prototype, 'currentPath', [mobx.computed], Object.getOwnPropertyDescriptor(_class.prototype, 'currentPath'), _class.prototype)), _class);

var createDirectorRouter = function createDirectorRouter(views, store) {
new director_build_director.Router(_extends({}, viewsForDirector(views, store))).configure({
Expand Down Expand Up @@ -447,26 +465,26 @@ var MobxRouter = function MobxRouter(_ref) {
router.currentView && router.currentView.component
);
};
var MobxRouter$1 = mobxReact.observer(['store'], MobxRouter);
var MobxRouter$1 = mobxReact.inject('store')(mobxReact.observer(MobxRouter));

var Link = function Link(_ref) {
var view = _ref.view;
var className = _ref.className;
var _ref$params = _ref.params;
var params = _ref$params === undefined ? {} : _ref$params;
var _ref$queryParams = _ref.queryParams;
var queryParams = _ref$queryParams === undefined ? {} : _ref$queryParams;
var _ref$store = _ref.store;
var store = _ref$store === undefined ? {} : _ref$store;
var _ref$refresh = _ref.refresh;
var refresh = _ref$refresh === undefined ? false : _ref$refresh;
var _ref$style = _ref.style;
var style = _ref$style === undefined ? {} : _ref$style;
var children = _ref.children;
var _ref$title = _ref.title;
var title = _ref$title === undefined ? children : _ref$title;
var _ref$router = _ref.router;
var router = _ref$router === undefined ? store.router : _ref$router;
var view = _ref.view,
className = _ref.className,
_ref$params = _ref.params,
params = _ref$params === undefined ? {} : _ref$params,
_ref$queryParams = _ref.queryParams,
queryParams = _ref$queryParams === undefined ? {} : _ref$queryParams,
_ref$store = _ref.store,
store = _ref$store === undefined ? {} : _ref$store,
_ref$refresh = _ref.refresh,
refresh = _ref$refresh === undefined ? false : _ref$refresh,
_ref$style = _ref.style,
style = _ref$style === undefined ? {} : _ref$style,
children = _ref.children,
_ref$title = _ref.title,
title = _ref$title === undefined ? children : _ref$title,
_ref$router = _ref.router,
router = _ref$router === undefined ? store.router : _ref$router;

if (!router) {
return console.error('The router prop must be defined for a Link component to work!');
Expand Down
Loading