Instanciating extended VueJS components with props data within - javascript

Hello everyone,
Let me give you a little bit of context about my problem :
I'm trying to create a system that can add charts on a page with the simple push of a button.
These charts are going to contain elements from a MySQL database.
I have a Chart.vue file that contains the template for a single HighChart element. It also contains a prop :
export default {
name : "Chart",
props : ["tableToDisplay"],
And then I have my main vue that is named "Test.vue".
It imports the Chart.vue from the component folder and then I basically just need to write :
<Chart :table-to-display="tableToDisplay"/>
to create an instance of a chart of the table contained within the variable : this.tableToDisplay.
But this is not what I want to do : I want to create a chart with the push of a button, so I made some changes :
<div>
<button #click="createGraph">Add a graph</button>
<Chart :table-to-display="tableToDisplay"/>
</div>
And with it, I created the method:
createGraph(event)
{
let ChartClass = Vue.extend(Chart)
console.log(ChartClass)
let graphInstance = new ChartClass({
props:{
"tableToDisplay": this.tableToDisplay
}
})
graphInstance.$mount()
let divContainer = event.target.parentElement
divContainer.append(graphInstance.$el)
},
That is where my problem is.
Within that method, I want to send a table to display to the newly created Chart, but it seems that I can't manipulate the props value in that way.
I thought that this piece of code was the solution :
let graphInstance = new ChartClass({
props:{
"tableToDisplay": this.tableToDisplay
}
})
But It turns out that it is not.
When I click the button, an empty chart does appear but the prop "tableToDisplay" is undefined.
I looked at the console and I get a "[Vue warn]: Error in the mounted hook: "TypeError: ciphertext is null".
It doesn't matter if I put an argument or not in the ChartClass, I always have this error on the graphInstance.$mount() line.

First, I think you don't need to programatically instantiate your Chart components. A simple v-for loop will do the trick:
<template>
<Chart v-for="table of chartTables :table-to-display="table"/>
</template>
<script>
...
data() {
chartTables: []
},
methods: {
createChart() {
// Adding a new table to the array will create a new Chart component.
this.chartTables.push(this.tableToDisplay)
}
}
</script>
If this solution suits your needs, go ahead in that way!
That said, if you really need to instantiate a Vue component yourself, you have to use the propsData parameter to pass your props.
const instance = new ChartClass({
parent: this, // The parent is your current component
propsData: {
tableToDisplay: this.tableToDisplay,
},
})
let divContainer = event.target.parentElement
instance.$mount(divContainer)
The parent option is really important: it adds your component to the Vue component dependency tree. Without it, your component won't have inherited properties (such as the Vuex store, plugins etc.).

Related

Access data inside dynamic components from parent component in VueJS + Vuex

I created this demo to explain better my goal: https://codepen.io/Albvadi/pen/OJMgByR
Each button create a new alert creating a object inside the vuex store in the components array. With the component property I know the type of the component to render.
Each alert generate a random string in a data property inside the component.
How do I need to configure the connection with Vuex to obtain the data from the child alert component inside the global components array?
Thanks!
Finally, I solved with a computed property that gives me the parent item. I don't know if it is the best way or if there is a simpler one, but I think it is the correct one.
computed: Vuex.mapState({
getParentItem(state) {
return state.components[state.components.indexOf(this.index)];
}
}),
Demo solved: https://codepen.io/Albvadi/pen/wvMrpLv

vuejs How to detect data changes in a function from grand-child component when an array data changes from grand-parent component

I'm making a map interface to manage the map-related data. like link, nodes, vertex, and so on.
I'm facing the issue that I'm not sure how to detect changes in a grand-child component when data changes from grandparent component. Please advise me.
component tree:
grand parent: map container + tables to display map data(import map(child))
parent: map + leaflet-draw customized component (import edit-map(child))
(grand)child: leaflet-draw customized component
Using Vuex to share data
grandparent: get data(vuex), display, catch the updated data(completed) - ok, no problem
grand-child: add, edit, delete data. updated data will be sent to vuex. - ok, no problem
issue: Users can modify data also by using a table in grandparent.
problem: not sure how to detect changed data from grand-child component.
I can see the object values are changes automatically when I change data in the table... but I really have zero ideas on how to detect that changes in grand-child component...
let me know if you need more info.
Edited:
I've converted leaflet-draw vanilla js to grand-child component. All my codes are written in < .s .cript> </> above export default { data(), methods: {}, compueted:{} }. Only mounted() hook is used to display the component to parent(map) component. This is why I have no idea how to detect changes....

vue dynamic async component set props with javascript

I have some code similar to below where i am importing a component dynamically and then i want to set some props on it.
I dont have a <component></component> html tag as i am passing the component to a 3rd party plugin that will display the component inside their own component.
the below code works but then i have started seeing the following vue warning error in the console:
option "propsData" can only be used during instance creation with the `new` keyword.
structure of settings variable:
const settings = {
props:{
title: "new title"
}
}
component loading function:
function Load(compName, settings){
import("#/"+ compName +".vue").then((mod)=>{
let mycomponent = mod.default;
const props = settings.props;
mycomponent.propsData = {...props});
//3rd party package needs this format
let template = function(){
return {template : mycomponent};
}
//not shown - code that passes the template variable to the 3rd party package
});
}
is there another way to get the props into the component - or is it ok to ignore this warning?

