This worked perfect, have used in one of my projects now.
Thanks a lot.
This worked perfect, have used in one of my projects now.
Thanks a lot.
I actually fixed this back then, sorry i did not posted here let me post now so it may help someone need this.
<template>
<div>
<q-file
:value="files"
@input="updateFiles"
:label="label"
outlined
multiple
:clearable="!isUploading"
>
<template v-slot:file="{ index, file }">
<q-chip
class="full-width q-my-xs"
:removable="isUploading && uploadProgress[index].percent < 1"
square
@remove="cancelFile(index)"
>
<q-linear-progress
class="absolute-full full-height"
:value="uploadProgress[index].percent"
:color="uploadProgress[index].color"
track-color="grey-2"
/>
<q-avatar>
<q-icon :name="uploadProgress[index].icon" />
</q-avatar>
<div class="ellipsis relative-position">
{{ file.name }}
</div>
<q-tooltip>
{{ file.name }}
</q-tooltip>
</q-chip>
</template>
<template v-slot:after v-if="canUpload">
<q-btn
color="primary"
dense
icon="cloud_upload"
round
@click="upload"
:disable="!canUpload"
:loading="isUploading"
/>
</template>
</q-file>
</div>
<!--
<div class="col-4 q-pa-sm">
<q-uploader
url="http://localhost:30035/uploads"
label="Upload files"
color="purple"
square
flat
bordered
/>
</div>
</div>
-->
</template>
<script>
import api from "@/api";
import axios from "axios";
export default {
name: "Uploader",
props: {
type: {
type: String,
default: ""
}
},
data() {
return {
files: null,
uploadProgress: [],
uploading: null
};
},
computed: {
label() {
return "Upload your " + this.type;
},
isUploading() {
return this.uploading !== null;
},
canUpload() {
return this.files !== null;
}
},
methods: {
cancelFile(index) {
this.uploadProgress[index] = {
...this.uploadProgress[index],
error: true,
color: "orange-2"
};
},
async updateFiles(files) {
this.files = files;
if (this.files.length) {
let objFile = this.files[0];
if (this.type == "Video") {
if (objFile.type.indexOf("video/") !== 0) {
this.$q.notify({
color: "red-5",
textColor: "white",
icon: "warning",
message: "Please select a valid VIDEO to upload."
});
return false;
event.preventDefault();
}
} else {
if (objFile.type.indexOf("image/") !== 0) {
this.$q.notify({
color: "red-5",
textColor: "white",
icon: "warning",
message: "Please select a valid IMAGE to upload."
});
return false;
event.preventDefault();
}
}
this.uploadProgress = (files || []).map(file => ({
error: false,
color: "green-2",
percent: 0,
icon:
file.type.indexOf("video/") === 0
? "movie"
: file.type.indexOf("image/") === 0
? "photo"
: file.type.indexOf("audio/") === 0
? "audiotrack"
: "insert_drive_file"
}));
}
},
upload() {
clearTimeout(this.uploading);
const config = {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: progressEvent => {
console.log(progressEvent.loaded);
this.uploadProgress[0].error = false;
this.uploadProgress[0].color = "green-2";
var percent = progressEvent.loaded / progressEvent.total;
this.uploadProgress[0].percent = percent;
console.log(
"Progress:",
progressEvent.loaded,
"/",
progressEvent.total,
percent + "%"
);
}
};
var file = this.files[0];
//var fileName = file.name;
var strType = this.type;
var formData = new FormData();
formData.append("file", file);
// You should have a server side REST API
var _this = this;
axios
.post(api.getAPIBaseUrl() + "/uploads", formData, config)
.then(function(objResponse) {
objResponse.data.strType = strType;
console.log("SUCCESS!!", objResponse.data);
_this.$emit("uploaded", objResponse.data);
})
.catch(function(err) {
console.log("FAILURE!!", err);
_this.$emit("uploaded", err);
});
}
/*
__updateUploadProgress() {
let done = true;
this.uploadProgress = this.uploadProgress.map(progress => {
if (progress.percent === 1 || progress.error === true) {
return progress;
}
const percent = Math.min(1, progress.percent + Math.random() / 10);
const error = percent < 1 && Math.random() > 0.95;
if (error === false && percent < 1 && done === true) {
done = false;
}
return {
...progress,
error,
color: error === true ? "red-2" : "green-2",
percent
};
});
this.uploading =
done !== true ? setTimeout(this.__updateUploadProgress, 300) : null;
}
*/
},
beforeDestroy() {
clearTimeout(this.uploading);
}
};
</script>
in express
app.post('/uploads', function (req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
// The name of the input field (i.e. "objFile") is used to retrieve the uploaded file
let objFile = req.files[Object.keys(req.files)[0]]; //returns 'someVal'
objFile.name = objFile.md5 + "." + mime.getExtension(objFile.mimetype);
console.log("objFile is ", objFile);
// Use the mv() method to place the file somewhere on your server
objFile.mv(__dirname + '/uploads/' + objFile.name, function (err) {
if (err) {
res.status(500).json({ blnOK: false, msg: err.message });
}
var objResponse = {};
objResponse.blnOK = true;
objResponse.objFile = objFile;
res.json(objResponse);
});
});
please note i am using “express-fileupload” for file uploading
Here is how i did it maybe it helps someone in future
component
<template>
<div class="q-gutter-sm">
<q-field
ref="radioGroup"
:value="radioGroup"
hide-bottom-space
borderless
:rules="rules"
>
<q-radio
v-model="radioGroup"
v-on:input="emit"
v-for="radioOption in radioOptions"
:key="radioOption.id"
:val="radioOption.id"
:label="radioOption.name"
/>
</q-field>
</div>
</template>
<script>
export default {
name: "base-radio",
created() {
this.radioGroup = this.value;
},
data() {
return { radioGroup: "" };
},
props: {
radioOptions: {
type: Array,
required: true
},
value: { required: true },
isRequired: { type: Boolean, default: false },
tooltip: {
type: String
}
},
computed: {
rules() {
return this.isRequired ? this.$utils.requiredRule : [];
}
},
methods: {
emit(value) {
console.log("emitting", value);
this.$emit("input", value);
}
}
};
</script>
<style lang="scss" scoped></style>
call to component
<base-radio
isRequired
v-model="objModel.title"
:radioOptions="arrTitle"
></base-radio>
This worked perfect, have used in one of my projects now.
Thanks a lot.
@Hawkeye64 thanks for the advice, lets see if @Pratik-Patel has this working and is an easy fix we can use. i will give it a try.
Thanks
@Pratik-Patel i have tried using your qgrid extension but there are a few quirks i found,
the filter_type: ‘select’ and pagination does not seem to work with data being requested from the server, i am pasting the demo i have built, its pretty straight forward with the ajax request i am doing.
can you please review
@Hawkeye64 if you can also advise, thanks!
<template>
<div>
<q-grid
:title="`${pageTitle}s`"
:pagination.sync="pagination"
:data="data"
:columns="columns"
:columns_filter="true"
:draggable="true"
:fullscreen="true"
:csv_download="true"
:global_search="true"
:no-data-label="$utils.noResultsLabel"
:no-results-label="$utils.filterNoResultsLabel"
row-key="id"
:loading="loading"
:filter="filter"
@request="onRequest"
binary-state-sort
>
<template v-slot:body="props">
<q-tr :props="props">
<q-td key="id">
{{ props.row.id }}
</q-td>
<q-td key="roll_no">
{{ props.row.roll_no }}
</q-td>
<q-td key="color">
{{ props.row.color }}
</q-td>
<q-td key="gsm">
{{ props.row.gsm }}
</q-td>
<q-td key="length">
{{ props.row.length }}
</q-td>
<q-td key="roll_size">
{{ props.row.roll_size }}
</q-td>
<q-td key="gross_weight">
{{ props.row.gross_weight }}
</q-td>
<q-td key="net_weight">
{{ props.row.net_weight }}
</q-td>
<q-td key="createdAt">
{{ $utils.formatDate(props.row.createdAt) }}
</q-td>
<q-td key="updatedAt">
{{ $utils.formatDate(props.row.updatedAt) }}
</q-td>
</q-tr>
</template>
</q-grid>
</div>
</template>
<script>
const strPage = "Roll";
import { mapActions, mapState, mapGetters } from "vuex";
export default {
async mounted() {
this.refresh();
},
computed: {
...mapState("enquiry", ["arrColor"]),
arrColorMapped() {
let arr = this.arrColor.map(str => {
return { id: str, name: str };
});
return arr;
},
pageUri() {
return this.pageTitle.toLowerCase().replace(" ", "_");
}
},
methods: {
async onRequest(props) {
const { page, rowsPerPage, sortBy, descending } = props.pagination;
const strFilter = props.filter;
this.loading = true;
let objParams = Object.assign({}, props.pagination);
objParams.search = strFilter;
let objResponse;
try {
//pass status as params
if (this.status) {
objParams.status = this.status;
}
objResponse = await this.$myAxios.execute(
"GET",
`/${this.pageUri}`,
objParams
);
} catch (e) {
this.$utils.showErrorNotice(e.message);
} finally {
this.loading = false;
}
if (objResponse) {
this.pagination.rowsNumber = objResponse.obj.count;
this.pagination.page = page;
this.pagination.rowsPerPage = rowsPerPage;
this.pagination.sortBy = sortBy;
this.pagination.descending = descending;
this.data = objResponse.obj.rows;
}
},
async refresh() {
this.onRequest({
pagination: this.pagination,
filter: undefined
});
}
},
data() {
return {
pageTitle: strPage,
breadcrumbs: [
{ id: 1, label: "Home", icon: "home", to: "/admin/dashboard" },
{ id: 2, label: strPage, icon: "folder" }
],
columns: [
{
name: "id",
required: true,
label: "Id",
align: "left",
field: "id",
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10),
sortable: true
},
{
name: "roll_no",
required: true,
label: "Roll #",
align: "left",
field: "roll_no",
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10),
sortable: true
},
{
name: "color",
label: "Color",
align: "left",
field: "color",
sortable: true,
filter_type: "select",
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
},
{
name: "gsm",
label: "GSM",
align: "left",
field: "gsm",
sortable: true,
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
},
{
name: "length",
label: "Length",
align: "left",
field: "length",
sortable: true,
filter_type: "select",
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
},
{
name: "roll_size",
label: "Roll Size",
align: "left",
field: "roll_size",
sortable: true,
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
},
{
name: "gross_weight",
label: "Gross Weight",
align: "left",
field: "gross_weight",
sortable: true,
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
},
{
name: "net_weight",
label: "Net Weight",
align: "left",
field: "net_weight",
sortable: true,
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
},
{
name: "createdAt",
label: "Date Created",
align: "left",
field: "createdAt",
sortable: true,
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
},
{
name: "updatedAt",
label: "Date Updated",
align: "left",
field: "updatedAt",
sortable: true,
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
}
],
objModel: {},
editMode: false,
dialog: false,
printDialog: false,
loading: false,
filter: "",
data: [],
pagination: {
sortBy: "id",
descending: true,
page: 1,
rowsPerPage: 10,
rowsNumber: 10
},
rollsToPrint: []
};
}
};
</script>
<style lang="scss" scoped></style>
Here is how i did it maybe it helps someone in future
component
<template>
<div class="q-gutter-sm">
<q-field
ref="radioGroup"
:value="radioGroup"
hide-bottom-space
borderless
:rules="rules"
>
<q-radio
v-model="radioGroup"
v-on:input="emit"
v-for="radioOption in radioOptions"
:key="radioOption.id"
:val="radioOption.id"
:label="radioOption.name"
/>
</q-field>
</div>
</template>
<script>
export default {
name: "base-radio",
created() {
this.radioGroup = this.value;
},
data() {
return { radioGroup: "" };
},
props: {
radioOptions: {
type: Array,
required: true
},
value: { required: true },
isRequired: { type: Boolean, default: false },
tooltip: {
type: String
}
},
computed: {
rules() {
return this.isRequired ? this.$utils.requiredRule : [];
}
},
methods: {
emit(value) {
console.log("emitting", value);
this.$emit("input", value);
}
}
};
</script>
<style lang="scss" scoped></style>
call to component
<base-radio
isRequired
v-model="objModel.title"
:radioOptions="arrTitle"
></base-radio>
Please ignore above i found out i have to use QField for this as input and select are already wrapping them.
Hello
I have a q-radio component that is working fine but i am having a very hard time trying to validate that atleast one of the option must be selected for form validation.
if someone can help on this i will be highly grateful. Thanks in advance below is my code.
//my component
<template>
<div class="q-gutter-sm">
<q-radio
:value="value"
:rules="arrRules"
v-on:input="emit"
v-for="objOption in arrRadioOptions"
:key="objOption.id"
:val="objOption.id"
:label="objOption.name"
/>
</div>
</template>
<script>
export default {
name: "base-radio",
data() {
return {};
},
props: {
arrRadioOptions: {
type: Array,
required: true
},
value: { required: true },
is_required: { type: Boolean, default: false },
tooltip: {
type: String
}
},
computed: {
arrRules() {
return this.is_required ? this.$utils.requiredRule : [];
}
},
methods: {
emit(value) {
this.$emit("input", value);
}
}
};
</script>
<style lang="scss" scoped></style>
my call for component in parent
<base-radio
is_required
v-model="objModel.title"
:arrRadioOptions="arrTitle"
></base-radio>
I actually fixed this back then, sorry i did not posted here let me post now so it may help someone need this.
<template>
<div>
<q-file
:value="files"
@input="updateFiles"
:label="label"
outlined
multiple
:clearable="!isUploading"
>
<template v-slot:file="{ index, file }">
<q-chip
class="full-width q-my-xs"
:removable="isUploading && uploadProgress[index].percent < 1"
square
@remove="cancelFile(index)"
>
<q-linear-progress
class="absolute-full full-height"
:value="uploadProgress[index].percent"
:color="uploadProgress[index].color"
track-color="grey-2"
/>
<q-avatar>
<q-icon :name="uploadProgress[index].icon" />
</q-avatar>
<div class="ellipsis relative-position">
{{ file.name }}
</div>
<q-tooltip>
{{ file.name }}
</q-tooltip>
</q-chip>
</template>
<template v-slot:after v-if="canUpload">
<q-btn
color="primary"
dense
icon="cloud_upload"
round
@click="upload"
:disable="!canUpload"
:loading="isUploading"
/>
</template>
</q-file>
</div>
<!--
<div class="col-4 q-pa-sm">
<q-uploader
url="http://localhost:30035/uploads"
label="Upload files"
color="purple"
square
flat
bordered
/>
</div>
</div>
-->
</template>
<script>
import api from "@/api";
import axios from "axios";
export default {
name: "Uploader",
props: {
type: {
type: String,
default: ""
}
},
data() {
return {
files: null,
uploadProgress: [],
uploading: null
};
},
computed: {
label() {
return "Upload your " + this.type;
},
isUploading() {
return this.uploading !== null;
},
canUpload() {
return this.files !== null;
}
},
methods: {
cancelFile(index) {
this.uploadProgress[index] = {
...this.uploadProgress[index],
error: true,
color: "orange-2"
};
},
async updateFiles(files) {
this.files = files;
if (this.files.length) {
let objFile = this.files[0];
if (this.type == "Video") {
if (objFile.type.indexOf("video/") !== 0) {
this.$q.notify({
color: "red-5",
textColor: "white",
icon: "warning",
message: "Please select a valid VIDEO to upload."
});
return false;
event.preventDefault();
}
} else {
if (objFile.type.indexOf("image/") !== 0) {
this.$q.notify({
color: "red-5",
textColor: "white",
icon: "warning",
message: "Please select a valid IMAGE to upload."
});
return false;
event.preventDefault();
}
}
this.uploadProgress = (files || []).map(file => ({
error: false,
color: "green-2",
percent: 0,
icon:
file.type.indexOf("video/") === 0
? "movie"
: file.type.indexOf("image/") === 0
? "photo"
: file.type.indexOf("audio/") === 0
? "audiotrack"
: "insert_drive_file"
}));
}
},
upload() {
clearTimeout(this.uploading);
const config = {
headers: {
"Content-Type": "multipart/form-data"
},
onUploadProgress: progressEvent => {
console.log(progressEvent.loaded);
this.uploadProgress[0].error = false;
this.uploadProgress[0].color = "green-2";
var percent = progressEvent.loaded / progressEvent.total;
this.uploadProgress[0].percent = percent;
console.log(
"Progress:",
progressEvent.loaded,
"/",
progressEvent.total,
percent + "%"
);
}
};
var file = this.files[0];
//var fileName = file.name;
var strType = this.type;
var formData = new FormData();
formData.append("file", file);
// You should have a server side REST API
var _this = this;
axios
.post(api.getAPIBaseUrl() + "/uploads", formData, config)
.then(function(objResponse) {
objResponse.data.strType = strType;
console.log("SUCCESS!!", objResponse.data);
_this.$emit("uploaded", objResponse.data);
})
.catch(function(err) {
console.log("FAILURE!!", err);
_this.$emit("uploaded", err);
});
}
/*
__updateUploadProgress() {
let done = true;
this.uploadProgress = this.uploadProgress.map(progress => {
if (progress.percent === 1 || progress.error === true) {
return progress;
}
const percent = Math.min(1, progress.percent + Math.random() / 10);
const error = percent < 1 && Math.random() > 0.95;
if (error === false && percent < 1 && done === true) {
done = false;
}
return {
...progress,
error,
color: error === true ? "red-2" : "green-2",
percent
};
});
this.uploading =
done !== true ? setTimeout(this.__updateUploadProgress, 300) : null;
}
*/
},
beforeDestroy() {
clearTimeout(this.uploading);
}
};
</script>
in express
app.post('/uploads', function (req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
// The name of the input field (i.e. "objFile") is used to retrieve the uploaded file
let objFile = req.files[Object.keys(req.files)[0]]; //returns 'someVal'
objFile.name = objFile.md5 + "." + mime.getExtension(objFile.mimetype);
console.log("objFile is ", objFile);
// Use the mv() method to place the file somewhere on your server
objFile.mv(__dirname + '/uploads/' + objFile.name, function (err) {
if (err) {
res.status(500).json({ blnOK: false, msg: err.message });
}
var objResponse = {};
objResponse.blnOK = true;
objResponse.objFile = objFile;
res.json(objResponse);
});
});
please note i am using “express-fileupload” for file uploading
Hello
I am new to quasar and will appreciate if someone can help on this.
I am using the q-file-picker component like this
<template>
<div class="q-pa-md column items-start q-gutter-y-md">
<q-file
:value="files"
@input="updateFiles"
label="Pick files"
outlined
multiple
:clearable="!isUploading"
style="max-width: 400px"
>
<template v-slot:file="{ index, file }">
<q-chip
class="full-width q-my-xs"
:removable="isUploading && uploadProgress[index].percent < 1"
square
@remove="cancelFile(index)"
>
<q-linear-progress
class="absolute-full full-height"
:value="uploadProgress[index].percent"
:color="uploadProgress[index].color"
track-color="grey-2"
/>
<q-avatar>
<q-icon :name="uploadProgress[index].icon" />
</q-avatar>
<div class="ellipsis relative-position">
{{ file.name }}
</div>
<q-tooltip>
{{ file.name }}
</q-tooltip>
</q-chip>
</template>
<template v-slot:after v-if="canUpload">
<q-btn
color="primary"
dense
icon="cloud_upload"
round
@click="upload"
:disable="!canUpload"
:loading="isUploading"
/>
</template>
</q-file>
</div>
<!--
<div class="col-4 q-pa-sm">
<q-uploader
url="http://localhost:30035/uploads"
label="Upload files"
color="purple"
square
flat
bordered
/>
</div>
</div>
-->
</template>
<script>
import api from "../../api";
export default {
data() {
return {
files: null,
uploadProgress: [],
uploading: null
};
},
computed: {
isUploading() {
return this.uploading !== null;
},
canUpload() {
return this.files !== null;
}
},
methods: {
cancelFile(index) {
this.uploadProgress[index] = {
...this.uploadProgress[index],
error: true,
color: "orange-2"
};
},
async updateFiles(files) {
this.files = files;
this.uploadProgress = (files || []).map(file => ({
error: false,
color: "green-2",
percent: 0,
icon:
file.type.indexOf("video/") === 0
? "movie"
: file.type.indexOf("image/") === 0
? "photo"
: file.type.indexOf("audio/") === 0
? "audiotrack"
: "insert_drive_file"
}));
},
async upload() {
clearTimeout(this.uploading);
const allDone = this.uploadProgress.every(
progress => progress.percent === 1
);
var file = this.files[0];
var fileName = file.name;
const objResponse = await api.uploadFile(file);
console.log("objResponse in app is ", objResponse);
this.uploadProgress = this.uploadProgress.map(progress => ({
...progress,
error: false,
color: "green-2",
percent: allDone === true ? 0 : progress.percent
}));
let done;
this.uploading =
done !== true ? setTimeout(this.__updateUploadProgress, 300) : null;
},
__updateUploadProgress() {
let done = true;
this.uploadProgress = this.uploadProgress.map(progress => {
if (progress.percent === 1 || progress.error === true) {
return progress;
}
const percent = Math.min(1, progress.percent + Math.random() / 10);
const error = percent < 1 && Math.random() > 0.95;
if (error === false && percent < 1 && done === true) {
done = false;
}
return {
...progress,
error,
color: error === true ? "red-2" : "green-2",
percent
};
});
this.uploading =
done !== true ? setTimeout(this.__updateUploadProgress, 300) : null;
}
},
beforeDestroy() {
clearTimeout(this.uploading);
}
};
</script>
and in my express code i am doing like this
app.post('/uploads', function (req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('No files were uploaded.');
}
// The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
let sampleFile = req.files[Object.keys(req.files)[0]]; //returns 'someVal'
console.log("sampleFile is ", sampleFile);
// Use the mv() method to place the file somewhere on your server
sampleFile.mv(__dirname + '/uploads/' + sampleFile.name, function (err) {
if (err)
return res.status(500).send(err);
res.send('File uploaded!');
});
});
I am not able to connect the both, the express code works ok with q-uploader but i would like to use q-file-picker as above for my project…
can someone please help.
Thanks
GS
i have now figured out you need to import like this Quasar.utils.exportFile
import { exportFile } from Quasar.utils.exportFile;
might help someone who have this issue…
Hello i am trying to import an exportFile component like this in a standalone quasar configuration on my existing project but it does not work…
<script src="https://cdn.jsdelivr.net/npm/vue@^2.0.0/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar@1.12.2/dist/quasar.umd.min.js"></script>
<script>
import { exportFile } from 'quasar';
function wrapCsvValue (val, formatFn) {
let formatted = formatFn !== void 0
? formatFn(val)
: val
formatted = formatted === void 0 || formatted === null
? ''
: String(formatted)
formatted = formatted.split('"').join('""')
/**
* Excel accepts \n and \r in strings, but some other CSV parsers do not
* Uncomment the next two lines to escape new lines
*/
// .split('\n').join('\\n')
// .split('\r').join('\\r')
return `"${formatted}"`
}
new Vue({
el: '#q-app',
data: function () {
return {
loading: false,
filter: "",
arrData: [],
Can somebody please advise the right way to do it as i could not find it in the docs…
Thanks
gs