Programmatically instantiate vuetify-components - javascript

I am trying to programmatically instantiate vuetify-components and add them to the DOM. With simple components like a v-card or v-dialoge it works fine, but it does not work with v-data-tables.
I created a codesandbox to showcase the problem: https://codesandbox.io/s/throbbing-butterfly-4ljhx?file=/src/components/MainWindow.vue
Look at the errors in the console when clicking the button to add a Table.
Can anyone help me out on how to add a table and why it throws these TypeErrors? Thanks!
Btw. I am following this guide: https://css-tricks.com/creating-vue-js-component-instances-programmatically/

You need to pass vuetify instance into extended Vue constructor same way as you do when instantiating main Vue instance...
MainWindow.vue
<template>
<v-app>
<v-btn #click="addTable" color="red">Generate Data-Table</v-btn>
<hr>
<v-btn #click="addCard">Generate simple Card</v-btn>
<v-container ref="mainContainer"></v-container>
</v-app>
</template>
<script>
import Table from "./Table.vue";
import Card from "./Card.vue";
import Vue from "vue";
import vuetify from "../plugins/vuetify";
export default {
name: "mainWindow",
components: { Table, Card },
methods: {
addTable() {
var ComponentClass = Vue.extend(Table);
var instance = new ComponentClass({ vuetify });
instance.$mount();
this.$refs.mainContainer.appendChild(instance.$el);
},
addCard() {
var ComponentClass = Vue.extend(Card);
var instance = new ComponentClass({});
instance.$mount();
this.$refs.mainContainer.appendChild(instance.$el);
}
}
};
</script>
<style>
</style>
Note: This is not recommended way of using dynamic components in Vue!
From the linked article:
In my case, I don’t know which component to insert in the template and also where to insert it. This information is only available at runtime
This is useful only when you do not know "where". If you know "where", "which" part is easily solvable with Dynamic Components

Related

this.$refs empty with Vue 3 Options API

In Vue 2 I used to be able to access a property on component children (rendered inside a v-for loop using this.$refs and a dynamically-assigned :ref).
The same code in Vue 3 fails, and when I log out this.$refs the object is empty.
Here I'm wanting to access an 'isOrderable' property on all children. The problem appears to be with :ref="product.id" being a variable. If I change it to ref="foobar" then I do get the last child in this.$refs.foobar. But it vue2 me an array back containing all children components.
<script>
import productItem from "./Product.vue";
export default {
props: ["products"],
components: {
'product-item': productItem
}
methods: {
addAllProducts() {
const orderable = this.products.filter((p) => this.$refs[p.id][0].isOrderable);
...
}
}
}
</script>
<template>
<form>
<div v-for="product in products" :key="product.id">
<product-item :product="product" :ref="product.id" />
</div>
<button #click="addAllProducts">Add All</button>
</form>
</template>
Obviously something changed in vue3 but I can't find any info about it. There's plenty of info on this.$refs, and but it all has to do with accessing refs from the composition API.
Any help appreciated.
In vue 3 they change how refs work with arrays, now you need to pass a function and have a state on your data to keep track of your refs https://v3-migration.vuejs.org/breaking-changes/array-refs.html#frontmatter-title.
I don't know how your code is structured but maybe there is a better solution to your problem than using refs, if the logic that toggles if a product-item is orderable lives inside the product-item component you can have an event that emits when the orderable value is changed an update an array of orderableProducts with the id of each product, you can even use that in a v-model with the multiple v-models options of vue3. in that way you don't need to hold a reference of the dom just to filter by the ones that are orderable.

How can I add a react Component To the DOM with jquery?

This is prob a super easy question but, I want to add components to a grid with react and jquery.
gridGame is a black 100px by 100px square and I want to add items into it. Im using rows (a variable) witch is a number from 1-9 to and sumbing it in to the gridGame-${rows} as seen here,so it can auto update the correct row to join. value should be: gridGame-${row} (what ever number row is from 1-9) and then I want to add a component inside gridGame called <Test /.> (witch is declared up-top in unseen parts of the code).
The function below has jquery that I thought would work in this situation:
function = (rows) => {
console.log(`joining ${rows}`)
let value= $(`#gridGame-${rows}`);
value.append("<Test />");
value.css("background-color", 'brown');
$(".create-coinflip-box").css("display", "none");
}
The css background change works but the value.append does not display the react component.
Here is the React Component inside the <Test /.>:
import React from 'react';
import StatusIcon from './img/image.png';
import Player1Icon from './img/image.png'
class newGame extends React.Component {
render() {
return(
<div>
HELLO
</div>
);
}
}
export default newGame;
I honestly have 0 idea on how this doesn't work.
Thanks for the help :)
This answer was from David in the comments. The problem for me was I was trying to render a component through a button and not the actual render its self.
The solution is to instead make the button change the state witch Updates the React DOM. Then just have a if statement that checks the state then display the component.
His comment explains much better and fixed the problem for me.

