Is there a better way to make an ajax request from a theme component?

AI-generated summary

The discussion revolves around creating a theme component that makes an AJAX request to a separate website to check if a Discourse user has an account on that site. wilson29thid has implemented a working solution but is looking for a better way to do it, as the current approach feels like “hackery.”

The current implementation uses a computed property to run an async function, which is not ideal. wilson29thid suspects that a better approach would be to run the async request in an “on load” hook, but only when on the user profile page.

Additionally, wilson29thid would like to make the plugin configurable, allowing settings to be defined using a Handlebars-like template.

pfaffman suggests using a plugin instead, making the request server-side and storing the data if it’s stable, then adding the link to the serializer. He also mentions that there might be an existing theme component that adds other topic lists to the top of the page, which could provide a useful example.

I’ve built a theme component that renders when you’re viewing a user’s profile. It makes an ajax request to a separate website (which I control) to check whether the discourse user has an account there (sending the discourse user id in the url). If so, it renders a link to their account on that website.

It’s working, but I’m fairly certain it’s awful hackery that would make a discourse developer sick! So I wanted to ask whether there’s a better way to do it? I’m a JavaScript and ruby developer, but I’ve never worked with Ember (I’ve used Vue, Backbone, and React).

<script type="text/discourse-plugin" version="0.8.7">
    const {ajax} = require('discourse/lib/ajax')

    api.registerConnectorClass('user-profile-primary', 'custom-profile-link', {
      setupComponent(args, component) {
        args.model.get('fetchPersonnelMember')
      }
    })
    
    api.modifyClass('model:user', {
      fetchPersonnelMember: function () {
        console.log('running!', ajax)
        const urlTemplate = settings.personnel_lookup_url // e.g. "https://api.29th.org/members?discourse_id=%s"

        const url = urlTemplate.replace(/%s/, this.id)
        return ajax(url).then((result) => {
            if (result.status == true && result.member.id) {
              this.setProperties({personnelMember: result.member})
            }
          })
      }.property('id'),
      
      personnelLinkUrl: function () {
        if (this.personnelMember) {
          const viewUrlTemplate = settings.personnel_view_url // e.g. "https://personnel.29th.org/#members/%s"
          return viewUrlTemplate.replace(/%s/, this.personnelMember.id)
        }
      }.property('personnelMember'),
      
      personnelLinkName: function () {
        return this.personnelMember ? this.personnelMember.short_name : ''
      }.property('personnelMember')
    })
</script>

<script type='text/x-handlebars' data-template-name='/connectors/user-profile-primary/custom-profile-link'>
  {{#if model.personnelLinkUrl}}
    <div class="public-user-fields">
      <div class="public-user-field">
        <span class="user-field-name">
          {{theme-setting 'personnel_label'}}
        </span>
        <span class="user-field-value">
          <a href="{{{model.personnelLinkUrl}}}">{{model.personnelLinkName}}</a>
        </span>
      </div>
    </div>
  {{/if}}
</script>

I derived this mostly from (Retired) Use an ID in a custom user field to link to a user's external profile - #24 by LeoMcA. My guess is the main problem is I’m using a computed property to run an async function, which is a no-no. Instead, I should be running that async request in some sort of “on load” hook (but only when I’m on the user profile page).

I also feel like I’m reinventing the wheel a little with the template format of the variables. Ideally, I’d make this a configurable plugin that I’d share, so it would be nice if the settings could look more like a handlebars template, e.g. https://mysite.com/members/{{id}}, but I didn’t see any utilities or helpers for this in the discourse codebase.

I’d welcome your feedback! Thanks for your time.

I hope bumping is okay because I could really use a steer on this one :slight_smile:

If you can use a plugin instead, I’d do the request server side (and store the data if it’s stable) and add the link to the serializer.

I think there is a theme component that adds other topic lists the three top of the page like feverbee does. It might have the example you need.