Skip to content

Instantly share code, notes, and snippets.

@muthu32
Created July 10, 2019 11:50
Show Gist options
  • Save muthu32/c916b3862e6f1787372cf043040515a1 to your computer and use it in GitHub Desktop.
Save muthu32/c916b3862e6f1787372cf043040515a1 to your computer and use it in GitHub Desktop.
Vue TinyMce v5 in V-for two way editable
<div id="example">
<div class="heading">
<h1>TinyMCE v5 & Vue.js v2</h1>
<h4>An example of TinyMCE editor working in Vue.js with v-for and two way editable.</h4>
</div>
<input type="button" value="Remove Editor" @click="remove" class="btn btn-xs btn-danger" />
<input type="button" value="Add Editor" @click="add" class="btn btn-xs btn-success" />
<div v-for="(col,index) in cols">
<div class="heading"><h1>V-for Tinymce {{index}}:</h1></div>
<input type="text" v-model="cols[index]" class="form-control" />
<vue-tinymce :id="'test'+index" class="form-control required" v-model="cols[index]" :options="tinymceOptions"></vue-tinymce>
</div>
<div class="heading"><h1>V-for v-model:</h1></div>
<div v-for="(col,index) in cols">
<input type="text" v-model="cols[index]" class="form-control" />
</div>
<div class="heading"><h1>V-for Preview:</h1></div>
<div v-for="(col,index) in cols" class="container">
<h4>TinyMCE's Content {{index}} <sup><span class="label label-primary">LIVE</span></sup></h4>
<div v-html="col"></div>
</div>
<br /><br />
<div class="heading"><h1>Content Tinymce:</h1></div>
<input type="button" value="change Content" class="btn btn-xs" @click="change" />
<div class="row">
<div class="col-md-12">
<vue-tinymce :id="'test'" class="form-control required" v-model="content" :options="tinymceOptions"></vue-tinymce>
</div>
</div>
<div class="heading"><h1>Content v-model:</h1></div>
<input type="text" v-model="content" class="form-control" />
<div class="container">
<h4>TinyMCE's Content Preview<sup><span class="label label-primary">LIVE</span></sup></h4>
<div v-html="content"></div>
</div>
</div>
var tinymceOptions={
selector: 'textarea',
height: 300,
menubar: true,
plugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
'searchreplace wordcount visualblocks visualchars code fullscreen',
'insertdatetime media nonbreaking save table directionality',
'emoticons template paste textpattern imagetools codesample toc'
],
toolbar1: 'undo redo | insert | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
toolbar2: 'print preview media | forecolor backcolor emoticons | codesample',
valid_elements:"*[*]",
images_upload_url: 'postAcceptor.php',
images_upload_base_path: '/some/basepath',
images_upload_credentials: true,
images_upload_handler: function (blobInfo, success, failure) {
var xhr, formData;
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST', 'postAcceptor.php');
xhr.onload = function() {
var json;
if (xhr.status != 200) {
failure('HTTP Error: ' + xhr.status);
return;
}
json = JSON.parse(xhr.responseText);
if (!json || typeof json.location != 'string') {
failure('Invalid JSON: ' + xhr.responseText);
return;
}
success(json.location);
};
formData = new FormData();
formData.append('file', blobInfo.blob(), fileName(blobInfo));
xhr.send(formData);
},
image_title: true,
// enable automatic uploads of images represented by blob or data URIs
automatic_uploads: true,
// URL of our upload handler (for more details check: https://www.tinymce.com/docs/configure/file-image-upload/#images_upload_url)
images_upload_url: 'postAcceptor.php',
// here we add custom filepicker only to Image dialog
file_picker_types: 'image',
// and here's our custom image picker
file_picker_callback: function(cb, value, meta) {
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
// Note: In modern browsers input[type="file"] is functional without
// even adding it to the DOM, but that might not be the case in some older
// or quirky browsers like IE, so you might want to add it to the DOM
// just in case, and visually hide it. And do not forget do remove it
// once you do not need it anymore.
/*
input.onchange = function() {
var file = this.files[0];
// Note: Now we need to register the blob in TinyMCEs image blob
// registry. In the next release this part hopefully won't be
// necessary, as we are looking to handle it internally.
var id = 'blobid' + (new Date()).getTime();
var blobCache = tinymce.activeEditor.editorUpload.blobCache;
var blobInfo = blobCache.create(id, file);
blobCache.add(blobInfo);
// call the callback and populate the Title field with the file name
cb(blobInfo.blobUri(), { title: file.name });
};
*/
input.click();
}
};
Vue.component("vue-tinymce",{
template:'<textarea :id="id" :value="value"></textarea>',
props:["value","options","id"],
mounted:function(){
var vm=this,
options=$.extend(true, {}, tinymceOptions); // use jquery temporary
// make an deep copy of options;should not modify tinymceOptions
//options.selector=undefined;
//options.target=vm.$el; // use options.target instand of options.selector
options.selector = "#"+this.id;
var oldSetup=options.setup||function(){};
options.setup=function(editor){
console.log("setup");
//Decorate origni one
oldSetup(editor);
// Bind keyup
editor.on("change",function(e){
var value=editor.getContent();
vm.$emit("input",value);
});
editor.on("keyup",function(e){
var value=editor.getContent();
vm.$emit("input",value);
});
};
/*
Without dummy div I can't made up with re-entering the editor with sameid
*/
var $dummydiv = $( "<div id='"+this.id+"'></div>" );
$("BODY").append($dummydiv);
tinymce.init(options).then(function(editors){
vm.editor=editors[0];
});
},
beforeUpdate () {
if(this.value!==this.editor.getContent())
this.editor.setContent(this.value);
},
destroyed () {
tinymce.remove("#"+this.id);
$("div #"+this.id).remove(); //remove dummy div
console.log("Removed #"+this.id);
}
});
var vm=new Vue({
el:"#example",
data:{
cols: ['Data for col 1', 'Data for col 2', 'Data for Col 3', 'Lorem Ipsum Lorem Ipsum'],
content:"test content",
content1:"text1 content1",
tinymceOptions:tinymceOptions
},
methods:{
change: function(){
this.content="Hello World";
},
remove: function(){
this.cols.shift();
},
add: function(){
this.cols.push("Lorem Ipsum Lorem Ipsum Lorem Ipsum");
},
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.0.11/tinymce.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
body {
background-color: #5c4084;
padding: 50px;
}
.container {
padding: 40px 80px 15px 80px;
background-color: #fff;
border-radius: 8px;
max-width: 800px;
margin-bottom:10px;
}
.container .mce-panel {
border-radius: 4px;
overflow: hidden;
}
.heading {
text-align: center;
}
.heading h1 {
background: -webkit-linear-gradient(#fff, #999);
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
text-align: center;
margin: 0 0 5px 0;
font-weight: 900;
font-size: 4rem;
color: #fff;
}
.heading h4 {
color: #a990cc;
text-align: center;
margin: 0 0 35px 0;
font-weight: 400;
font-size: 24px;
}
.label-primary {
background-color: #5c4084;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment