Can we use `.gjs` for route templates?

Are glimmer components currently supported in routes?

I’m guessing that the answer is “no” for Discourse, but would be pleased to learn otherwise.

2 Likes

Yes you can use .gjs files for route templates in Discourse right now. In fact, we just converted a whole load of them today in core! Check out https://github.com/discourse/discourse/tree/main/app/assets/javascripts/discourse/app/templates for examples.

“someone” is Discourse - we sponsored the development of ember-route-template, and it’s in our GitHub organisation :wink:

We’re not on the latest Ember 6.x yet, but we use that addon to enable the functionality.

5 Likes

Oh beautiful! Thanks for the quick reply!

Haha, I didn’t even see it was in the Discourse github. Good point. :slight_smile:

3 Likes

Adding a new route to the app I found to be very non-obvious. I will document here for others.

I wanted to add a /print route at the root.

The final code that worked for me was as follows. county-fence is my plugin name, so replace that with your own.

Ember Route

assets/javascripts/discourse/routes/county-fence-route-map.js.es6

export default function() {
  this.route('print', { path: '/print' });
}

assets/javascripts/discourse/routes/print.js

import Route from "@ember/routing/route";

export default class PrintRoute extends Route {
  model() {
    return { message: "This is a custom print page!" };
  }
}

assets/javascripts/discourse/templates/print.gjs

import RouteTemplate from "ember-route-template";
import ...;

export default RouteTemplate(
  <template>
    ...
  </template>
)

API Route

plugin.rb

after_initialize do
  require_relative "app/controllers/print_controller"
end

app/controllers/print_controller.rb

# frozen_string_literal: true
# HTTP Status codes: https://github.com/discourse/discourse/blob/main/lib/discourse.rb

class ::CountyFence::PrintController < ::ApplicationController
  requires_plugin CountyFence::PLUGIN_NAME


  def save_print
  end

  def list_prints
    render json: { name: "donut", description: "delicious!" }
  end

  def get_print
  end

end

Discourse::Application.routes.append do
  post "/print" => "county_fence/print#save_print"
  get "/print" => "county_fence/print#list_prints"
  get "/print/:id" => "county_fence/print#get_print"
end
4 Likes

Is there a way I can get a blank page without the layout, navbars, or any CSS of the site? I want to use the Glimmer component, but none of the website styles are helpful in terms of producing a print page.

If its just styling for an actually printed page, and you don’t mind the navbar and extra stuff when viewing the page, you can use @media print CSS selector to change the styling when printing. Discourse already hides a lot of things during print (discourse/app/assets/stylesheets/common/printer-friendly.scss at main · discourse/discourse · GitHub). You can preview the page using print media simulation mode in firefox’s inspect mode. There should be an equivalent feature in chrome’s inspect mode.

If you need a complete blank canvas to work with, not just in the print mode but also in normal web browsing, you can use a bit of CSS trickery.
Suppose your glimmer component has a root div with id="print-component__root", we can let CSS check for its existence with a :has() selector (or just add/remove a class to body from the glimmer component lifecycle), and selectively apply styling.

body:has(#print-component__root) {
  header,
  .avatar,
  .sidebar-wrapper,
  ... {
    all: unset !important;  // all: unset might be a bit too aggressive
    display: none !important;
  }
}

#print-component__root {
  // normal styling
}

There might be a better, cleaner way that would actually remove the DOM elements, but the CSS is a good workaround to reset everything.

2 Likes

My SCSS isn’t loading. Any ideas? I set up my files per the chat plugin in core.

I have in plugins.rb:

register_asset "stylesheets/common/index.scss"

In assets/stylesheets/common/index.scss:

@import "common";

In assets/stylesheets/common/common.scss:

body:has(#print-root) {
  header,
  .avatar,
  .sidebar-wrapper {
    all: unset;  // all: unset might be a bit too aggressive
    display: none !important;
  }
}

Just to make sure the issue is with loading the SCSS and not an issue with style rule I gave you, try adding a style without the :has() selector. Something like:

.sidebar-wrapper {
  outline: 5px solid red;
}

I would also try restarting your rails server. I’ve had issues in the past where adding/removing register_asset calls would require me to do a full docker container reboot.

2 Likes
  • I removed the :has() selector.
  • Put the code into index.scss to verify it’s not the @import statement
  • ran d/rake assets:clobber tmp:clear and d/rails s

Nothing so far is getting it to load.

Most other files cause an auto-reload in the browser. This one isn’t doing that. So I think that register_asset may be where the problem lies. I think register asset automatically looks in the assets folder? All of the examples I’m looking at indicate this is the case.

1 Like

d/rails c and DiscoursePluginRegistry.stylesheets reveals:

"county-fence"=>#<Set: {"/src/plugins/county-fence/assets/stylesheets/common/index.scss"}>

So something is happening. ^^

1 Like

I found in the d/rails s logs: No such file or directory @ rb_sysopen - /src/app/assets/stylesheets/plugin-cf.scss.

So even though the plugin is symlinked into the /plugins folder as county-fence, and my plugin is explicitly named county-fence in plugins.rb, some code is reading the underlying directory name plugin-cf and making assumptions about what the compiled CSS name for my plugin is.

Is this a bug or a feature? :smiley:

I think the moral of this story is do not under any circumstances name your plugin directory anything except the matching name of your plugin. A symlink named correctly is not sufficient.

EDIT: also register_asset "stylesheets/common/index.scss", plugin: "county-fence" is a workaround that forces the plugin name to be set correctly.