I’m trying to build a custom component that is essentially just Uploadcare’s Vue integration. I ran into a few problems, but got past them. However, I’m stuck on this one, and hoping that someone here might have an idea where to go next.
It seems that, even if I have the @uploadcare/blocks
web components configured correctly, its upload network request is still sent without a payload property its API requires, which ends up giving me an error in the UI that looks like this:
I’ve confirmed that my configuration works, by configuring a blank Uploadcare project on Codesandbox this way, and trying an upload.
Here’s my configuration.
I’m using a Vue plugin to allow the Uploadcare components to bypass the Vue compiler:
install(Vue /* options */) {
const isCustomElement = Vue.config?.compilerOptions?.isCustomElement
Vue.config.compilerOptions = {
...(Vue.config.compilerOptions || {}),
isCustomElement: (tag) => {
return (isCustomElement && isCustomElement(tag)) || tag.startsWith("lr-")
},
}
},
And my template looks like this:
<lr-config
ref="configRef"
:ctx-name="uploaderCtxName"
pubkey="my-valid-public-key"
multiple
sourceList="local, url, camera, dropbox, gdrive"
:confirmUpload="true"
:debug="true"
imgOnly
></lr-config>
<lr-file-uploader-regular
:ctx-name="uploaderCtxName"
:class="[uploaderClassName, { 'dark-mode-enabled': theme === 'dark' }]"
></lr-file-uploader-regular>
<lr-form-input :ctx-name="uploaderCtxName"></lr-form-input>
<lr-upload-ctx-provider
ref="ctxProviderRef"
:ctx-name="uploaderCtxName"
@change="handleChangeEvent"
@modal-close="handleModalCloseEvent"
></lr-upload-ctx-provider>
Here’s the <script>
tag for this component. It uses composition api.
<script>
import "@uploadcare/blocks/web/lr-file-uploader-regular.min.css"
import "@uploadcare/blocks/web/lr-file-uploader-minimal.min.css"
import "@uploadcare/blocks/web/lr-file-uploader-inline.min.css"
import * as LR from "@uploadcare/blocks"
import plugin from "./plugin"
LR.registerBlocks(LR)
export default {
beforeCreate() {
this.$.appContext.app.use(plugin)
},
props: {
uploaderCtxName: {
type: String,
default: () => `uploader-${Math.random().toString(36).substring(7)}`,
},
uploaderClassName: {
type: String,
default: "lr-file-uploader-regular",
},
files: {
type: Array,
default: () => [],
},
theme: {
validator(value) {
return ["light", "dark"].includes(value)
},
},
},
emits: ["update:files"],
data() {
return {
uploadedFiles: [],
}
},
methods: {
resetUploaderState() {
/*
Note: Here we use provider's API to reset File Uploader state.
It's not necessary though. We use it here to show users
a fresh version of File Uploader every time they open it.
Another way is to sync File Uploader state with an external store.
You can manipulate File Uploader using API calls like `addFileFromObject`, etc.
See more: https://uploadcare.com/docs/file-uploader/api/
*/
this.$refs.ctxProviderRef.uploadCollection.clearAll()
},
handleRemoveClick(uuid) {
this.$emit(
"update:files",
this.files.filter((f) => f.uuid !== uuid)
)
},
handleChangeEvent(e) {
if (e.detail) {
this.uploadedFiles = e.detail.allEntries.filter((f) => f.status === "success")
}
},
handleModalCloseEvent() {
this.resetUploaderState()
this.$emit("update:files", [...this.files, ...this.uploadedFiles])
this.uploadedFiles = []
},
},
mounted() {
/*
Note: Localization of File Uploader is done via DOM property on the config node.
You can change any piece of text of File Uploader this way.
See more: https://uploadcare.com/docs/file-uploader/localization/
*/
this.$refs.ctxProviderRef.localeDefinitionOverride = {
en: {
photo__one: "photo",
photo__many: "photos",
photo__other: "photos",
"upload-file": "Upload photo",
"upload-files": "Upload photos",
"choose-file": "Choose photo",
"choose-files": "Choose photos",
"drop-files-here": "Drop photos here",
"select-file-source": "Select photo source",
"edit-image": "Edit photo",
"no-files": "No photos selected",
"caption-edit-file": "Edit photo",
"files-count-allowed": "Only {{count}} {{plural:photo(count)}} allowed",
"files-max-size-limit-error": "Photo is too big. Max photo size is {{maxFileSize}}.",
"header-uploading": "Uploading {{count}} {{plural:photo(count)}}",
"header-succeed": "{{count}} {{plural:photo(count)}} uploaded",
"header-total": "{{count}} {{plural:photo(count)}} selected",
},
}
},
beforeUnmount() {
this.$refs.ctxProviderRef.localeDefinitionOverride = null
},
}
</script>
I was trying to crawl through the call order of the code, but, since there’s no error logged to the console, it’s hard to tell if I’m going the right way or not, through their source code. All I’ve been able to do so far, is confirm that their request they’re sending to their API is indeed missing my public key.
I have since moved on from this project, instead building my own UI for Uploadcare’s API using PrimeVue. But I’d like to keep working on this project, because it seems more ideal to have Uploadcare’s own official web component, if possible.
I had another problem in my PrimeVue project that seems related to this, although I’m not sure how. In Uploadcare’s @uploadcare/client-upload
Javascript library, their isFileData
function was always returning false
for me, even for a file taken directly from input.filex[0]
. In a blank Codesandbox, it just worked, but in Weweb, it would always return false
. Eventually, I discovered that I could get it to work by instantiating a new File using the file from the input, then passing the new File instance instead of the file directly from the input. I’m not sure if this helps identify anything going on with the Uploadcare Blocks component, but I’m including it just for the possibility that it might.
Thank you to anyone who takes the time to read through this, and I pray your day is blessed!