Plugin routing problems

Hi all,

I’m building a plugin but having some problems understanding what the current API is for appending routes to the Ember router. I’ve read a number of tutorial/howto posts on Meta but there seem to be a number of different ways of doing it, and it’s unclear what is the ‘current’ way.

The tutorials are great but they clearly can’t cover every use-case, and so one is left trying to guess how you do routing if you don’t want to add to an admin route, for example. In my case I want to add a new tab to the user’s profile, and have managed to do this using the plugin outlet.

I’ve followed the following tutorials in detail:
Creating Routes in Discourse and Showing Data
Adding Ember Components to Discourse
These are helpful but they are written from the point of view of patching Discourse core code, rather than building this into a plugin, so some elements of it don’t apply.

Of course I’ve also followed the full series of Beginner's Guide to Creating Discourse Plugins - Part 1 through Beginner’s Guide to Creating Discourse Plugins Part 7: Publish your plugin

At present I’m struggling with what will, I’m sure, turn out to be a simple problem with the interplay between Rails routing and Ember routing.

I have added my ‘Reflection’ tab to the user profile, which seems to work

// assets/javascripts/discourse/reflexivity-route-map.js.es6
export default {
  resource: 'user',
  path: '/u/:username/reflection',
  map() {
    this.route('reflection');
  }
};

On the Rails side there is a route declaration which is intended to serve only JSON to Ember’s Ajax call, it seems to be working at the URL reflection.json, although I haven’t figured out the Ajax bit yet within Ember (see below)

# config/routes.rb
Discourse::Application.routes.append do
  get "u/:username/reflection" => "reflections#index", constraints: { username: RouteFormat.username }
  get "users/:username/reflection" => "reflections#index", constraints: { username: RouteFormat.username }
end

Problem1

All this seems to work fine if I navigate to the Reflection tab from within another part of the Ember app, but if I refresh the page while on my Reflection tab, the Ember app disappears and it seems as though Rails routing takes over, because I just get JSON in the browser.

I’ve tried all kinds of permutations of routing options in Ember and Rails and can’t get my head around what’s going on.

Problem 2

I’m also having difficulty getting the Ember router to be run, I don’t know if it’s a file path issue, or if there is some other problem.

// assets/javascripts/discourse/routes/user/reflection.js.es6
import { ajax } from 'discourse/lib/ajax';

export default Ember.Route.extend({
  model() {
    console.log('The model hook just ran!') # <-- this does not log to console
    const userReflection = "something"

  }
});

I’ve looked at a number of other plugins and plugin templates (eg ProCourse) in order to deduce the patterns, but there seems to be a variety of different ways of doing things, and although inspecting the Discourse source code can be useful, it doesn’t replace the need for regular documentation with examples. Of course, the Ember and Rails guides and doco are useful, but some elements of this interaction are pure Discourse, and hence different to how it’s done in pure Ember or Rails.

Any help with the above would be greatly appreciated, and of course I can supply more information if needed.

2 Likes

Can you post the rails code for your controller? It seems like for some reason it is skipping the bootstrap part. Normally if a request comes in that doesn’t include .json (or an accept header of JSON like from AJAX) Discourse will bootstrap the ember app by serving HTML. If you are getting JSON without a .json that tells me you have somehow disabled this. Maybe by skipping a filter?

I noticed you are trying to append to the user route without using a resource parameter. Discourse-assign does it like this:

I would try that approach.

1 Like

Thanks for the reply @eviltrout!

Here you go

# app/controllers/reflections_controller.rb
class ReflectionsController < ActionController::Base
  def index
    render json: { title: "reflection title", description: "reflection description" }
  end
end

And posting that has made me see one of the problems - brainfart here:

class ReflectionsController < ActionController::Base 

when it should be inheriting from ApplicationController!

Changing that fixes the JSON issue.

1 Like

I also note that in Discourse-Assign you have a different file path structure which contains all your client-side javascript:

example from discourse-assign:

assets/javascripts/discourse-assign/routes/user-activity-assigned.js.es6

example from my plugin:

assets/javascripts/discourse/routes/user/reflection.js.es6

I think I have assumed the namespacing is like inserting templates into plugin outlets, when it should have its own plugin namespace?

You should prefer putting your plugin name in the path. Both used to work but it’s much clearer if you use the correct namespace. We can do things like try to not include the code when it’s disabled.

3 Likes

Ok thanks.

I had a look at the route-map and I am using a resource:

export default {
  resource: 'user',
  path: '/u/:username/',
  map () {
    this.route('reflection')
  }
}

I will confess I am a bit confused about what these terms refer to and I couldn’t find any reference to resource in the Ember docs, apart from old posts - there seems to be a suggestion they have been deprecated, and I found reference to deprecation of resource in a commit to the Discourse source code too (don’t ask me where, yesterday I read so much code and guides I’m all over the place)

I get that the path is the URL where you expect this route to be triggered
this.route('reflection') is the name that Ember will use to refer to the route
But although the resource is, strictly, a Reflection I’m not sure what to put in resource so that the route will be found.

Gradually getting there, I’ve learned a lot in the past few days, and slowly building the bridges between my (shaky) Rails knowledge and my (non-existent) Ember knowledge, and the Discourse plugin API in between.