How do I find out if I'm inside an engine?

Hi,

I’m writing a route mixin that is supposed to call this.transitionToExternal when used inside an engine and this.transitionTo otherwise. Is there a way to reliably find out if I’m inside an engine?

Currently I’m using Boolean(Ember.getOwner(this).dependencies), but a blessed way would be great.

Cheers, Jan Niklas

1 Like

I can’t speak to a direct answer to your question (detecting wether you’re in an engine) — but, I handled a similar scenario by naming the route in the engine something totally different from what it’s named in the app (though, I’ll agree that it might be nice that there’s a convention for this — if there is one, oops!).

For example, let’s say these pieces are for my app:

// app/router.js
Router.map(function() {
  this.mount('my-engine');
  this.route('home');
});

// app/app.js
const App = Ember.Application.extend({
  engines: {
    myEngine: {
      externalRoutes: {
        'external-home': 'home'
      }
    }
  }
});

Then, in my engine:

// addon/engine.js
const Eng = Engine.extend({
  dependencies: {
    externalRoutes: ['external-home']
  }
});

That mixin that you use could then look something like:

// mixins/transition-to-local-or-external.js
export default Mixin.create({
  actions: {
    transitionToLocalOrExternal(route, ...params) {
      if (route.indexOf('external-') === 0) {
        this.transitionToExternal(route, ...params);
      } else {
        this.transitionTo(route, ...params);
      }
    }
  }
});

Thanks for your reply.

Unfortunately this won’t work in my case: The route name is fixed inside the mixin (login - it’s an auth mixin). Basically I could add a config option to supply a route name as you suggested, but this wouldn’t be very different from an option isEngine, i.e. doing it manually.

Ahh, I just double checked where I had done this — funnily enough, this was the same use case (login route from a protected route in an engine). I’m using ember-simple-auth — not sure if you’re using the same. But, this is what I ended up doing…it does feel kinda hacky as I’m overloading the transitionTo method:

// my-engine/addon/mixins/authenticated-route.js
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';

export default Mixin.create(AuthenticatedRouteMixin, {
  authenticationRoute: 'external-login',

  transitionTo(routeName) {
    if (routeName === this.get('authenticationRoute')) {
      return this.transitionToExternal(...arguments);
    } else {
      return this._super(...arguments);
    }
  }
});

Then, I use that mixing rather than the simple auth one directly.

It looks like some other folks are using a slightly different solution too: How to use ember-simple-auth with ember engines? · Issue #1125 · simplabs/ember-simple-auth · GitHub (not sure wether this applies to you if you’re not using Simple Auth).

Thanks again.

If I understand correctly, your approach uses different mixins inside and outside routes.

In the linked Issue there is an additional route inside the engine that has only one function: Propagating to the app’s route of the same name.

Both are interesting ideas, but not really what I was looking for. I think, the basic problem is that there is a distinction between transitionTo and transitionToExternal - a unified method could just look if it’s an aliased name and then do the right thing …

Yes, but to clarify: different mixins inside and outside engines.

I think you’re bumping up against some of the things yet to be fully fleshed out with Engines. It seems they initially went for very deliberate and explicit separation. (Here’s an example of a relevant conversation: Add transitionToExternal to router · Issue #260 · ember-engines/ember-engines · GitHub).

I’m curious — what would your ideal solution look like for your use case? Something like — Invoke transitionTo from inside an engine and if the route doesn’t exist in the engine, then check to see if it exists in the consuming application — if so, go there?

Yes, of course, inside and outside engines. Something got mixed up between my brain and my keyboard. :grin:

Thanks for the link. My ideal solution would be having a thing inside the engine with an interface like the standard routing service that knows about (a) the routes defined inside the engine and (b) about the routes that are part of the interface, i. e. the names specified under the externalRoutes key in engine.js. Basically those externalRoutes should look like a “normal” route from inside the engine, known by the name specified in externalRoutes.

This would mean I could use the same mixin with the same hard-coded route name inside and outside an engine. In other words: The mixin wouldn’t have to be aware of engines.

Let’s say for some reason you have both an internal route and an external route with the same name — let’s say it’s 'home'.

In that case, when you do this.transitionTo('home');, how does the router know what the intent of that transition is?

I suppose one option is that we can assume that then intent is a local route name if available first — and potentially external if not. Otherwise, if we need to disambiguate we would just use this.transitionToExternal('home'); to skip the local route.

Anyway — perhaps some other folks will chime in. I do think this is a bit of a pain point that’s worth exploring.

// utils/app.js

import config from 'host-app/config/environment';
import { getOwner } from '@ember/application';

export function modulePrefix(app) {
  return getOwner(app).resolveRegistration('config:environment').modulePrefix;
}

export function inHostApp(app) {
  return modulePrefix(app) === config.modulePrefix;
}
// my-route/route.js
import { inHostApp } from 'utils/app';

// ...
goSomewhere() {
  if (inHostApp(this)) {
    this.transitionTo('somewhere');
  } else {
    this.transitionToExternal('somewhere');
  }
}