Vuetify v-file-input rule for "csv only" - vuejs2

Good day,
I have a v-file-input like so:
<v-file-input
:rules="rules"
accept="text/csv"
label="Select a CSV file..."
></v-file-input>
Here is my rule that is supposed to detect when the file doesn't have a ".csv" extension:
rules: [
file => {
const pattern = /\.csv$/;
return (
!file || pattern.test(file) || 'File type should be csv.'
)
}
],
But it doesn't work. Whether I select a .txt, .csv, or .jar, the file-input thinks they're all invalid.
How can I get it only error on a non-csv file?
Thank you for any assistance.

Working codepen link https://codepen.io/manojkmishra/pen/vYgaZOy. This is working fine on windows, in case of any issue on Mac, please check file type in inputChanged(e) function and include that in if statement.
HTML:
<div id="app">
<v-app id="inspire">
<v-file-input accept=".csv" label="Select a CSV file..." v-model="ffile"
#change="inputChanged"></v-file-input>
<p v-show="show">
{{message}}
</p>
</v-app>
</div>
JS:
new Vue({ el: '#app',
vuetify: new Vuetify(),
data(){ return{ ffile:[], message:"",show:false} },
methods:{
inputChanged(e)
{ console.log('file type=',this.ffile.type)
if (this.ffile.type.match('application/vnd.ms-excel'))
{ console.log('csv matched');
this.show=false;
}
else{ console.log('not matched')
this.show=true;
this.message="this is not a csv file" ;
}
}
}
})

I hope you are well.
Part of the file type filtering configuration, mostly depends on the: primary MIME types, I share a complete list of these:
https://developer.mozilla.org/es/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types

Related

How to reset a v-input-file with vuetify?

I'm trying to reset my v-input-file when i click on a button. But, i don't see any function to do that.
I tried the following commands :
(upload_json is the name of my v-input-file)
this.$refs.upload_json.files=[]
this.$refs.upload_json.files=null
this.$refs.upload_json.value=""
this.$refs.upload_json.reset()
For all the commands, I had the same following error :
Uncaught TypeError: proxy set handler returned false for property
You can clear input by using this approach
<div id="app">
<v-app id="inspire">
<v-file-input
v-model="files"
accept="image/png, image/jpeg, image/bmp"
placeholder="Pick an avatar"
prepend-icon="mdi-camera"
label="Avatar"
></v-file-input>
<v-btn #click="restInput">Clear Input</v-btn>
</v-app>
</div>
main.js
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
files:{},
}),
methods:{
restInput(){
this.files = {}
}
}
})
you can use this.$refs.upload_json.value=null
You can clear input this way:
this.$refs.upload_json.internalValue = this.multiple ? [] : null
I tested it and it works. If you look at the source of the v-file component, that is how Vuetify developers clear the input when you click on the clear icon.
Try the following
this.$refs.upload_json.$refs.input.value = null
With Vue2, there was no warning as well!

VueJs: bind `v-on` on a custom component to replace an existing one

