It's been a few weeks since I've done this, but while looking at my new stats, I saw one of my old Vue.js posts getting some activity: Reading Image Sizes and Dimensions with Vue.js. In that blog post, I showed how to take a user-selected file and check the file size and dimensions of an image. As I've been slowly going through my Vue.js posts and creating Aline.js versions, I thought this would be a perfect fit.
I'm not going to repeat everything from the previous entry, but let me recap the highlights.
input
tag using type=file
.Image
object.onload
event), you can then check the dimensions.
Ok, so given the above, let's build a quick demo. First, the HTML. I'm just going to have the input field and a place to print out details about the image.
<div x-data="app">
<input type="file" x-ref="myFile" x-on:change="selectedFile" accept="image/*"><br/>
<template x-if="imageLoaded">
<p>
Image size is <span x-text="image.size"></span><br/>
Image width and height is <span x-text="image.width"></span> / <span x-text="image.height"></span>
</p>
</template>
</div>
A few things to note here. Like Vue, we sometimes need to reach out to the DOM, and like Vue, this is done via refs
. You can see my setting x-ref="myFile"
to gain access to the input field directly. Also, note I'm using the change
event. This will fire when the user selects a file. Now let's look at the code.
document.addEventListener('alpine:init', () => {
Alpine.data('app', () => ({
imageLoaded:false,
image: {
size:null,
width:null,
height:null
},
selectedFile() {
this.imageLoaded = false;
let file = this.$refs.myFile.files[0];
if(!file || file.type.indexOf('image/') !== 0) return;
this.image.size = file.size;
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = evt => {
let img = new Image();
img.onload = () => {
this.image.width = img.width;
this.image.height = img.height;
this.imageLoaded = true;
}
img.src = evt.target.result;
}
reader.onerror = evt => {
console.error(evt);
}
}
}))
});
My Alpine app has two main variables, imageLoaded
and image
. The only real logic is in selectedFile
. This will use $refs
to grab the input field and the selected image. I then use a FileReader
object to read in the bits, set it to the image, and when onload
is fired, I can update my variables to the front-end displays. Given this source image for example:
If I select it, I'll see this:
You can test this yourself using the CodePen below:
Ok, so as I did in the previous post, let's consider a simple example that adds validation. Specifically - a max file size, a max width, and a max height. The HTML is mostly the same except now I show an error on a validation failure:
<div x-data="app">
<input type="file" x-ref="myFile" x-on:change="selectedFile" accept="image/*"><br/>
<template x-if="imageError">
<p class="imageError" x-text="imageError">
</p>
</template>
</div>
In the JavaScript, I added constants for my max values:
const MAX_SIZE = 100000;
const MAX_WIDTH = 500;
const MAX_HEIGHT = 300;
And here's the Alpine app itself:
document.addEventListener('alpine:init', () => {
Alpine.data('app', () => ({
imageError:'',
image: {
size:null,
width:null,
height:null
},
selectedFile() {
this.imageError = '';
let file = this.$refs.myFile.files[0];
if(!file || file.type.indexOf('image/') !== 0) return;
this.image.size = file.size;
if(this.image.size > MAX_SIZE) {
this.imageError = `The image file size (${this.image.size}) is too much (max is ${MAX_SIZE}).`;
return;
}
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = evt => {
let img = new Image();
img.onload = () => {
this.image.width = img.width;
this.image.height = img.height;
if(this.image.width > MAX_WIDTH) {
this.imageError = `The image width (${this.image.width}) is too much (max is ${MAX_WIDTH}).`;
return;
}
if(this.image.height > MAX_HEIGHT) {
this.imageError = `The image height (${this.image.height}) is too much (max is ${MAX_HEIGHT}).`;
return;
}
}
img.src = evt.target.result;
}
reader.onerror = evt => {
console.error(evt);
}
}
}))
});
For the most part, this is the same, with the only change being that now I check the various properties and set a new variable, imageError
, when something fails validation. You can test this below:
I'll repeat myself, which my readers know I like to do, but the more I use Alpine, the more it just clicks with me.
Also published here.