javascriptember.jshandlebars.jsember-dataember-cli

How to bind actions to markup in ember template by {{{ }}} (Template helper)?


I am constructing a markup string with an onclick action as shown below.

    helpInfoMarkup: computed('levelInfo.name', function() {
       return '<a class="help-text" {{action "switchContext"}}' + get(this, 'levelInfo.name') + '</a>';
    }

My .hbs file

<div>{{{helpInfoMarkup}}}</div>

However this is not binding to the ember action and just appears as part of the markup and does not work? Any thoughts on this ?


Solution

  • This can not work because the glimmer rendering engine (which is used by ember) does not operate on strings. Instead your ember templates are compiled to a binary format during the build, and later at the runtime glimmer will directly produce DOM, not an HTML string.

    This means you can never pass a glimmer template around as a string. You could bundle the template compiler with your app (but you should not) to compile the template in the browser, but even then you can not use it to produce an HTML string representation because in this step you loose information (for example action binding). This is because these action bindings are never part of the HTML, but glimmer directly attaches event listeners to the DOM nodes during rendering. By using a HTML string and {{{...}}} you render plain HTML, not a glimmer template, and so this wont work.

    What you should do is move all your HTML to templates and use a component to embed it.

    The only other possibility is to utilize did-insert from ember-render-modifiers to manually attach an event. so you could do this:

    <div {{did-insert this.attachSwitchContextAction}}>{{{helpInfoMarkup}}}</div>
    

    and then in your component:

    @action
    switchContext() {
    }
    
    @action
    attachSwitchContextAction(elem) {
      elem.addEventListener('click', (...args) => this.switchContext(...args));
    }
    
    get helpInfoMarkup() {
      return '<a class="help-text" ' + get(this, 'levelInfo.name') + '</a>';
    }
    

    this way you work around the glimmer template engine altogether and fall back to manual DOM manipulation.