How to force Antd to append element as child element of div React renders to instead of to HTML body?

Edit:
I figured it out & posted answer below.
Original Question
I am trying to create a completely compartmentalized web application within a shadow-dom and I've been using Antd components and ran into the issue where Antd is appending drop-down options into the body tag instead of as a child of the element that React is rendering into.
To isolate this issue I've removed everything outside of just React.render & a single Antd element which still does the same thing.
I then used a different component, "react-date-picker", which works how I had hoped Antd did, where the component renders as a child of the div specified for react.
Unfortunately, Antd rendering to the body of the HTML instead of as a child makes using shadow-root pointless.
Essentially my question is:
Is this Antd's normal functionality? If not then what might I be screwing up to have this happen? If so then is there a built-in Antd option that I'm missing that will render Antd options as child elements? If that option doesn't exist within their libraries, is there a way for me to force Antd to render as a child of my shadow-root node?
Here is what I'm using to render the Antd DatePicker component:
import ReactDOM from 'react-dom';
import React from 'react';
import DatePicker from 'antd/lib/date-picker';
ReactDOM.render(<DatePicker/>, document.getElementById('entry-fields'));
Before clicking on the Antd date picker:
After clicking on it, drop down options are appended to <body> and not <div id="entry-fields>:
Here is what I'm using to render the react-date-picker component to demonstrate the functionality I expected / need:
import ReactDOM from 'react-dom';
import React from 'react';
import DatePicker from "react-datepicker";
class Example extends React.Component {
state = {
startDate: new Date()
};
handleChange = (date: any) => {
this.setState({
startDate: date
});
};
render() {
return (
<DatePicker
selected={this.state.startDate}
onChange={this.handleChange}
/>
);
}
}
ReactDOM.render(<Example/>, document.getElementById('entry-fields'));
Before clicking on the react-date-picker date picker:
After clicking on the react-date-picker date picker (the drop down options are appended as children of the element react is rendered onto):
Basically I expected Antd to render its options encapsulated within the React rendered into <div></div> but it is instead appending elements on the <body></body>.
I'm relatively inexperienced in the web-dev domain and have resorted to asking a question here after way too much time trying to find the answer on my own. I am getting extremely frustrated in web-dev in general where any question seems to yield hundreds of irrelevant medium blog posts that are not useful in any capacity... assuming that it's not just me not knowing what to search for yet to find the answers I need which could very well be the case.
Really appreciate any help in advance.
Not sure how I managed to miss this but Antd has a parameter called "getCalendarContainer" which if left blank will render options into the body of the document but if supplied with the correct parameters will render the child elements into the container of your choosing.
Going off this example: https://react-component.github.io/calendar/examples/getCalendarContainer.html
I got it working by adding this function to my component:
getCalendarContainer()
{
return this.d || document.getElementById('calendarContainer');
}
and adding this to the component in JSX:
<div id="calendarContainer" ref={n => (this.d = n as HTMLDivElement)} >
<DatePicker onChange={EntryFields.onDateChange} getCalendarContainer={this.getCalendarContainer}/>
</div>
and initializing the div tag to reference it in the component's constructor like this:
private d: HTMLDivElement;
constructor(props: any)
{
super(props);
this.d = document.createElement("div");
...
It's also worth noting that the above will not work immediately when using shadow-DOM since you need to access the node that the shadow-DOM is a child to and then use getElementById().
Something along these lines (but probably better written I hope lol)
getContainer() {
let elem = null;
let shadowContainer = document.getElementById('entryFieldsShadow') as HTMLInputElement;
if (shadowContainer != null) {
let shadowDom = shadowContainer.firstElementChild;
if (shadowDom != null) {
let shadowRoot = shadowDom.shadowRoot;
if (shadowRoot != null) {
elem = shadowRoot.getElementById("entryFieldsContainer")
}
}
}
return elem || this.d;
}
where the JSX with react-shadow's shadow root is included looks like this:
return (
<div id="entryFieldsShadow">
<root.div>
<div>
<div id="entryFieldsContainer"/>
<style type="text/css"> #import "static/1.css"; </style>
<Layout>
<Content>
{this.RowCols()}
</Content>
</Layout>
</div>
</root.div>
</div>
)
This solve my problems
<DatePicker
{...}
getCalendarContainer={triggerNode => triggerNode.parentNode}
/>

Vue-Multiselect looks like zero width text input and has no styling

I'm trying to figure out how to set up Vue-multiselect component on Laravel 5. Until now i've got a component called register.vue
and here is the part of the code where i need the component (I won't put all the code here becase it'd be too large.
<b-row align-v="center">
<multiselect
v-model="selected"
:options="options">
</multiselect>
</b-row>
Here's where i included the component in the script section
import Multiselect from 'vue-multiselect';
export default {
components: { Multiselect },
data(){
return {
selected: null,
options: ['list', 'of', 'options'],
}
}
Nevertheless, what i'm obtaining is a weird component like this one
This is how it looks when the focus is not in the component
And when the focus is in the component it only shows an input field with some bullet list which the elemtents of the array with the options are.
Does somebody know what is going on in my app? I've been a lot of time checking if there's some missed css file or stuff like that but everything seems to be OK, except that part, my component seems not be loading any style.
Thanks a lot everyone.
I noticed you included the b-row part which lets me know you have Bootstrap Vue, considering you have that whole library and all of it's CSS included (it may even contain a multiselect component of its own or use select2). It's most likely that the two stylesheets are messing with each other or one is causing the other not to init correctly or at the proper time.
I found this issue here which seems possibly like it could help you withchecking your styling. https://github.com/shentao/vue-multiselect/issues/718
Also I would get rid of all the other Vue components off that page for a minute and load in Vue Multiselect by itself exactly according to their documentation. That will let you narrow it down to the problem being a conflict between another Vue component or Laravel / when you are starting up Vue multiselect.
I use it in a PHP / Vue project currently this is my working setup with Buefy framework.
I made my own Vue component as a wrapper for multiselect which lets me scope the CSS to just its specific HTML.
<template>
<multiselect
v-model="value"
:options="options"
:placeholder="placeholder"
label="label"
track-by="value"
#input="inputChanged" />
</template>
<script>
import Multiselect from 'vue-multiselect';
export default {
components: { Multiselect },
props: {
options: {
type: [Array],
default() {
return [];
}
},
savedValue: {
type: [Array],
default() {
return [];
}
},
placeholder: {
type: [String],
default: 'Select Option...'
}
},
data() {
return {
value: null
};
},
mounted() {
this.value = this.savedValue;
},
methods: {
inputChanged(selected) {
this.$emit('selected', selected.value);
}
}
};
</script>
<style scoped>
#import '../../../../../node_modules/vue-multiselect/dist/vue-multiselect.min.css';
</style>
Then you can call the component from another file kind of like this.
<select-input
:saved-value="artistSelected"
:options="artistOptions"
:placeholder="'Choose an existing artist...'"
#selected="artistChanged"
/>
Scoping the CSS like I did will help other things from not messing with your styling.
The problem occurs because of the wrong component import statement.
In my case, the following statement gave the correct behaviour:
import Multiselect from 'vue-multiselect/src/Multiselect'

Implement Jsignature within vuejs component

I'm new to Vue and trying to implement Jsignature within a 'custom' Vuejs component.
My solution is based on: https://v2.vuejs.org/v2/examples/select2.html
It should be straight forward however I don't get it working, the solution I got so far results in the following error:
'Jsignature' is defined but never used
import Jsignature from '../../lib/jsignature
The component containing the signature.
<template>
<div>
<app-signature></app-signature>
</div>
</template>
<script>
import Signature from './signature/Signature.vue'
export default {
components: {
appSignature: Signature
}
}
</script>
The signature component.
<template>
<div id="signaturecanvas"></div>
</template>
<script>
import Jsignature from '../../lib/jsignature'
export default {
data () {
return {
signature: ''
}
},
methods: {
initial () {
var element = ('#signaturecanvas')
element.Jsignature.jSignature()
}
},
created () {
this.initial()
}
}
</script>
<style></style>
Never worked with Jsignature but I suppose you use it as a jquery plugin.
The issue you have is right at the this.$signaturecanvas. This is somehow a wrong way to get the div via jQuery.
var element = ("#signaturecanvas") or var element = $(this.$el) if you want to selected the whole component. $el refers to the identifier of the current vue component instance and basically is the first tag from the component. Choose the appropriate way depending on what you want to select and you should get it working.
Instead of importing JQuery and JSignature, I made the choice to use signature pad. https://github.com/szimek/signature_pad
This 'plug-in' is in native javascript/html5 which makes my application less constraint to external libraries like JQuery.

Categories

Resources