Vue.js dynamic component with dynamic data

I'm trying to use the <component> element to dynamically display a chosen component. Each of these displayed components can take one of any number of data objects. Something like:
<div id="containers">
<component v-bind:is="currentView"></component>
</div>
var myVue = new Vue({
el:"#containers",
data:{
currentView: "my-component-one",
currentData: {...}
},
method: {
changeView: function(){
//change this.currentView
//change this.currentData
}
}
});
However, the Vue documentation says the v-bind:is attribute can be used to pass either a component name or the options object.
It is unclear how I would conditionally get an object of values for that component to use and also conditionally change which component is shown.
I am very green with using Vue (coming fresh off a knockout kick) so perhaps I am simply misunderstanding the intention of the component tag.
you can simply use v-bind
html
<component v-bind:is="currentView" v-bind="data"></component>
script
data()
{
return {
data: {
currentData: "example"
}
}
}
and it will pass currentData down to child. You can also add other properties along with it, including is.
If you need to change the component along with props, then you just change the data property, or whatever you want to call it.
https://codesandbox.io/s/7w81o10mlx
This example might help you understand it. https://jsfiddle.net/jacobgoh101/mLbrj5gd/
For passing Component Name
If the component is global, you can pass the component name to v-bind:is
for e.g.,
Vue.component('test1', {
template: `<div>test1</div>`
})
HTML
<component is="test1"></component>
For passing option
A Vue component is literally just a Javascript object with specific properties
for e.g.,
<component v-bind:is="{
template: `<div>test2</div>`
}"></component>

Vue V-Bind to Programmatically Created Instance

I follewed this instructions to create a Vue instance programmatically. I use this to dynamically add component instances in project by user events. My problem now it, that my component to initialize needs a model. I regular I would use it like this:
<my-component v-model="variable"/>
But now I create this component with this code snippet within another components methods section:
import MyComponent from '../MyComponent'
...
add () {
const Component = Vue.extend(MyComponent)
const instance = new Component()
instance.$mount()
document.getElementById('app').appendChild(instance.$el)
}
I know using a $ref here is better, but it must work globally, so I didn't know how to add it else to the DOM. But just as side note.
Now i need to give this instance a v-model binding. I already know how to define props or slots, but not a model. In the official docu they mention something for that. But to be honest I don't understand it and didn't get it work.
Can anybody tell me how I have to extend my code to define the model for this instance? Something like instance.$model = this.variable would be awesome. Thank u!
Finally I got some kind of workaround for this. I'm not aware if there is a better solution out there, but this works for me.
The MyComponent used this description to handle the v-model. By this is emit the change event for the parent component. So The idea is simply to pass the model variable as property, work in MyComponent on a copy of this variable and emit changes to the parent. To catch this change event I can add to my instance the following:
const Component = Vue.extend(EditWindow)
const instance = new Component({
propsData: { content: this.variable }
})
instance.$on('change', value => {
this.variable = value
})
instance.$mount()
document.getElementById('app').appendChild(instance.$el)
I guess this is pretty the same as Vue actually does in the background (maybe?). But after all it works and I'm happy. Of cause I'm open for the 'correct' solution, if such one should exist.

Categories