MithrilJS / mithril.js

A JavaScript Framework for Building Brilliant Applications
https://mithril.js.org
MIT License
14.01k stars 926 forks source link

How to Route to an external url #2170

Closed pdfabbro closed 6 years ago

pdfabbro commented 6 years ago

Hi, my company has a website that allows you to create customizable "add-ins" all within one website. As far as I know, the way our code works is to replace the content of a div with the content of the add-in. I have an add-in built in mithril which appears on the website as a menu item in the left hand navigation. The other add-ins are in the left hand nav and NOT built in mithril/do not have anything to do with my application. Therefore it is impossible for me to add routes for all of these other add-ins in my m.route call. What happens is, the main website redirects to the add-in I selected, but then mithril's routing kicks in after the fact and redirects back to my default route.

I've tried everything I can think of, and am currently doing the following (see below). Based on this quote "Note that to redirect to a different page outside of the scope of Mithril's routing, you should use window.location", from this link, I would think that this would work:

https://mithril.js.org/archive/v0.2.5/mithril.route.html

(I am using a newer version of mithril (1.1.6), but I assume this documentation still applies):

I should add a little more background - our main website provides hooks for "initialize", "focus", and "blur". Initialize runs when people select your add-in for the very first time in the session, focus runs at various points whne doing stuff within the add-in, and blur runs when you leave the add-in by clicking on another one or leaving the site altogether. So I am trying the following:

        blur: function() {
            const hash = window.location.hash;
            m.route.set(`/external/${hash.substring(1, hash.length)}`);
        }

        m.route(document.getElementById("container"), "/dashboard/manage", {
            "/home/:dashboardId": homePage,
            "/dashboard/manage": dashboardManagerPage,

            //TODO: Change dashboardId and snapshotId to just id
            "/dashboard/:dashboardId": dashboardPage,
            "/dashboard/:dashboardId/:action": dashboardPage,
            "/dashboard/:dashboardId/:action/:dateRange/:filters": dashboardPage,

            "/snapshot/:snapshotId": dashboardPage,
            "/snapshot/:snapshotId/:action": dashboardPage,
            "/snapshot/:snapshotId/:action/:dateRange/:filters": dashboardPage,
            "/external/:hash": {
                onmatch(args) {
                    window.location.href = window.location.origin + window.location.pathname + "#" + args.hash;
                }
            }

The onmatch runs correctly and calls the location.href and changes the url, however, since I am still in the main application's framework, mithril is still active and therefore still checks the route and defaults back to /dashboard/manage.

Is there any way to "kill" mithril on blur or tell it to stop routing?

Expected Behavior

Navigate to the add-in the user selects

Current Behavior

Navigates to the add-in the user selects, but then the mithril router kicks in and changes the url to dashboard/manage

Possible Solution

No idea, I've tried everything

Steps to Reproduce (for bugs)

This is a secure application so I can't really provide links. Information about the sdk I am building an add-in for is here:

https://my.geotab.com/sdk/#/addins

Context

I want the url to be the url of the new add-in selected/tell mithril not to route/kill/"unmount" mithril

Your Environment

Mithril 1.1.6 All browsers All OS

I apologize if this is not the right spot for this, but appreciate any help/ideas that can be provided. Thanks!

pdfabbro commented 6 years ago

Update. If I change my onmatch too:

            onmatch(args) {
                return;
            }

It correctly works the first time since I can manually set the path to external in my onblur. But when I click on other add-ins, since I'm still within the main applications framework, those add-ins don't have "external" in their path and so it redirects back to mithril's default. I might be able to figure out a way to have this always work, but my question remains - is there a way to kill mithril or turn off its routing. If so, I would do this in my onBlur event and then reinitialize it in my focus event.

barneycarroll commented 6 years ago

Killing off Mithril's routing is kind of convoluted. There's no baked-in affordance to do this, so what you need to do instead is re-initialise routing on your root element with the current route as the default route and a route map with one single wild-card route that will map to every conceivable route, and take no action as a result. So we're still using Mithril routing, but it doesn't do anything.

Here's an example where I set up a route with a single path, which logs a message onmatch, and then immediately resets routing with the 'noop' route map on line 9. You can verify this works because despite routing to the same default route, the console log is not triggered a second time (if it didn't work, we would be stuck in an infinite loop where route resolution initialises a new route and resolves the old route again, initialising etc etc etc).

So for your scenario we would copy that route initialisation code into the blur function, replacing the element reference and the initial route to match your circumstances.

pdfabbro commented 6 years ago

Thanks for the prompt reply Barney! I'll give that a try and keep you posted. I didn't realize you could call m.route more than once within the application, I thought once you set it, it was uneditable. I'll reply shortly with whether or not it worked.

pdfabbro commented 6 years ago

Can't seem to get it working. When you say "re-initialise routing on your root element with the current route as the default route", what do you mean by current root - the "external" route that they are going to? Also, which of the following "/" need to be replaced to my current route. I've tried every possible combination I think so let me just number them:

            m.route(document.getElementById("container"),  1, {
                2: {
                    onmatch: () => {
                        console.log("match!");
                        m.route(document.getElementById("container"), 3, {
                            ":path...": {
                                onmatch: () => {
                                    return;
                                },
                            },
                        });
                    }
                },
            });

Should all of 1, 2, and 3 change to be my current route?

pdfabbro commented 6 years ago

I think I was just able to get it working without touching the blur event at all. Thanks to your ":path..." wild card (thanks for that, I was wondering how to do that). I did the following and now if it doesn't recognize the url, it just does nothing i.e. let's the url go/redirect, and if it does recognize it, it redirects to a page in my app. Thanks so much for your help!

            m.route(document.getElementById("container"), "/dashboard/manage", {
                "/home/:dashboardId": homePage,
                "/dashboard/manage": dashboardManagerPage,

                //TODO: Change dashboardId and snapshotId to just id
                "/dashboard/:dashboardId": dashboardPage,
                "/dashboard/:dashboardId/:action": dashboardPage,
                "/dashboard/:dashboardId/:action/:dateRange/:filters": dashboardPage,

                "/snapshot/:snapshotId": dashboardPage,
                "/snapshot/:snapshotId/:action": dashboardPage,
                "/snapshot/:snapshotId/:action/:dateRange/:filters": dashboardPage,
                "addin-mygeotab_bi_sandbox-mapsbi-1191_appspot_com": dashboardManagerPage,
                ":path...": {
                    onmatch: (args) => {
                        return;
                    },
                },
            });