df-image Component
The df-image component provides an image upload field: a preview of the current image, a drag & drop zone, and a button that opens the browser's file dialog. It shares its backend communication contract with df-file.
Basic Usage
Below is an example of the df-image component used with DynamicForms:
data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cmVjdCB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgZmlsbD0iIzE5NzZkMiIvPjx0ZXh0IHg9IjUwJSIgeT0iNTAlIiBmaWxsPSJ3aGl0ZSIgZm9udC1zaXplPSIyMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZG9taW5hbnQtYmFzZWxpbmU9Im1pZGRsZSI+UHJvZmlsZTwvdGV4dD48L3N2Zz4=
Features
- Preview of the current image, shown as soon as one is set
- Drag & drop, and a click-to-browse dialog
- Upload progress indication
- Image deletion support
- Downloading the currently set image - its value is already a URL the browser can fetch, so no
commsmethod is needed for this, unlikedf-file'sgetDownloadUrl - Automatic periodic "touches" to keep uploaded images active
- Customizable labels, hints, and error messages
- Backend communication abstraction through the same
FileCommsinterface asdf-file
Props
In addition to common props from InputBase, this component requires:
| Prop | Type | Default | Description |
|---|---|---|---|
| comms | FileComms | Required | Object with methods for image operations |
| touchInterval | number | 60000 | Milliseconds between keep-alive touches. See Configuration for setting this application-wide instead |
Inherited Props
This component inherits all common props from InputBase, including:
control- DynamicForms field objectmodelValue- The image URL (v-model)label- Input labelhint- Hint text- And more...
Value Format
The component stores and displays a URL string, not the file itself:
comms.uploadresolves to the URL the component renders as the image'ssrc- there is no separate lookup for turning an identifier into a display URL, so whatever it returns must already be usable as one.- The same value is used in subsequent operations (delete, touch) to reference the image.
- It is stored in the form data when using DynamicForms.
FileComms Interface
The comms prop requires an object implementing the same FileComms interface df-file uses:
interface FileComms {
/**
* Called when an image is picked, dropped, or selected via the dialog, and needs to be uploaded
* @param file The image file to be uploaded
* @param progressCallback Callback function for upload progress
* @return Promise resolving to the URL the component displays the image from
*/
upload: (file: File, progressCallback?: FileProgressCallback) => Promise<string>;
/**
* Called when the image is removed. Throw FileGoneError if the backend already reports the identifier as
* gone; any other thrown error is treated as transient.
* @param fileIdentifier The URL that was returned by upload
*/
delete: (fileIdentifier: string) => Promise<void>;
/**
* Called periodically to keep the image active. Throw FileGoneError if the backend reports the identifier
* no longer exists — the component then clears the field and, where a `control` is bound, shows the
* error's `errorText`. Any other thrown error is treated as transient and left to the consumer.
* @param fileIdentifier The URL that was returned by upload
*/
touch: (fileIdentifier: string) => Promise<void>;
}
// Progress callback type
type FileProgressCallback = (loaded: number, total: number) => void;
// Thrown by touch/delete to report that the backend has already discarded the image
class FileGoneError extends Error {
constructor(public errorText: string) {
super(errorText);
}
}FileComms also declares an optional getDownloadUrl, for df-file's use — df-image ignores it, since its value already is the URL a download link needs.
Upload Progress
The component displays a progress bar during upload, using the values provided by the progressCallback in the upload method.
After an image has been uploaded to the backend, it is touched every touchInterval milliseconds (60 seconds by default) to let the backend know that it's still relevant. If a touch rejects with a FileGoneError, the field and preview are cleared and, where a control is bound, the error's errorText is shown as a validation error. Any other rejection is treated as a transient failure and left to the consumer.
Non-image Files
A dropped or picked file whose type is not image/* is rejected without calling comms.upload.
Events
This component emits all common events from InputBase:
update:modelValue- When the image URL changes
Examples
Basic Example with Direct API Communication
<template>
<df-image
v-model="imageUrl"
:comms="imageComms"
label="Profile picture"
hint="PNG or JPG, max 5MB"
/>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
import { DfImage } from '@dynamicforms/vuetify-inputs';
import { FileGoneError } from '@dynamicforms/vuetify-inputs';
const imageUrl = ref(null);
// Implementation of FileComms for API communication
const imageComms = {
upload: async (file, progressCallback) => {
const formData = new FormData();
formData.append('file', file);
const response = await axios.post('/api/images', formData, {
onUploadProgress: (progressEvent) => {
if (progressCallback) {
progressCallback(progressEvent.loaded, progressEvent.total);
}
}
});
return response.data.url;
},
delete: async (imageUrl) => {
await axios.delete('/api/images', { params: { url: imageUrl } });
},
touch: async (imageUrl) => {
try {
await axios.post('/api/images/touch', { url: imageUrl });
} catch (err) {
if (err.response?.status === 404) {
throw new FileGoneError('This image is no longer available. Please upload it again.');
}
throw err;
}
}
};
</script>With DynamicForms Integration
<template>
<df-image
:control="form.fields.avatar"
:comms="imageComms"
label="Profile picture"
/>
</template>
<script setup>
import { Group, Field } from '@dynamicforms/vue-forms';
import { DfImage } from '@dynamicforms/vuetify-inputs';
const form = new Group({
avatar: new Field({ value: null })
});
// Implementation of FileComms (same as above)
const imageComms = {
// ... implementation as in previous example
};
</script>