Search code examples
javascriptmithril.js

Mitrhil.js conditional routing and authentication


I'm studying javascript and mithril.js 1.1.6. I'm writing down a simple web app in which users land on a page where he can login. Users who already did login land on a different page. I'm trying this using conditional routing, here is the main component:

const m = require("mithril");
...
import Eventbus from './whafodi/eventbus.js';
import WelcomePage from './ui/welcome.js';
import User from './model/user.js';
var eventbus = new Eventbus();
function MyApp() {
  return {
    usrAuth: function() {
      m.route(document.body, "/", {
        "/": { view: () => m("p", "hello")}
    })
  },
  usrNotAuth: function() {
    m.route(document.body, "/", {
      "/": { render: v => m(WelcomePage, eventbus) }
    })
  },
  oninit: function(vnode) {
    vnode.state.user = new User();
    eventbus.subscribe({
      type: "login",
      handle: function(action) {
        vnode.state.user.token = action.token;
        console.log(JSON.stringify(vnode.state.user));
      }
    });
  },
  view: function(vnode) {
    if(vnode.state.user.token) {
      this.usrAuth();
    } else {
      this.usrNotAuth();
    }
  }
}
};
m.mount(document.body, MyApp);

MyApp is the main component. It check if user has a token, then return the proper route. This is the component that is in charge to let users login:

const m = require("mithril");
const hellojs = require("hellojs");
function TopBar(node) {
  var bus = node.attrs.eventbus;
  function _login() {
    hellojs('facebook').login({scope:'email'});
  }
  return {
    oninit: function(vnode) {
      hellojs.init({
         facebook: XXXXXXX,
      }, {
         redirect_uri: 'http://localhost'
      });
      hellojs.on('auth.login', auth => {
        var fbtoken = auth.authResponse.access_token;
        m.request({
          method:"POST",
          url:"./myapp/login/fb/token",
          data:auth.authResponse,
          background: true
        }).then(function(result){
          console.log(result);
          bus.publish({ type: "login", token: result.jwttoken });
          m.route.set("/");
        }, function(error){
          console.log(error);
          bus.publish({ type: "login", token: "" });
        });
      });
    },
    view: function(vnode) {
      return m("div", [
        m("button", { onclick:  _login }, "Login")
      ]);
    }
  }
}
export default TopBar;

TopBar component occurs in the WelcomePage component mentioned in the main one. TopBar renders a button and use hello.js to login. It uses the EventBus bus parameter to tell main component user logged in (there is an handler in main component to update the user model). Once user logins, event is fired and main component updates the user model. Good. Now, how can trigger the main component to load the right route?


Solution

  • I read mithril'docs again and I found that RouteResolvers perfectly suit my needs. Here is an example:

    var App = (function() {
      var login;
      function isLoggedIn(component) {
        if(login) {
          return component;
        } else {
          m.route.set("/hey");
        }
      }
      return {
        oninit: function(vnode) {
          EventBus.subscribe({
            type: "login",
            handle: function(action) {
              console.log("incoming action: " + JSON.stringify(action));
              login = action.value;
            }
          });
        },
        oncreate: function(vnode) {
          Foo.eventbus = EventBus;
          Bar.eventbus = EventBus;
          Hey.eventbus = EventBus;
          m.route(document.body, "/hey", {
            "/foo": {
              onmatch: function(args, requestedPath, route) { return isLoggedIn(Foo); }
            },
            "/bar": {
              onmatch: function(args, requestedPath, route) { return isLoggedIn(Bar); }
            },
            "/hey": Hey
          });
        },
        view: function(vnode) {
          return m("div", "home..");
        }
      };
    })();
    

    Eventbus is used to let components communicate with App. They fire events (login type events) that App can handle. I found convenient to pass Eventbus the way oncreate method shows, I can use Eventbus in each component's oncreate to let components fire events.