How to override addon's template?

Let’s say I’m using a Glimmer component from an addon that’s initialized like this: <PowerSelect .../>. This addon is using a co-located template named components/power-select/trigger.hbs. I want to override this template (use custom html).

Previously when the template wasn’t co-located, I could just override the template by creating a file app/templates/components/power-select/trigger.hbs. I’ve tried the same here (app/components/power-select/trigger.hbs), but that’s failing with Uncaught (in promise) TypeError: can't redefine non-configurable property "default". I think that because creating that file, I’m creating a template-only component, instead of augmenting the addon’s component.

How would I go about replacing an addon’s template?

Just spitballing here what happens if you also re-export the addon component class for trigger e.g.

// app/components/power-select/trigger.js
export { default } from 'ember-power-select/components/trigger';

The following input seems to comply with the TypeScript compiler:

// app/components/power-select/trigger.ts
export { default } from 'ember-power-select/addon/components/power-select/trigger';

However this fails run time:

Uncaught Error: Could not find module ember-power-select/addon/components/power-select/trigger imported from cb-ember/components/power-select/trigger

Uncaught Error: Failed to create an instance of ‘component:power-select/trigger’. Most likely an improperly defined class or an invalid module export.

Yeah I should have looked in the actual addon first, should be

export { default } from 'ember-power-select/components/power-select/trigger';

Basically you just want to match the re-export that’s already in the addon. I’m not 100% sure this will even work but if the problem is that Ember thinks you’re overriding the power select trigger with a template only component this seems like the easiest way to get it to use the class for the addon version and the template for yours

The TypeScript compiler complains about there being no such module:

app/components/power-select/trigger.ts:1:25 - error TS2307: Cannot find module 'ember-power-select/components/power-select/trigger' or its corresponding type declarations.

1 export { default } from 'ember-power-select/components/power-select/trigger';
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I’ve also tried bypassing TypeScript by giving the file a .js extension, but that still fails run time:

Uncaught Error: my-app/components/power-select/trigger.js does not contain a default export. Did you forget to export the component class?

Uncaught TypeError: can’t redefine non-configurable property “default”

hmmmm maybe try

import PowerSelectTrigger from 'ember-power-select/components/power-select/trigger';

export default PowerSelectTrigger;

Not sure why that would make any difference but i guess you could toss a breakpoint in there and see what’s happening.

That fails with the following error:

Uncaught Error: Assertion Failed: Cannot call `setComponentTemplate` multiple times on the same class (`class Trigger extends _component.default {
    clear(e) {
      e.stopPropagation();
      this.args.select.actions.select(null);

      if (e.type === 'touchstart') {
        return false;
      }
    }

  }`)

Firefox reports “this page has no sources”, probably because the JavaScript throws an error during startup. I cannot set / hit a breakpoint.

I’m on ember 3.16, and tested against 3.23 as well. The errors stay the same, but there is some additional information printed:

Error occurred while rendering:

- While rendering:
  my-wrapping-component
    power-select
      basic-dropdown
        basic-dropdown-trigger
          -dynamic-element

So I suppose the answer to my question is answered here: Build-time transformations:

One caveat here is that each component JavaScript file should export a value that is unique to that file. For example, this should be avoided :

// app/components/foo-bar.js

import MyParentComponent from "./my-parent";

// BAD: don't do this!
export default MyParentComponent;

This is problematic because setComponentTemplate will be called on MyParentComponent directly, affecting the parent component and all of its descendants. This can be avoided by subclassing, even when no customization is required:

// app/components/foo-bar.js

import MyParentComponent from "./my-parent";

// GOOD: do this instead!
export default class extends MyParentComponent {}

With that in mind, I’ve changed my code to read like this:

import Trigger from 'ember-power-select/components/power-select/trigger';
export default class extends Trigger { }

And now my custom template is being used!

As a separate issue I cannot seem to find the magical incantation to get TypeScript to allow the import. TypeScript insists on adding /addon/, which fails run time. This might be due to the following caveat listed in ember-cli-typescript:

  • Similarly, apps must use .js to override addon defaults in app , since the different file extension means apps no long consistently “win” over addon versions (a limitation of how Babel + app merging interact).
1 Like

Ah wow good find. We’re working on Octane migration right now and I hadn’t run into that yet. Thanks for sharing.