Div with ContentEditable and two-way-binding

Hi, I am working on an Ember 3.28 component where the user can do some text formatting in WYSIWIG style. The Data comes from a model and consists of html. I am able to show the formatted HTML but I am not able to update the model after the user has finished his editing. I appreciate any help :slight_smile: THX Marco

what code have you tried so far?

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

export default class RichTextEditorComponent extends Component {
    @tracked editorContent='';
    
    @action
    setupEditor(element){
        element.innerHTML = this.args.initialContent || '';
        element.addEventListener('input', this.updateContent.bind(this));
    }
    
    @action
    execCmd(command){
        document.execCommand(command, false, null);
        document.getElementById('editor').focus();
    }
    
    @action
    updateContent(event){
        this.editorContent = event.target.innerHTML;
        if (this.args.onSave) {
            this.args.onSave(this.editorContent);
        }
    }
}
<div class="toolbar" contenteditable="false">
    <button {{on "click" (fn this.execCmd "bold")}} contenteditable="false"><b>B</b></button>
    <button {{on "click" (fn this.execCmd "italic")}} contenteditable="false"><em>I</em></button>
    <button {{on "click" (fn this.execCmd "underline")}} contenteditable="false"><u>U</u></button>
    <button {{on "click" (fn this.execCmd "insertUnorderedList")}} contenteditable="false"><b>Aufzählung</b></button>
</div>
<div id="editor" value={{{@initialContent}}} class="editor form-control ember-text-area ember-view" contenteditable="true" {{did-insert this.setupEditor}}></div>
1 Like

after playing around with this for a bit, I made: Glimmer Tutorial

tl;dr: you have to give up on making contenteditable a controlled input due to how the cursor resets, and instead just set the value once.

However, ember doesn’t gave a good primitive for doing things non reactively.

So this kind of a hack is needed:

const setContent = modifier((element, [initialize]) => {
  (async () => {
    // Disconnect from auto-tracking
    // so we can only set the inner HTML once
    await Promise.resolve();
    initialize(element);
  })();
});

Thx for your help. Where is modifier coming from ? In case of the ember addon ember-modifier I am not able to install it due to my ember version 3.24 :frowning:

Do you get an error?

Yes it seems that my Ember version does not fit the requirements of ember-modifier. Furthermore it needs additional dependencies like ember-auto-import.