ckeditor 5 - How do you get the editor data? - ckeditor5

I have the following div:
<div name="content" id="editor">
I have the following script:
<script>
$(document).ready(function () {
// CKEDITORs
var myeditor = ClassicEditor
.create(document.querySelector('#editor'))
.then(editor => {
console.log(editor);
})
.catch(error => {
console.error(error);
});
});
const data = editor.getData();
const data = myeditor.getData();
</script>
From the documentation, I thought I would be able to do the following:
const data = editor.getData(); //fails with editor.getData is not a function
So added the myeditor var as above and tried this:
myeditor.getData();// also fails with same error.
How do I get data?

Usually the creditor data is obtained as
CKEDITOR.instances.editorid.getData();
But in CKEditor 5 there's no single global editor instance like old versions so we have to manually create a instance to hold the data globally to get the data when needed.
let editorinstance;
<script>
$(document).ready(function () {
// CKEDITORs
var myeditor = ClassicEditor.create(document.querySelector('#editor'))
.then
(editor => { editorinstance =editor;})
.catch(error => {
console.error(error);
});
});
const data = editorinstance.getData();
</script>

Related

testing if function was called inside vue sfc

I have the following files:
Component.vue
<template>
...
</template>
<script setup lang=ts>
...
const model = reactive([]);
watch(model, () => {
foo();
});
const foo = () => {
// do something
};
</script>
Component.spec.ts
describe("some test suite", () => {
it("calls 'foo' when model changes", async () => {
const wrapper = mount(Component);
const spyFoo = jest.spyOn(wrapper.vm, "foo");
wrapper.vm.model.push("bar");
expect(spyFoo).toHaveBeenCalledTimes(1);
});
});
What I want to test with this is, if foo() is called when i change the model. But when I do it like this I get the following error:
TypeError: object.hasOwnProperty is not a function
What is the proper way to implement test cases like that?

Vue3 Composition API: Computed value not updating

I am building a project with Nuxt and I need to know the size of the wrapper to adjust the grid setting
(I want a single line, I could still do this in pure CSS probably by hiding the items)
It's my first time using composition API & script setup
<script setup>
const props = defineProps({
name: String
})
const width = ref(0)
const wrapper = ref(null)
const maxColumns = computed(() => {
if (width.value < 800) return 3
if (width.value < 1000) return 4
return 5
})
onMounted(() => {
width.value = wrapper.value.clientWidth
window.onresize = () => {
width.value = wrapper.value.clientWidth
console.log(width.value);
};
})
</script>
<template>
<div class="category-preview" ref="wrapper">
...
</div>
</template>
The console log is working properly, resizing the window and refreshing the page will return 3, 4 or 5 depending on the size, but resizing won't trigger the computed value to change
What am I missing ?
In my test enviroment I had to rename your ref 'width' into something else. After that it did worked for me with a different approach using an event listener for resize events.
You can do something like this:
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'
const wrapperWidth = ref(0)
const wrapper = ref(null)
// init component
onMounted(() => {
getDimensions()
window.addEventListener('resize', debounce(() => getDimensions(), 250))
})
// remove event listener after destroying the component
onUnmounted(() => {
window.removeEventListener('resize', debounce)
})
// your computed property
const maxColumns = computed(() => {
if (wrapperWidth.value < 800) {
return 3
} else if (wrapperWidth.value < 1000) {
return 4
} else {
return 5
}
})
// get template ref dimensions
function getDimensions () {
const { width } = wrapper.value.getBoundingClientRect()
wrapperWidth.value = width
}
// wait to call getDimensions()
// it's just a function I have found on the web...
// there is no need to call getDimensions() after every pixel have changed
const debounce = (func, wait) => {
let timeout
return function executedFunction (...args) {
const later = () => {
timeout = null
func(...args)
}
clearTimeout(timeout)
timeout = setTimeout(later, wait)
}
}
</script>
<template>
<div ref="wrapper">
{{ maxColumns }} // will change after resize events
</div>
</template>

Axios/Vue/Nuxt - Find out when all API calls are finished

