How to update TipTap content dynamically with Vue 3? - vue.js

I was able to set the initial value of the content from the editor with the value of a ref, but it doesn't update the content when the value of the ref changes.
Would anyone have an idea how to do it?
<editor-content
:editor="editor"
v-model:content="
/>
<script setup>
import { ref } from 'vue'
import { useEditor, EditorContent } from '#tiptap/vue-3'
import StarterKit from '#tiptap/starter-kit'
const generatedText = ref('<p>Hello!</p>')
/* editor */
const editor = useEditor({
content: generatedText.value,
extensions: [
StarterKit,
],
onUpdate: ({editor}) => {
generatedText.value = editor.getHTML()
}
})
</script>
Sorry if the question is too silly, I couldn't find an answer in the documentation.
Thank you for your help!

How will the generatedText be generated? Through tiptap?
The v-model of tiptap is the editor.content value. You could give it an initial state but if you want to change the editor.content after some interaction with the wysiwyg (like onFocus) you could use: setContent
Or maybe this helps:
Listening for changes

Related

SyncFusion TreeGrid ContextMenu doesn't seem to work with vue3

I'm trying to add a context menu to my SyncFusion TreeGrid, but nothing seems to happen.
This is what my file looks like:
<template>
<ej2-treegrid
:dataSource="tabla"
:contextMenuItems="contextMenuItems"
rowHeight="20px"
><e-columns>
<e-column field="nombre" headerText="Nombre"></e-column>
<e-column field="cantidad" headerText="Cantidad"></e-column>
<e-column field="vu" headerText="Valor Unitario" format="C"></e-column>
<e-column field="precio" headerText="Precio" format="C"></e-column>
</e-columns>
</ej2-treegrid>
</template>
<script setup>
import { ref, reactive, provide } from "vue";
import { TreeGridComponent as ej2Treegrid,
ColumnsDirective as eColumns,
ColumnDirective as eColumn,
ContextMenu,
} from '#syncfusion/ej2-vue-treegrid';
provide("treegrid", [ContextMenu]);
const contextMenuItems = ["Cancel"];
const tabla = reactive([{id: 1, nombre: "", cantidad: 2, vu: 2, precio: 2}]);
</script>
I'm not sure what I'm doing wrong. Maybe is the provide part? The documentation seems to be for vue2.
When I right click on the grid, the default context menu appears. I've had similar problems with editting.
We suspect that the context menu module is not injected properly. Based on your shared code example, we checked, and the provide part returns an error. So, we suggest you follow the code example below.
Your code:
provide("treegrid", [ContextMenu]);
Modified code:
provide: {
treegrid: [Page, ContextMenu, Edit,],
}
And we have prepared a simple Vue3 sample with context menu and editing. Please refer to the below sample:
https://www.syncfusion.com/downloads/support/directtrac/general/ze/Vue_3_context_menu-200508808

How to apply webpack tree shaking feature to Vue template‘s v-if directive?

Vue#2.6.14 Webpack#4.29.6
<template>
<module-a v-if="isClientA"></module-a>
<module-b v-else></module-b>
</template>
<script>
export default {
computed: {
isClientA() {
return process.env.VUE_APP_CLIENT === 'A'
}
}
}
</script>
In above case, if tree shaking feature works, module-b's code should not be included in the build package when VUE_APP_CLIENT is set to A. But it failed.
Is tree shaking not works in template syntax ?
Hope your help, thanks.
As far as I know tree shaking works on script level not on vue template. Maybe you should try to pass client component/module to template?
<template>
<div :is="clientModule"></div>
</template>
created() {
if(process.env.VUE_APP_CLIENT === 'A') {
this.clientModule = moduleClientA
}
else {
this.clientModule = moduleClientB
}
}
If I'm wrong you need also fix computed property because you use '=' instead of '=='.

Vue 3 use dynamic component with dynamic imports

