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

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'

Related

How do you pass dynamic css classes in react?

I have an image where the CSS class on the image needs to be change dynamically. It is passed in dynamically from this object from the key "size":
export const decals = [
{ label: 'Nikola Tesla', img: `/images/decals/tesla.svg`, size: `decalMed` },
{ label: 'Tattoo Mom Heart', img: `/images/decals/ARF149.svg`, size: `decalSm`}
Into another component here:
import styles from ./Shirt.module.css';
<img key={decals[decal].label} src={decals[decal].img} alt={decals[decal].label} className={`${styles}.${decals[decal].size}`}/>
The className= is the issue. I've tried so many different ways to pass this class.
I've captured this (decals[decal].size) into a variable before passing to the image tag.
I've tried to use the style tag instead of className and created a variable to hold the class.
I've changed around the brackets. I've tried passing it into an outside div.
Nothing seems to work.
I'm wondering if anyone can clue a react newbie in. Thank you!
if I am not wrong, you trying to dynamically manage classes that are passing through to the image element.To do that, I recommend you to use "classnames"
https://www.npmjs.com/package/classnames
With this package, you can control classNames with variables in your component.
I will try explain usage with a little example.
Your style file like
.yourParentClass {
&.decalMedClass {
// your styles for this class
}
&.decalSmClass {
// your styles for this class
}
}
your component file like
import Style from '../style.scss'; // Your classes
var classNames = require('classnames/bind'); // classname package
const cx = classNames.bind(Style);
// your code
render() {
return (
<img className={
cx({
yourParentClass: true,
decalMedClass: this.state.sizeMed,
decalSmClass: !this.state.sizeMed,
})
}
/>
);
}
Basically, you telling that which class gonna be active.
Please do not stick with my example, there are good examples on the npm page.

Programmatically instantiate vuetify-components

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

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}
/>

How to avoid Vuetify overrides default CSS

Summarize the problem
I am trying to implement the Vuetify to a part of an existing project. But after I added Vuetify to the Project. I found out that the "default" CSS styles for like input field, select are changed. And it makes those input field and select all look like plain text rather than input field and select.
Because I only want to implement the Vuetify for a part of the project, so it is bad that the Vuetify overrides the "default" CSS Rules.
I am looking for a way to implement the Vuetify for a part of an existing project. But the rest of the project should be rendered as normal (just with default CSS, not my own CSS).
To make the Qustion more clair, I will put an example which shows two selects. The first one is made with Vuetify <v-select> and the second one is made with normal HTML code <select>
Provide background and tell us what you've already tried
I have already tried to put custom CSS rules for input field and select after the Vuetify Script and Vuetify CSS link. But Vuetify still overrides my custom CSS-Styles.
Show your code
HTML PART:
<div id="app">
<div>Vuetify select:</div>
<v-select
:items="items"
>
</v-select>
<hr/>
<div>
<div>Normal select:</div>
<select>
<option value="0">test1</option>
<option value="1">test2</option>
<option value="2">test3</option>
</select>
</div>
</div>
JS PART:
new Vue({
el: '#app',
data() {
return {
item: null,
items: [
{
text: "a"
},
{
text: "b"
},
{
text: "c"
},
]
}
}
})
Describe expected and actual results
I expected that I can use Vuetify for some parts of this project. But in the meanwhile, the rest of the project should be acting just like normal (with default CSS).
This is caused by the Vuetify reset styles (src/styles/generic/_reset.scss).
There is also an issue for that problem: https://github.com/vuetifyjs/vuetify/issues/8530. You can find there a little postcss hack from mkalus that isolates necessary styles via the wrapper (adding the prefix class to selectors).
In my case I just needed to remove some element selectors, excluding those that I added myself. So this is my variation of mkalus's solution with the postcss-filter-rules plugin.
I used the filter option to filter selectors. Its description:
Selectors are kept if the function returns true, otherwise they are removed.
And the keepAtRules option to prevent unnecessary deletions:
By default, #font-face and any empty at-rules (after filtering) are removed. To keep specific at-rules, provide an array of names to this option. To keep all at-rules, use the value true.
vue.config.js (Vue CLI 4):
const autoprefixer = require('autoprefixer')
const filterRules = require('postcss-filter-rules')
module.exports = {
/* ... */
css: {
loaderOptions: {
postcss: {
plugins: [
filterRules({
filter: (selector) => {
const re = new RegExp(/^(html|body|\*|ul|ol|select|small)(\W|$)/, 'i')
const exception = '.vue-global'
return !re.test(selector) || selector.includes(exception)
},
keepAtRules: true
}),
autoprefixer
]
}
}
}
/* ... */
}
Now that I've removed some Vuetify reset styles, I can still style html and other elements like this:
html.vue-global
font-size 16px
You can try adding a normalize css file to your project and the vuetify styling should only apply to when you want it to, i use this

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