DEV Community

Sheary Tan
Sheary Tan

Posted on

How to reuse your Trix WYSIWYG editor with Vue?

When adding a WYSIWYG editor to your site, have you considered:

1) Which library to use?
2) How to implement one?
3) What if I have more than 2 editors I have to add?
4) Could I make them reusable?

Yes you can.

Here I am using the Trix editor from Basecamp.

Have a look at the doc and install the library before getting started!

Before making it reusable, let's quickly set the editor up first.

Getting Started

First, let's add the editor tag like what the doc has mentioned:

<template>
   <div>
       <input id="x" type="hidden" name="content">
       <trix-editor input="x"></trix-editor>
   </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Now you've got a nice editor. But it would be great to store the text you've typed in somewhere. Let's create a two-way binding on the form input:

<template>
   <div>
       <input id="x" type="hidden" name="content" v-model="trixText"> <!-- new code here -->
       <trix-editor input="x"></trix-editor>
   </div>
</template>

<script>
import Trix from 'trix';
export default {
   data() {
      return {
          trixText: '' // new code here
      }
   }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Listen to the changes on the Trix editor

From the doc:

The editor emits several events which you can use to observe and respond to changes in editor state.
More of that can be found here.

Here we will be using the trix-change listener.

<template>
   <div>
       <input id="x" type="hidden" name="content" v-model="trixText">>
       <trix-editor input="x"></trix-editor>
   </div>
</template>

<script>
export default {
   data() {
      return {
          trixText: ''
      }
   },
   methods: {
       setTextToTrix: function() {
           this.trixText = document.getElementById("x").value; 
       }
   }
   mounted() {
       document.addEventListener("trix-change", this.setTextToTrix); // Listen to the changes on the editor
   }
}
</script>
Enter fullscreen mode Exit fullscreen mode

And remove the event listener once done.

<!-- TrixComponent.vue -->
<script>
export default {
 ..
  mounted() {
    document.addEventListener("trix-change", this.setTextToTrix);
  },
  beforeDestroy: function() {
    document.removeEventListener("trix-change", this.setTextToTrix); // New code here
  }
};
</script>

Enter fullscreen mode Exit fullscreen mode

Make it reusable

So now you've got your basic editor set up! It's time to make it reusable.

The secret to make the trix-editor reusable:

The id on the tags

<input id="x" type="hidden" name="content" v-model="trixBody"> <!-- notice the id? -->
<trix-editor ref="trix" input="x"></trix-editor> <!-- notice the id? -->
Enter fullscreen mode Exit fullscreen mode

That means we can create a unique id for each component that is using TrixComponent.vue.

To demonstrate this, let's have 2 components: ComponentA.vue and ComponentB.vue using the same editor that you just created.

And let's work on ComponentA.vue first.

<!-- ComponentA.vue -->

<template>
  <div class="component-a">
    <h3>Component A</h3>
    <h6>
      Text:
      <span v-html="textA"></span> <!-- Data received will be in HTML format -->
    </h6>
    <TrixComponent></TrixComponent> <!-- TrixComponent -->
  </div>
</template>

<script>
import TrixComponent from "./TrixComponent";
export default {
  components: {
    TrixComponent
  },
  data() {
    return {
      textA: "",
    };
  },
  methods: {
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

We can pass the id as props from ComponentA.vue to TrixComponent.vue.

<!-- ComponentA.vue -->
<template>
  <div class="component-a">
    <h3>Component A</h3>
    <h6>
      Text:
      <span v-html="textA"></span>
    </h6>
    <TrixComponent :id="id"></TrixComponent> <!-- pass the id here -->
  </div>
</template>

<script>
import TrixComponent from "./TrixComponent";
export default {
  components: {
    TrixComponent
  },
  data() {
    return {
      id: "A" // Here we have an id 'A'
    };
  },
  methods: {
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

And we can assign the props(id) to the the input & the trix-editor.

<!-- TrixComponent.vue -->
<template>
  <div>
    <input :id="id" type="hidden" name="content" v-model="trixText"> <!-- Set the id according to the props -->
    <trix-editor :input="id"></trix-editor> <!-- Set the id according to the props --> 
  </div>
</template>

<script>
export default {
  props: ["id"], // Receive the id from the parent component
  data() {
    return {
      trixText: ""
    };
  },
  methods: {
    setTextToTrix(e) {
      this.trixText = document.getElementById(this.id).value; // REMEMBER TO UPDATE THE ID HERE TOO!
    },
  },
  mounted() {
    document.addEventListener("trix-change", this.setTextToTrix);
  },
  beforeDestroy: function() {
    document.removeEventListener("trix-change", this.setTextToTrix);
  },
};
</script>

Enter fullscreen mode Exit fullscreen mode

Getting data back from TrixComponent.vue

We will emit the data back from TrixComponent.vue to its parent component, which is ComponentA.vue

<!-- TrixComponent.vue -->
<template>
  <div>
    <input :id="id" type="hidden" name="content" v-model="trixText">
    <!-- updated here -->
    <trix-editor :input="id"></trix-editor>
  </div>
</template>

<script>
export default {
  props: ["id"],
  data() {
    return {
      trixText: ""
    };
  },
  methods: {
    setTextToTrix(e) {
      this.trixText = document.getElementById(this.id).value;
    },
    emitDataBackToComponent() {
      this.$emit("dataFromTrix", this.trixText); // Emit the data back to the parent component
    }
  },
  mounted() {
    document.addEventListener("trix-change", this.setTextToTrix);
  },
  beforeDestroy: function() {
    document.removeEventListener("trix-change", this.setTextToTrix);
  },
  updated() {
    this.emitDataBackToComponent(); // Fire the event whenever there's an update
  }
};
</script>

Enter fullscreen mode Exit fullscreen mode

And ComponentA.vue will then be able to receive the data from TrixComponent.vue

<!-- ComponentA.vue -->
<template>
  <div class="component-a">
    <h3>Component A</h3>
    <h6>
      Text:
      <span v-html="textA"></span>
    </h6>
    <TrixComponent :id="id" @dataFromTrix="getDataFromTrix"></TrixComponent> <!-- Receive the data back -->
  </div>
</template>

<script>
import TrixComponent from "./TrixComponent";
export default {
  components: {
    TrixComponent
  },
  data() {
    return {
      textA: "",
      id: "A"
    };
  },
  methods: {
    getDataFromTrix: function(data) {
      this.textA = data; // Assign the data back to the the variable
    }
  }
};
</script>

Enter fullscreen mode Exit fullscreen mode

It will be the same logic for ComponentB.vue: just pass a different id (maybe id: 'B'?) to TrixComponent.vue.

That's it!
You've now created a reusable trix editor :)

Here is the code example.

Happy coding 😊

Top comments (0)