I use Vue 3 and I have a dynamic component. It takes a prop called componentName so I can send any component to it. It works, kind of.
Part of the template
<component :is="componentName" />
The problem is that I still need to import all the possible components. If I send About as a componentName I need to import About.vue.
Part of the script
I import all the possible components that can be added into componentName. With 30 possible components, it will be a long list.
import About "#/components/About.vue";
import Projects from "#/components/Projects.vue";
Question
It there a way to dynamically import the component used?
I already faced the same situation in my template when I tried to make a demo of my icons which are more than 1k icon components so I used something like this :
import {defineAsyncComponent,defineComponent} from "vue";
const requireContext = require.context(
"#/components", //path to components folder which are resolved automatically
true,
/\.vue$/i,
"sync"
);
let componentNames= requireContext
.keys()
.map((file) => file.replace(/(^.\/)|(\.vue$)/g, ""));
let components= {};
componentNames.forEach((component) => { //component represents the component name
components[component] = defineAsyncComponent(() => //import each component dynamically
import("#/components/components/" + component + ".vue")
);
});
export default defineComponent({
name: "App",
data() {
return {
componentNames,// you need this if you want to loop through the component names in template
};
},
components,//ES6 shorthand of components:components or components:{...components }
});
learn more about require.context

Vue emit (did not listen the emitting event from the child)

I am a beginner and I tried vue emit event. But the event do not listen from the parent. Please Help Me!!
In App.vue
<app-header v-bind:somethings='name' #custom-event-name="setName"></app-header>
setName(childName){
this.name= childName;
}
In Body.vue
<button #click="changeName"> click me to change name </button>
changeName: function(){
this.$emit('custom-event-name', 'Some Value'); }
From what I'm seeing in your script section, you're not importing the Body component, which is where you are trying to emit an event.
Right now you have this:
<script>
import Header from './components/Header.vue';
export default {
components:{
'app-header': Header,
'app-body' : Body
},
data () {
return {
name: 'John',
}
},
methods: {
setName(payload) {
this.name = payload;
}
}
}
</script>
There isn't an import statement for the Body component, so your parent component doesn't know what Body is. To fix this you just need to add an import like you've done for Header. It might look like this: import Body from './components/Body.vue';
Now that the Body component is being used, you need to include it in your template. You'll do the same thing you did for app-header, and include a tag like this <app-body></app-body>. Finally, you need to add the event listener so the parent knows when to run setName. This gets added to the app-body tag, and will end up looking like this: <app-body #custom-event-name="setName"></app-body>

vue constructor not having local state

