I am trying to integrate ckeditor 5 in my vue js app.I have successfully added ckeditor but now i want to add some text at the cursor position in ckeditor on click of a button. So to achieve that i tried insertText method. But i am not able to get the editor instance in vue while adding the code as editor.model.change(...)
Uncaught ReferenceError: editor is not defined
Code =>
<template>
<ckeditor
id="custom"
ref="custom"
name="custom"
:editor="editor"
:config="editorCustomConfig"
v-model="message">
</ckeditor>
Add Text In Editor
</template>
<script>
import ClassicEditor from "#ckeditor/ckeditor5-build-classic";
export default {
name: "topbar",
},
data() {
return {
editor: ClassicEditor,
editorConfig: {
},
message:''
};
},
methods: {
addCodeInMsg(e){
editor.change( writer => {
const insertPosition = editor.model.document.selection.getFirstPosition();
writer.insertText( 'CKEditor 5 rocks!', { linkHref: 'https://ckeditor.com/' }, insertPosition );
} );
}
}
</script>
I don't know what i am missing while using the ckeditor component.Any helps would be appreciated.
I ran into the same issue with Vue3 and CKEditor 5. Here is what I did for anyone that stumbles across this and still needs an answer.
Reference: https://ckeditor.com/docs/ckeditor5/latest/framework/guides/architecture/editing-engine.html#model
Setup the editor:
<ckeditor id="templateEditor" :editor="editor" v-model="myModel.myProperty" #ready="onReady"></ckeditor>
In my case, I have a list of li tags and clicking on each would add some text to the editor.
<li><span class="clickable emailToken" #click="appendToken('{year}')">{year}</span> - current year</li>
Then add this block of code to your component code (I'm using TypeScript and the ClassicEditor - you should be able to replace ClassicEditor with the name of whichever editor you're using):
let editor = {} as ClassicEditor;
function onReady(useEditor: ClassicEditor) {
editor = useEditor;
}
function appendToken(token) {
editor.model.change(writer => {
writer.insertText(token, editor.model.document.selection.getFirstPosition());
});
editor.focus();
}
The ckeditor ready event passes the editor instance to the onReady function and stores that ready instance in editor. The appendToken function then uses that captured editor to make changes to the model.
forgot about this:
methods: {
addCodeInMsg(e){
this.editor.change( writer => {
const insertPosition = this.editor.model.document.selection.getFirstPosition();
writer.insertText( 'CKEditor 5 rocks!', { linkHref: 'https://ckeditor.com/' }, insertPosition );
} );
}
}
I was able to do this through the editor-object you get when the CKeditor is ready.
In the on-ready-event, you get a editor and then I save that one for using such actions.
source: https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/frameworks/vuejs.html#using-the-document-editor-build
Related
We a create a html content using Vue Editor and save the data and close the dialog after saving . how do we remove the html content in the editor after the dialog box is closed
Using this editor for writing a html document in Vuejs --
import { VueEditor } from 'vue2-editor'
You just need to reset data property which has been passed to "v-model" attribute, something like that:
<template>
<vue-editor v-model="content" />
</template>
<script>
export default {
data() {
return {
content: '<p>Sample</p>',
};
},
methods: {
save() {
// Closing dialog code
this.content = null; // or this.content = ''
},
},
};
</script>
I am using Laravel + Vue.js to create a SPA. In the SPA, I am creating a form where user can write markdown content in it and click button to submit it. I want to show an error toaster at the bottom right corner of the screen if the user didn't input any content when they clicked the send button.
I am using boostrap vue toast to implement the error toaster.
However, when I clicked the send button, the error toaster will just blink for 1 second and dissapear immediately. Also, the error toaster blinks at top left corner which is different from what I wrote in the code below.
The mixin that contains the method to invoke the toast:
ErrorMessage.vue
# ErrorMessage.vue
<script>
export default {
methods: {
showErrorMessage (msg) {
this.$bvToast.toast(msg, {
title: ["Error!"],
variant: "danger",
toaster: "b-toaster-bottom-right"
});
}
}
};
</script>
I imported the above mixin in this vue component ArticleForm.vue.
ArticleForm.vue
<template>
<form #submit.prevent="passArticleData">
<div id="editor-markdown-editor">
<div id="editor-markdown">
<div
id="editor-markdown-tag-input"
#click="toggleTagModal"
>
<ul v-if="insertedTags.length">
<li v-for="tag in insertedTags"
:key="tag.id"
class="tag"
>
{{ tag.name }}
</li>
</ul>
<span v-else>タグを入力してください</span>
</div>
<div id="editor-markdown-textarea">
<textarea :value="input" #input="update" ref="textarea"></textarea>
<div id="drop-here"></div>
</div>
</div>
<div id="editor-preview">
<article v-html="compiledMarkdown(input)" class="markdown-render" ref="articleHtml"></article>
</div>
</div>
</form>
</template>
<script>
import _ from "lodash";
import ErrorMessage from "./mixins/ErrorMessage";
import { markdownTable } from "markdown-table";
export default {
props: {
article: Object,
tags: Array
},
mixins: [ErrorMessage],
data () {
return {
articleToPass: {
title: "",
body: "",
isDraft: true
},
input: "",
tagsToPass: [],
insertedTags: [],
tagModalOpen: false
};
},
methods: {
update: _.debounce(function (e) {
this.input = e.target.value;
}, 300),
passArticleData (e) {
// Get title from input
try {
this.articleToPass.title = this.$refs.articleHtml.getElementsByTagName("h1").item(0).innerHTML;
} catch (e) {
this.showErrorMessage(["Body md can't be blank"]);
}
// Remove first line(title) from users' input
this.articleToPass.body = this.input.substring(this.input.indexOf("\n") + 1);
// tag id of written article
const tagIds = this.insertedTags.map(obj => obj.id);
this.tagsToPass = tagIds;
this.$emit("handle-new-data", this.articleToPass, this.tagsToPass);
}
}
Parent component of the above vue component:
ArticleCreate.vue
<template>
<div id="content-area">
<header-component></header-component>
<main id="editor-area">
<article-form :article="article" :tags="tags" #handle-new-data="postArticle"></article-form>
</main>
</div>
</template>
<script>
import HeaderComponent from "./Header.vue";
import ArticleForm from "./ArticleForm.vue";
export default {
data () {
return {
article: {
title: "",
body: "",
is_draft: true
},
tags: []
};
},
components: {
HeaderComponent,
ArticleForm
},
methods: {
postArticle (articleObj, tagsData) {
const data = { article: articleObj, tags: tagsData };
axios.post("/api/articles", data)
.then((res) => {
this.$router.push({ name: "article.show", params: { article: res.data } });
});
}
}
};
</script>
I tried:
changed this.$bvToast to this.$root.$bvToast (based on this issue)
downgrade my bootstrap version from 4.6.0 to 4.5.3 (based on this question)
I have spent trying to solve this but failed. Does anyone know why is this happening and how can I make it work? Please let me know if you need any extra information. Thank you in advanced.
As stated in this comment on stackoverflow that is usually a sign of a bootstrap version mismatch.
I was actually able to reproduce that issue and also fix it with rolling back to bootstrap v4
Broken with bootstrap 5
https://codesandbox.io/s/bootstrap-vue-toasting-broken-with-bootstrap-5-bqe2c
Working with bootstrap 4
https://codesandbox.io/s/bootstrap-vue-toasting-working-with-bootstrap-4-jk2jl
I have figured out the problem.
The problem is that bootstrap-vue css is not loaded properly in my project.
Just add
import "bootstrap-vue/dist/bootstrap-vue.css";
to app.js and it works perfectly fine now.
Thank you
I have a Vue page which loads an json array from an api and displays the content in a list of multiple s by using v-for.
If you focus on one of the textarea's or change the text a function automatically resize's the textarea to fit the content.
<div v-for="post in posts">
<textarea v-model="post.body" rows="1" #focus="resizeTextarea" #keyup="resizeTextarea"></textarea>
</div>
resizeTextarea(e) {
let area = e.target;
area.style.overflow = 'hidden';
area.style.height = area.scrollHeight + 'px';
}
With my limited Vue knowledge, I can't find a solution to automatically resize all textarea's after loading the data from the API. There is no #load on a textarea.
I was trying to reach the same goal with using a watcher on the data but it feels like a long workaround.
Anyone a descent solution? Thank you!
https://jsfiddle.net/oehoe83/c1b8frup/19/
One solution would be to create a component for your textarea element and then resize it in the mounted() hook. Here's an example using single-file components:
// CustomTextarea.vue
<template>
<textarea
v-model="value"
ref="textarea"
rows="1"
#focus="resize"
#keyup="resize"
>
</textarea>
</template>
<script>
export default {
props: {
value: {
type: String,
required: true,
}
},
mounted() {
this.resize();
},
methods: {
resize() {
const { textarea } = this.$refs;
textarea.style.height = textarea.scrollHeight - 4 + 'px';
}
}
}
</script>
Then in your parent:
<template>
<div v-for="post in posts">
<CustomTextarea v-model="post.body" />
</div>
</template>
<script>
import CustomTextarea from './CustomTextarea.vue';
export default {
components: {
CustomTextarea,
}
// etc.
}
</script>
Note: if you're using Vue 3, replace value with modelValue in the child component.
Alternatively you could use a watch like you suggested, there's nothing wrong with that. Something like this:
watch: {
posts() {
// Wait until the template has updated
this.$nextTick(() => {
[...document.querySelectorAll('textarea')].forEach(textarea => {
this.resizeTextarea({ target: textarea });
});
});
}
}
you can add the ref attribute :
<div id="app">
<div v-for="post in posts" ref="container">
<textarea v-model="post.body" rows="1"#focus="resizeTextarea" #keyup="resizeTextarea" ></textarea>
</div>
</div>
and add the following code at the end of mounted() :
this.$nextTick(()=>{
this.$refs.container.forEach( ta => {
ta.firstChild.dispatchEvent(new Event("keyup"));
});
});
I have a problem with the structure of my Vue.js components, but I don't understand what it is.
This is my app.js:
require('./bootstrap');
window.Vue = require('vue');
Vue.component('search', require('./components/Search').default);
Vue.component('graph', require('./components/Graph').default);
Vue.component('account', require('./components/Account').default);
Vue.component('design-theme', require('./components/DesignTheme').default);
const app = new Vue({
el: '#app',
data: {
},
methods: {
},
mounted: function () {
},
computed: {
}
});
So I don't have any methods or anything here, it is all in the four individual components. Each component works fine on its own, but when there is more than one in a page, something is off. Consider the Search.vue component. It simply sends an axios request to the server on keyup and shows a list of results:
<template>
<div class="search basic-search">
<input type="text" v-model="search_string" v-on:keyup="search" class="form-control search" placeholder="Search" value=""/>
<div :class="['search-results', active === true ? 'active' : '']">
<div class="search-result" v-for="result in search_results" v-on:click="submit(result.id)">
{{ result.name }}
</div>
</div>
</div>
</template>
<script>
export default {
data: function() {
return {
search_string : '',
search_results : [],
active : false
};
},
methods : {
search : function() {
const axios_data = {
_token : $('meta[name="csrf-token"]').attr('content'),
str : this.search_string
};
axios.post('/search', axios_data).then(response => {
if(response.data.success){
this.search_results = response.data.stocks;
this.active = true;
}
});
},
submit : function(stock_id) {
document.location = "/graphs/" + stock_id;
}
}
}
</script>
This works fine if the Graph.vue component is not included on the page. But, if it is, then search_str always remains empty, even though the search method is called on keyup.
There are no errors in the console - it's just that search_string remains empty when I type (as does the input field).
Perhaps I don't understand something on a conceptual level in Vue.js, but I can't figure out the relation here, or how to adapt the code to this situation.
This is the problematic part of the Graph component, if this is removed then the search works OK.
<vue-range-slider v-model="range" :min="0" :max="100" v-on:drag-end="updateRange"></vue-range-slider>
This is the component in question:
https://github.com/xwpongithub/vue-range-slider
Another interesting side effect (that I just noticed) is that, when this component is on the page, it is impossible to select text with the mouse. It seems like the component is somehow hijacking events, but I don't understand how.
As you identified correctly, the Vue Range Slider component is intercepting the events. There is an open merge request on their github page.
As suggested in the referenced issues, you should change this line in your package.json file:
"vue-range-component": "^1.0.3",
To this one:
"vue-range-component": "Anoesj/vue-range-slider#master",
However, since this is not the default branch of the plugin, you should frequently check the issue on github and switch back to the official branch as soon as the merge request passes.
I'm trying to create a custom component with VueJS & Element-UI and I'm getting a very annoying error when trying to enter data into the input field.
Below are the files & the contents related to the issue:
components.js file:
Vue.component('yetti-input', {
props: ['value'],
template: '<el-input ref="input" v-bind:value="value" v-on:input="parseValue($event.target.value)"></el-input>',
methods: {
parseValue (value) {
this.$emit('input', value)
}
}
})
index.vue file:
<template>
<div>
<div class="login-form">
<yetti-form>
<yetti-input v-model="login.email"></yetti-input>
</yetti-form>
</div>
</div>
</template>
<script>
export default {
data () {
return {
login: {
email: '',
password: ''
}
}
}
}
</script>
Error I'm receiving in the Console:
Please point out if I'm being a fool, however I cannot for the life of me figure out what is going on.
Cheers,
Tim
Okay, I solved my problem.
Interestingly, the $event is the input value being provided when using el-input.
Rather than have: v-on:input="parseValue($event.target.value)"
I removed target.value and I had my value.
v-on:input="parseValue($event)"
Not sure if I've done the wrong thing by VueJS here. However, this has resolved my issue.