Upgrade from 5.4 to 5.8

You missed some details from my answer on stack overflow :sweat_smile: No worries!

<this.tool /> has the same “dynamic” problem you were encountering before.

What you’re noticing about CaseNote is solved by the TOOLS-map/object concept below:

Also, copy pasting, in case stackoverflow loses threads or something:


Looking back at your error:

Unsafe dynamic component: this.tool in 
routes\components\case-tools\template.hbs 

is because of this code:

        <div>{{component this.tool caseId=@caseId}}</div>

you can’t do {{component this.tool}} anywhere in your app if you want to be fully strict compatible.

Going forward {{component will not accept strings.

The laziest thing you can do today is this:

{{#let (component (ensure-safe-component this.tool)) as |Tool|}}
  <Tool />
{{/let}}

which you probably don’t want to do, since to be more future proof, you’ll benefit from importing all of your possible components and creating a map to choose from like this:

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { assert } from '@ember/debug';

// import all your tools, they could come from anywhere, 
// doesn't have to be sibling (./)
import Hammer from './hammer';
import Drill from './drill';
import Shovel from './shover';

const TOOLS = {
  // map of:
  // lower-case-hyphenated/maybe/namespaced-name => ActualComponent
  hammer: Hammer,
  drill: Drill,
  shover: Shovel,
}

export default class CaseToolsComponent extends Component {
    @tracked tool;

    @action  
    toggleTool(toolName) {
      let tool = TOOLS[toolName];
    
      assert(`Tool named ${toolName} is not known, and cannot be used`, tool);

      this.tool = tool;
    }
}

and then your template would look like this

{{#if this.tool}}

  {{#let (ensure-safe-component this.tool) as |Tool|}}
    <Tool />
  {{/let}}

{{/if}}

not that this still requires ensure-safe-component because property accesses are dynamic.

to have the least “weirdness” you can migrate all the way to GJS, which would look like this:

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { assert } from '@ember/debug';

// import all your tools, they could come from anywhere, 
// doesn't have to be sibling (./)
import Hammer from './hammer';
import Drill from './drill';
import Shovel from './shover';

const TOOLS = {
  // map of:
  // lower-case-hyphenated/maybe/namespaced-name => ActualComponent
  hammer: Hammer,
  drill: Drill,
  shover: Shovel,
}

export default class CaseToolsComponent extends Component {
    @tracked tool;

    @action  
    toggleTool(toolName) {
      let tool = TOOLS[toolName];
    
      assert(`Tool named ${toolName} is not known, and cannot be used`, tool);

      this.tool = tool;
    }

    <template>
        {{#if this.tool}}
            <this.tool />
        {{/if}}
    </template>
}

1 Like