In order to ease the styling of my page, I'd like to create a bunch of mini components like, and exploit how attributes are merged in VueJs. So for example, here is a minimal js file also hosted on this JSFiddle:
Vue.component('my-button', {
template: '<button style="font-size:20pt;"><slot></slot></button>'
})
var app = new Vue({
el: "#app",
data: {
message: "world",
},
methods: {
sayHello: function () {
alert("Hello");
}
}
})
and then in my html I just want to use <my-button> instead of button:
<div id="app">
Hello {{message}} <my-button #click="sayHello" style="color:red;">Style works, but not click</my-button> <button v-on:click="sayHello" style="color:red;">Both works</button>
</div>
Unfortunately, it seems that attributes are merged, but not listeners, so it means that I can't do v-on:click on my new button... Any way to make it possible?
Thanks!
-- EDIT --
I saw the proposition of Boussadjra Brahim of using .native, and it works, but then I found this link that explains why it's not a great practice and how to use v-on="$listeners" to map all listeners to a specific sub-button. However, I tried, to just change my template with:
template: `<button style="font-size:20pt;" v-on="$listeners"><slot></slot></button>`,
but I get an error:
Vue warn: Property or method "$listeners" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option."
Here is the JSFiddle.
Your fiddle didn't work because you were using an old version of Vue, $listeners was added in Vue 2.4.0.
Here's a demo:
Vue.component('my-button', {
template: '<button style="color: red" v-on="$listeners"><slot/></button>'
})
new Vue({
el: '#app',
methods: {
sayHello() {
alert('Hello')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-button #click="sayHello">Custom Button</my-button>
<button #click="sayHello">Ordinary Button</button>
</div>

How to use vuetify's v-img inside v-html

I would like to use <v-img> of vuetify inside v-html to replace the standard <img>, i.e.
<div v-html="'<v-img src="some_image_url" />'">
I understand that v-html is meant for only standard HTML component, and I would like to know if there is anyway to use a custom component (such as <v-img>) inside v-html.
The example below uses some cheap and cheerful RegExps to do the parsing, nothing I would use in production code. My focus was on how to avoid using v-html rather than finding a reliable way to parse out the <img> tags.
The key thing I'm trying to demonstrate is how you can parse the text into chunks and then iterate over the chunks in the template to create v-img components. I've used a dummy component for v-img but the principle would be exactly the same for the real thing.
new Vue({
el: '#app',
components: {
vImg: {
template: '<strong>[<slot/>]</strong>'
}
},
data () {
return {
text: 'Something something <img src="somepath"> and <img src="otherpath">'
}
},
computed: {
chunks () {
const re = /(<img\s[^>]*>)/g
const text = this.text
const parts = text.split(re).filter(part => part)
return parts.map(part => {
if (part.match(re)) {
const matchSrc = part.match(/\ssrc="([^"]*)"/)
return {
src: matchSrc && matchSrc[1]
}
}
return part
})
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<template v-for="chunk in chunks">
<template v-if="typeof chunk === 'string'">{{ chunk }}</template>
<v-img v-else>{{ chunk.src }}</v-img>
</template>
</div>
I managed to solve it with v-runtime-template, which can be found here:
https://github.com/alexjoverm/v-runtime-template
Cheers!

v-autocomplete and setting user input as its value

I use vuetify in my project and need typeahead component. Sadly v-autocomplete implemented as combobox with filter, so it doesn't allow setting user input as v-model (or at least I can't find I way how to do so).
Could someone please explain me how to implement such functionality (maybe by another vuetify component)? I load items from server, but they are serve just as suggestions. Users need to have an ability to type and set any value they want to.
Here is a base example https://codepen.io/miklever/pen/oMZxzZ. The problem is that if I type any word that doesn't start with 'John' v-autocomplete clears it on blur. I've tried to set v-model manually and to add user input to array, but any of this methods has issues and doesn't work as expected.
<div id="app">
<v-app>
<v-content>
<v-container>
<p>Name: {{ select || 'unknown'}}</>
<v-autocomplete
:items="items"
:search-input.sync="search"
v-model="select"
cache-items
flat
hide-no-data
label="Name"
></v-autocomplete>
</v-container>
</v-content>
</v-app>
</div>
new Vue({
el: "#app",
data: () => ({
items: [],
search: null,
select: null,
commonNames: ["John", "John2", "John3"]
}),
watch: {
search(val) {
val && val !== this.select && this.querySelections(val);
}
},
methods: {
querySelections(v) {
setTimeout(() => {
this.items = this.commonNames.filter(e => {
return (e || "").toLowerCase().indexOf((v || "").toLowerCase()) > -1;
});
}, 500);
}
}
});
In Vuetify 1.1.7 Combobox has new feature which you can refer to.
Its on the Advance custom options.
had this same issue and solved it with the v-combobox it's kinda like the same as v-autocomplete so, you can just replace your tags with <v-combobox></v-combobox> or check the documentation here: https://vuetifyjs.com/en/components/combobox/#usage

Using new Date() with v-date-picker doesn't work

I'm trying to follow this video using Vue and Vuetify to apply the current date with v-model to the date picker component v-date-picker using the data value date that's initially being set with new Date().
This is a simplified structure of my project:
JS
new Vue({
el:"#app",
data: {
date: new Date(),
time: new Date()
}
})
Template
<div id="app">
<v-date-picker v-model="date"></v-date-picker>
{{ date }}
<v-time-picker v-model="time"></v-time-picker>
</div>
And here's a CodePen. Unfortunately I couldn't get the Vuetify CSS to work with the CodePen, but if you open up the console, you'll see that I get errors in the v-date-picker when trying to use new Date() with the v-model directive. Also the date picker isn't rendering. The v-time-picker however works fine.
On my local setup I've created a Vue project with the vue-cli. Here's the error I'm getting there:
[Vue warn]: Error in created hook: "TypeError: dateString.split is not
a function"
found in
--->
at src/components/Meetup/CreateMeetup.vue
at src/App.vue
I'm doing exactly as in the tutorial I'm following, so I don't know if this is a bug with the latest version of either Vue or Vuetify? Or am I missing something?
Obviously (from the error message you're getting) v-datepicker expects to be bound to a String. You might want to try
data: {
date: new Date().toJSON(),
time: new Date().toJSON()
}
https://codepen.io/connexo/pen/ypWxLv
Also see Vuetify API docs (which explicitly states it expects v-model to be of type String):
v-model String null Controls the displayed date. Must use ISO 8601 format.
Instead, use the value attribute in order to overcome the binding.
example
data: {
date: new Date().toISOString().substr(0, 10)
}
<v-text-field slot="activator" :value="dataValue.datePass" label="Date" append-icon="event" readonly style="font-size:14px"></v-text-field>
<v-date-picker v-model="dataValue.datePass"></v-date-picker>
In my case, I needed the date to be stored as a Date object instead of a String. So instead of using v-model in the date-picker, I handled this using #input and :value.
new Vue({
el: '#app',
data() {
return {
isActive: false,
theDate: new Date()
}
},
computed: {
formattedDate() {
return this.theDate ? moment(this.theDate).format('MM/DD/YYYY') : undefined; // Custom format
},
datePickerFormattedDate() {
return this.theDate ? moment(this.theDate).format('YYYY-MM-DD') : undefined; // Date picker objects needs date in this particular format
}
},
methods: {
inputHandler(date) {
if (typeof date === 'string')
date = moment(date).toDate();
this.isActive = false;
this.theDate = date;
}
}
})
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.1/locale/en-gb.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/1.3.12/vuetify.min.js">
</script>
<script src="https://cdn.jsdelivr.net/momentjs/2.10.6/moment-with-locales.min.js">
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/material-design-icons/3.0.1/iconfont/material-icons.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons">
<link rel="stylesheet" href="https://unpkg.com/vuetify/dist/vuetify.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="app">
<v-app id="inspire">
<v-content>
<v-container>
<v-layout row wrap>
<v-flex pb-4 xs12>
Stored date: {{ theDate }}
</v-flex>
<v-flex xs12>
<v-text-field :readonly="true" :value="formattedDate" label="I want to enter dates here"></v-text-field>
<v-menu :close-on-content-click="true" v-model="isActive" :nudge-right="40" lazy transition="scale-transition" offset-y full-width min-width="290px">
<v-icon slot="activator">event</v-icon>
<v-date-picker :value="datePickerFormattedDate" #input="inputHandler"></v-date-picker>
</v-menu>
</v-flex>
</v-layout>
</v-container>
</v-content>
<v-footer></v-footer>
</v-app>
</div>
</body>
</html>
You can use a computed property as a "shim" for v-model. The computed property contains all the logic for the type coercion and everything else is business as usual.
JS
new Vue({
el:"#app",
data: {
date: new Date()
},
computed: {
// "shim" for v-date-picker
sdate: {
get() {
return this.date?.toISOString()
},
set(val) {
this.date = new Date(val)
}
}
}
})
Template
<div id="app">
<v-date-picker v-model="sdate"></v-date-picker>
{{ date }}
</div>
Vuetify date picker need date in that particular format ('YYYY-MM-DD'). This could be solved by replacing this with:-
new Vue({
el:"#app",
data: {
date: moment(new Date()).format('YYYY-MM-DD'),
time: new Date()
}
})
From the official examples, it looks like you have to use like this
data: {
date: new Date().toISOString().substr(0, 10),
time: new Date().getHours() + ':' + new Date().getMinutes(),
}
Example -
https://github.com/vuetifyjs/vuetifyjs.com/blob/master/src/examples/date-pickers/dateDialogAndMenu.vue