I have this code:
import Vue from 'vue'
import s from 'vue-styled-components'
import Test1x from './test1x'
export default Vue.extend({
name:'test1',
render(){
const Div=s.div`
`
const test1x1=new Test1x()
const test1x2=new Test1x()
const el=
<Div>
{test1x1.state.greeting}
{test1x2.state.greeting}
<button vOn:click={()=>test1x1.commit('change')}>change</button>
<button vOn:click={()=>test1x2.commit('change')}>change</button>
</Div>
return el
}
})
and test1x.js file is as follows:
import withStore from './withStore'
export default withStore({
state: {
greeting:'hola'
},
mutations: {
change(state){state.greeting='hello'}
}
})
and withStore.js file is as follows:
import Vue from 'vue'
export default ({ state, mutations }) => {
return Vue.extend({
data () {
return { state }
},
methods: {
commit (mutationName) {
mutations[mutationName](this.state)
},
},
})
}
Given that code, I assume each greeting will be changed by the corresponding button, separately, individually, but not, when I press a button all two greetings change. Anyone knows why? Thank you in advance.
And even more strange is that while at least code presented before is reactive, I mean, greeting change when pressing a button, code below it is not:
import Vue from 'vue'
import s from 'vue-styled-components'
import withStore from './withStore'
export default Vue.extend({
name:'test1',
render(){
const Div=s.div`
`
const Test1x=withStore({
state: {
greeting:'hola'
},
mutations: {
change(state){
state.greeting='hello'
}
}
})
const test1x1=new Test1x()
const test1x2=new Test1x()
const el=
<Div>
{test1x1.state.greeting}
{test1x2.state.greeting}
<button vOn:click={()=>test1x1.commit('change')}>change</button>
<button vOn:click={()=>test1x2.commit('change')}>change</button>
</Div>
return el
}
})
when pressing button nothing happens, greeting remains with hola instead of hello. Isn't that strange? Anyone knows why? Thanks again.
edit
thanks to #skirtle answer, I solved the issue doing this:
import Vue from 'vue'
import s from 'vue-styled-components'
import Test1 from './test1/test1'
import Test1x from './test1/test1x'
export default Vue.extend({
name:'app',
render(){
const Div=s.div`
`
const test1x1=new Test1x()
const test1x2=new Test1x()
//test1x1.commit('init')
test1x1.state={greeting:'hola'}
test1x2.state={greeting:'hola'}
console.log(test1x1.state)
const el=
<Div>
<Test1 test1x={test1x1}/>
<Test1 test1x={test1x2}/>
</Div>
return el
}
})
and test1.js being this:
import Vue from 'vue'
import s from 'vue-styled-components'
export default Vue.extend({
props:{
test1x:Object
},
name:'test1',
render(){
const Div=s.div`
`
const el=
<Div>
{this.test1x.state.greeting}
<button vOn:click={()=>this.test1x.commit('change')}>changes</button>
</Div>
return el
}
})
and test1x.js being this:
import withStore from './withStore'
export default withStore({
state: null,
mutations: {
change(state){state.greeting='hello'},
init(s){s={greeting:'hola'}
console.log(s)}
}
})
This works. The strange thing now is that if I uncomment test1x1.commit('init') I get an infinite loop, don't know why. If I then comment test1x1.state={greeting:'hola'} I don't get an infinite loop but I get an error that cannot read property greeting of null in test1.js. Anyone knows why this is happening? The thing is test1x1.commit('init') does not change the value test1x1.state, it remains null. Thanks.
Addressing the first problem first.
The problem starts here:
state: {
greeting:'hola'
},
The value of state points to a specific object. That object then gets passed around but at no point is a copy taken. The result is that both test1x1 and test1x2 will have the same object for state.
You can confirm this by adding a bit of console logging:
console.log(test1x1.state === test1x2.state)
The way Vuex handles this problem is to allow state to be a function, just like data:
state () {
return {
greeting:'hola'
}
},
Each time the state function is invoked it will return a new object.
As you aren't using Vuex you would need to ensure that you call the state function at the correct point to generate the relevant object. Something like this:
data () {
if (typeof state === 'function') {
state = state()
}
return { state }
},
So, to your second problem. I'm afraid I don't know what the problem is there. However, I very much doubt that 'when pressing button nothing happens'. It may not update the message but that isn't the same as 'nothing happens'. It should be relatively straightforward to add in some console logging at each stage and to establish exactly what does and doesn't happen. Once you've gathered all of that extra information about precisely what is happening it should be fairly simple to pinpoint precisely where the disconnect is occurring.
My suspicion would be that you've made some other changes to withStore that are causing this new problem. It could also be a file caching problem, so that the code you're running is not the code you think it is. Either way the extra logging should reveal all.
If you need further help with that then please update the question with the extra information gathered via console logging.
Update:
This is why the updated code causes an infinite rendering loop:
Inside the render function there is a call to test1x1.commit('init').
Inside commit it accesses the property this.state. This will add the property this.state as a rendering dependency for the component. It doesn't matter what the current value of this.state is, it's the property itself that is the dependency, not its current value.
On the next line it sets test1x1.state={greeting:'hola'}. This changes the value of the state property. This is the same state that has just been registered as a rendering dependency. As a rendering dependency has now changed the component will be re-added to the rendering queue, even though it hasn't finished the current rendering yet.
Eventually Vue will work its way through the rendering queue and get back to this same component. It will again call render to try to render the component. The previous steps will all occur again and so the component keeps being rendered over and over.
The bottom line here is that you shouldn't be initialising these data structures within the render function in the first place. There are various places you might create them but inside render does not appear to be appropriate based on the code you've provided.