I want to make several API calls to get data into a component. I created a PostService.ts that looks like this:
const apiClient = axios.create({
baseURL: '/api/v1',
})
export default {
async getPosts() {
const { data }: { data: Post[] } = await apiClient.get('/posts')
// transform data ...
return data
},
async getTags() {
const { data }: { data: Tag[] } = await apiClient.get('/tags')
return data
},
async getComments() {
const { data }: { data: Comment[] } = await apiClient.get('/comments')
return data
},
}
This is my posts.vue:
<template>
<div>
<div v-if="dataLoaded">
content
</div>
<div v-else>
loading...
</div>
</div>
</template>
<script>
finishedApiCalls = 0
get dataLoaded() {
return this.finishedApiCalls === 3
}
created() {
PostService.getPosts()
.then((posts) => {
this.posts = posts
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
PostService.getTags()
.then((tags) => {
this.tags = tags
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
PostService.getComments()
.then((comments) => {
this.comments = comments
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
}
</script>
The key point is that I want to display a loading spinner as long as the data has not been loaded. Is it recommended to make the API calls from created()? What would be a more elegant way to find out when all calls are finished? It does not feel right to use the finishedApiCalls variable.
I recommend using Nuxt's fetch method along with Promise.all() on all your async PostService fetches:
// MyComponent.vue
export default {
fetch() {
return Promise.all([
PostService.getPosts().then((posts) => ...).catch((error) => ...),
PostService.getTags().then((tags) => ...).catch((error) => ...),
PostService.getComments().then((comments) => ...).catch((error) => ...)
])
}
}
Nuxt provides a $fetchState.pending prop that you could use for conditionally rendering a loader:
<template>
<div>
<Loading v-if="$fetchState.pending" />
<div v-else>My component data<div>
</div>
</template>
You can use Promise.all for this kind of requirements.
this.loading = true
Promise.all([PostService.getPosts(), PostService.getTags(), PostService.getComments()])
.then(values => {
let [posts, tags, comments] = values
this.posts = posts
this.tags = tags
this.comments = comments
//Here you can toggle your fetching flag like below
this.loading = false
})
You can use Promise.all(). This will wait till all resolves or if 1 fails.
With async / await you can make it "synchronous"
data() {
return {
loaded: false
}
},
async created() {
let [posts, tags, comments] = await Promise.all([PostService.getPosts(), PostService.getTags(), PostService.getComments()])
this.posts = posts;
this.tags = tags;
this.comments = comments;
this.loaded = true;
}

How to access Vue $refs in a plugin?

methods: {
async create () {
this.disableSubmit = true;
await this.$firestore
.collection('collectionName')
.add(this.item)
.then(() => {
this.$refs.createForm.reset();
this.$notify('positive', 'Item successfully created!');
})
.catch(error => {
this.$notify('negative', 'ERROR! Try again later!', error);
});
this.disableSubmit = false;
},
}
If I use the code above inside the methods property, then everything works fine, but I would like to access that ref from outside the Vue component, for example a plugin, but it gives me an error.
TypeError: "_this.$refs is undefined"
Even when I just import it as a function, the error is the same, so I would like to know how to access the ref outside vue?
Bellow is the code for my plugin, and I would also like to point that I am using the quasar framework.
export let plugin = {
install (Vue, options) {
Vue.prototype.$plugin = async (collection, item) => {
return await firestore
.collection(collection)
.add(item)
.then(() => {
this.$refs.createFrom.reset();
notify('positive', 'Booking successfully created!');
})
.catch(error => {
notify('negative', 'ERROR creating booking! Try again later!', error);
});
};
}
};
I hope my question makes sense, and thanks in advance for any help
you could pass the context of your component, to apply the reset form from your plugin:
// plugin declaration
Vue.prototype.$plugin = async (collection, item, ctx) {
...
ctx.$refs.createFrom.reset()
...
}
then when u call to your plugin from yours components can do it like this:
// your component
methods: {
myFunction () {
this.$plugin(collection, item, this)
}
}
this is the reference of the context of your current component that will be used inside of your plugin
for example:
Vue.component('my-form', {
methods: {
resetForm() {
console.log('the form has been reset')
}
}
})
Vue.prototype.$plugin = (item, ctx) => {
console.log('item passed:', item)
ctx.$refs.refToMyForm.resetForm()
}
new Vue({
el: '#app',
data: {
item: 'foo'
},
methods: {
submit() {
this.$plugin(this.item, this)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<my-form ref="refToMyForm"></my-form>
<button #click="submit">submit</button>
</div>

ag grid not retrieving data when mounted with vue using axios

I have this strange case when trying to retrieve data from mongoDB using axios not showing on grid. It should be already successful given the data can already loaded into the view (already tested it), but it's nowhere inside beforeMount, mounted, or ready hook.
I already tried with
this.gridOptions.onGridReady = () => {
this.gridOptions.api.setRowData(this.ticketData)
}
but only yields partial success (unreliable),
here's a code snippet to show what I mean,
<template>
<div class="ticketing">
<ag-grid-vue style="width: 100%; height: 350px;"
class="ag-fresh"
:gridOptions="gridOptions"
>
</ag-grid-vue>
{{testData}} <!--testData can be loaded-->
<input type="button" #click.prevent="showData" value="test"> </div>
</template>
<script>
//import stuff
//header and url stuff
export default {
//component stuff
data () {
return {
gridOptions: null,
ticketData: [],
testData: [] // only for testing purpose
}
},
methods: {
showData () {
console.log('data shown')
this.testData = this.ticketData // this is working
}
},
beforeMount () {
var vm = this
axios.get(ticketingAPIURL, {'headers': {'Authorization': authHeader}})
.then(function (response) {
vm.ticketData = response.data
}) // this is working
.catch(function (error) {
console.log(error)
})
this.gridOptions = {}
this.gridOptions.rowData = this.ticketData // this is not working
this.gridOptions.columnDefs = DummyData.columnDefs
}
// mount, ready also not working
}
</script>
To be more specific, I still can't determine what really triggers onGridReady of ag-grid in conjunction with Vue component lifecycle, or in other words, how can I replace button to show testData above with reliable onGridReady/Vue component lifecycle event?
You define vm.ticketData and after you call it like this.ticketData
You can change it by: this.rowData = vm.ticketData
You are setting this.gridOptions.rowData outside of the axios callback, so this.ticketData is still empty.
Set it inside the callback:
mounted() {
var vm = this
axios.get(ticketingAPIURL, {'headers': {'Authorization': authHeader}})
.then(function (response) {
vm.ticketData = response.data
vm.gridOptions = {}
vm.gridOptions.rowData = vm.ticketData
vm.gridOptions.columnDefs = DummyData.columnDefs
})
.catch(function (error) {
console.log(error)
})
}
it is due to overlapped intialization between axios, ag-grid, and vue.
after much tinkering, I am able to solve it with using Vue's watch function:
watch: {
isAxiosReady(val) {
if (val) {
this.mountGrid() // initiate gridOptions.api functions
}
}
}