Building KiteTail #1: Previewing Image Uploads with Vue.js

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
I know if you can hear me while I have this placeholder up so I'm going to talk a little bit and see what people say cool let me get this out of the way so [Music] all right I guess here we are a lot of that goes actually shouldn't be because I have my volume turned off everywhere and I'm not listening to my own anyways Oh got a real crowd here today ok so I guess I'm just going to start by sort of showing what's here and explaining what I wanted to do next so let's just make sure that we have a fresh database going on here so right now I have some basic stuff set up this is kind of my product dashboard with a list of various products that are in the system and you can kind of go in and start editing them and stuff most of this stuff is just like pretend doesn't actually work but the way I had it set up originally it was when you'd go to add any product you'd have to specify all the information before you could create it so what I've been working on this morning what I want to continue working on now is basically being able to just add a product by only specifying the name this is kind of a crappy placeholder step for now I think the way I want to have it work eventually is either click this button and have it like expand out a little form field in line or maybe a little modal or something like that but anyways you create a product so the one that we're going to create today is going to be called dog food and that drops you into the sort of edit screen right away so what I wanted to work on was basically being able to do like a client-side live image preview here whenever you go to change the image so right now this is just kind of static stuff that doesn't actually work at least this image part so we're going to need to work through the markup and all that stuff and figure out how we need to set it up to work as an actual file input thing yeah Taylor is right I don't use any frameworks I used to use bootstrap a lot when they switch to sass I kind of gave up on it because I hate sass so I kind of put together my own thing that's built in less that's somewhat bootstrap inspired as far as like the naming conventions and stuff but it's sort of been growing over the last couple years and it's my own little thing so anyways yeah what we want to be able to do is get it to a point where we can click this get our file dialog pick an image and have it preview here and do all that with view and be able to save and have that image show up so this used to just be like a big ugly form but I really wanted the Edit screen to sort of mimic the actual check out page a little bit more so felt like you were kind of editing the actual form that's how we're kind of going with this approach so if you see if I go into like another one it actually shows the real one and kind of looks similar to the actual checkout process so yeah let's figure out how we're going to do it what I hate about sass I just think it's not good it is a very like procedural it doesn't behave the way CSS behaves like Declaration order is super important you have to declare variable before you use it less works a lot more intuitively to me I hate that in sass you have to create mix-ins explicitly instead of being able to mix in an existing class so like something I do in laughs a lot most of my stuff is very utility driven but a lot of the time I'll have components that kind of graduate out of those utilities so if you look in this flat folder I have tons of utilities like these spacing utilities which are hard to see because this is all kind of dynamically generated but if you look at something like see if I can find an example of something that's built out of other utilities I mean this is something I do a lot right so here's like some utilities for absolute positioning so this pins the element to the top to the right the bottom the left and I have one that pins it on the y-axis that's just composed of the one that pins it to the top and the one that pins it to the bottom you can't do this sort of thing as nicely in sass so anyways let's figure out how this is going to work so here's our kind of edit view this is the mark-up that I have right now while I was just kind of playing around and figuring out what I needed to look like and stuff so there's no actual file input thing here or anything like that and yeah so I think one of the tricky things is going to be that we want clicking this to simulate clicking like an actual file input so the trick that I've seen that seems to work the best for that is basically making this whole element the input label as since the label for any element you know is clickable just like clicking the actual button so let's think about what that might look like so say that we had I have some utilities for like hiding stuff so here's like the pseudo hidden one which I've used in the past to basically just like make something beyond the screen as far as everything and the browser can tell but not be visible at all because you can't use a label for a hidden file input it actually has to exist so if we were to go and add a file input here maybe we stick it inside of this box which represents what shows up on hover here let's put an input type file and give it a class of pseudo hidden so you can to actually see it so if we go ahead and pull this up now and kind of poke around here we should see that that is there but it's just out of the document flow and not interfering with anything else and we'll give it a name let me think what this name has to be let's go to the products controller and look at the update method because I used to have a different version of this where everything is just kind of a basic form so let's give me call the banner image so we'll give us a name of banner image and then I think what we want to do is basically make this entire div a label instead of a div so now when we click anything here it should trigger this file input and we might not need this as clickable class that let's mean you kind of simulate the cursor:pointer and all that stuff so I have two computers going here so I can see the chat okay so let's just see that did what we wanted it to do so clicking that does open like my downloads thing nice you can see all the things I've downloaded here's the image that we're going to use for our dog food so that works now because it thinks this whole thing is a label so let's get rid of this is clickable thing and see if it still feels like a label in terms of still having like our cursor pointer and stuff it doesn't okay so we're going to need to have that let's make this a little bigger so I have this some utility called hover only that means this will only show on hover and this is like a responsive screen size prefix so that as long as the screen is a certain size it'll show but on mobile it'll always show because you don't want things only working on hover on a phone since there's no way to actually support hover okay so you can go through and click this and you know in theory we can actually update this and that's actually there now so there's our dog food so I'm going to open up the database and get rid of that because it's the easiest way to do that currently since you have no way to actually remove images but that's something that we'll probably want to add to dog food I'll have a null image path okay so getting this previewing on the client side we don't want to write any nasty JavaScript you want to do with Vijay s I don't want to make this entire form like an async JavaScript form yet I think I just want to make this portion use view so we have to kind of think about like what is this component going to be I think we could probably make it something pretty specific like we could call it like a banner image file input or like a banner image upload or something and we can kind of bake some of that stuff in there so let's think about how it's going to work okay so let's go ahead and create a new view component so inside our JavaScript's components folder create a new file we'll call it banner image upload W something like that um there's like some other few crap I have kicking around so it's going to have a templates and for now we'll just make this basically do nothing fancy just kind of migrate everything over from the other template to export defaults so let's copy this whole thing paste that into our template here and we have to think about like some stuff here is obviously coming from PHP which isn't going to work with a view component so we have to figure out how we want to pass that stuff in as props instead so first thing I'm going to do actually is fix this icon helper this isn't going to work in view j/s because this is a blade directive so the easiest thing I could do there is actually just in line this whole SVG so we'll take like the whole camera thing here just paste that in and let's find out what classes were applied to that so we can copy them so it looks like it's just got a class of icon okay and the next thing that we want to do is this stuff so the image here is actually just like a div with a set aspect ratio and a background image property so right now we're checking with blade if the product has an image set then specify the path to that image otherwise specify the path to the place holder so I think the easiest thing to do would just be to pass through like a default image and handle the conditional stats on the PHP side so let's think about what this is going to look like let's head over to our bootstraps javascript file and register this component and I'll app okay so we'll pull in banner image upload and then over here you can just add a banner image upload tank and maybe we'll just keep both of them stacked on top of each other temporarily and just work on getting them both to work the same way so I think I'm going to default to just showing the placeholder image and just see if this is enough to even get this to load okay so we do have both there now which is good if we were to go to look at a different product though I'm pretty sure yeah the view one has the placeholder image and the non view one doesn't so we want to be able to pass through the path so let's think about how that's going to work I think we're going to have a prop that's going to be like something like defaults image and this will use some blade but it'll be something like products will can't type so if the product has an image path and pass through the URL as this is just the field that stores where it is relative to the file system and then the URL helper generates the full absolute URL to the actual thing so if there is an image then we want to use the image that's there otherwise we want to pass through this placeholder string and we need to add another curly bracket here and yes now the reason I was thinking about doing this play originally because I thought we might be passing through null and having the placeholder be handled in the banner image itself so you're right for now we don't want to have that if we're going to pass through the whole string which also yeah so that's would be okay let's make sure that everything's as broken as it was before so it looks fine and I will actually make use of this prop and actually accept it in here so there we call a default image so we've got a default image here and now we can use that to interpolate inside this style tag which is probably not the best way to do it we probably want to have a computed property for that let's add our data thing here which just be empty for now I did not enable anything to save or export the stream but you can always watch the previous one until I do a new one so that you'll still be able to watch this whole thing for until I do another stream okay so computed I think we want to have like banner styles or something and this is where we're going to return an object that has a background image set to [Music] template string and this is going to be our default image to start okay so and then we want to bind these style to be banner styles hopefully I'm remembering this correctly okay so we have our our thing they're working let's check a couple other ones seems okay dog food perfect okay so I feel confident that we can get rid of the second one now and just worry about this first one so let's see if it even still works at least like setting the dog food thing so we set the image we don't get a preview you know none of this stuff is working the way that you would want it to but I bet if we go and look at the file type does that still have a value it's not really possible for us to tell and let's just see yeah it's still there okay cool delete that image bye deleted is being cleared out of the database it's definitely still taking up space on my hard drive okay so we want to figure out how to do the actual image preview so I forget to say once before shouldn't the image URL determined the empty image - yeah we could move that helper into the product but I'm not going to do it yet so here's another project those working on once where I first figured out how this worked so we're going to kind of pour it over some of this stuff from here so you have to use this file Reader API thing and we have to listen for that anytime like the actual file input changes so if we go into our banner image here we can add an event listener on the inputs which i think is for change i will just call this update image preview so we'll add some methods onstage image preview and what did i do here now remember if it needs to take in the event or anything ml okay so we need to declare we need to give ourselves access to the actual input field so you see I have like VL equals logo field so we can do the same sort of thing here for this one so we can say V L is going to be image fields and let's just copy all this for now and think about what we need to change and what's actually happening okay so the first thing that we do is declare that we need to have a file reader and then we set files equal to the files that are attached to the image field so this is not going to be Al's doll logo field will be L image fields because you can have like multiple file input types where you can upload multiple files at a time this comes back as an array file reader is a native browser API so if you look at mdn file reader you'd see here so let's let's you look at that stuff yeah that's a good point about the getting the eventtarget stuff I'm just going to leave it this way for now and then we can look at refactoring towards using like the actual target so what this was doing is if there were no files there so someone clicked to upload a file and then hit cancel that clears out the file that's there so you want that to go back to the placeholder in our case in our case that's kind of that's going to have an impact on how we pass this prop through so I think what we actually are going to want to do is pass those through the way that we were going to do before so we're going to pass this through as a string use single quotes here because you're going to want to be able to pass through null just so that we can move the placeholder behavior into the actual components so it's just pass through null if there isn't one okay I don't know what the difference between l and r f is actually i can't remember what ref does let's just see if we had weird random errors because we broke something unexpected bracket I hate when you get errors that are really illogical ovl is deprecated all right let's just switch to using the event version then so I'll take an E and we'll just do e got targets of files everything's all broken I hate when you get these errors because I literally have no idea where they're actually coming from but it's likely related to this stuff here so if I was to just pass in this placeholder thing I wonder if that would be it would be happy with that or would still be re okay so that's better so part of the errors related to that and then we can go and get rid of this thing so we're not relying on that stuff at all okay so null is a string I wouldn't think that it would want that because like once you do it this way it's expecting like real JavaScript oh yeah you're right now this smell should be a string got it okay I wonder where it's trying to generate one of where it's trying to get that from that must be oh because of the image preview because of this thing here okay so let's make a an image URL computed property how is that doing this here update little go preview so yeah we have to generate the entire URL string okay okay I'm thinking about where we want to put this string basically okay so we're going to have a image preview here we'll just have it be null to start and then we're going to have like a mmm I hate when you're trying to come up with a name for something that's different as in this one because basically what we want to do is have this rely on a computed property so maybe it's like image preview path and this can return this done image preview null scuse a template string for this and the side of being default image this will be image preview okay so here this clear logo preview thing can just be image preview equals now for now I don't want to do well we could do the image URL in the model let's just wait and see where it goes and think about it because I can't decide what I want the behavior to be when someone hits cancel on the upload thing because you can't go back to PHP to get it then but it depends if there's already an image or not so we'll figure it out just chill it'll be fine okay so here this is actually going to become image preview and this is now going to be I think we're going to want to put baked the URL thing in here too or just return the Sun image preview and this will be image preview path who knows what's going to happen okay dog food hey we have a preview image so now if we want to get rid of that we should be able to click this and hit cancel and it goes back to the placeholder which is kind of clunky admittedly but it's better than nothing so let's save that and see what happens Jain error that was a bizarre turn of events is it just an edit screen that's failing so something view is failing here let's check out a would have even saved to the database so it did put the image there but now it's complaining just that it can't compile the template which is a little bit weird so let's go and see what we've been trying to pass through is the banner image thing that doesn't seem like it should be broken to me though because this isn't a that needs to get passed there's a string okay how come it's not a string already I got to put single quotes in here quotes everywhere now that's busted so we have to carefully consider what quotes we even want to use maybe that'll be better surprised that maybe it'll still work I don't know okay surprise that image isn't showing up okay so I think we're not even taking advantage of the default image anymore so can we just do default image here there we go okay there's our dog food yeah I wonder if seems like it doesn't care that we are passing through the weird sort of like HTML entity versions of the of the quotes so I'm not too worried about that but let's see if we just use the raw ones that makes it nicer yeah that's nicer so let's just roll with that cool so the behavior that is a little bit weird that I think I still want to figure out if you haven't liked something with an existing preview image and I click this and I cancel hmm I expected that to go back to the placeholder but I guess that's because we haven't actually uploaded an image so if we clicked this send it to dog food clicked it again cancelled we'd be back to a placeholder state if we save that it doesn't care because the way that the backend is set up right now there's no way to tell it to basically delete the existing image so once you've set an image there's no way to get rid of it so I've been trying to think about how I even want to handle that because it's a little bit kind of bizarre I don't want to have it delete the image when you hit save if you haven't set a new image because like you can imagine a situation where I go here right now the file input has no value right because I haven't clicked it to choose a new image so I don't want hitting save here to delete the existing image just because I didn't attach a new one so the only options that I can really think of are basically like another input that kind of captures the state of whether or not I've actually decided to explicitly get rid of the image it's almost like it's almost like we want to have like some sort of way to eliminate this image by like clicking like an X in the corner or something and then have that set a hidden input field or something that's like remove banner image and have that set to true which I don't know is kind of crappy there might be cases where you'd want to delete an image so one thing that I want to support with this app is say you don't have an image because like you're not artistic or something it would be nice if I could at least you know make this gone but then like at the top of this card have like border top like a pixels solid and then just like use whatever the theme color is that you've chosen and maybe it's bigger than that but you know just like something that's like okay it's still kind of styled even though you don't have an image so if you have a crappy image and I have a better one and just want to go to like this basic styling that's just based on the title with like a little bit of an accent color it would be nice to be able to support that so I do want people to be able to eliminate the current image but it's not the end of the world so um let's run my great the whole database and just kind of go through from the beginning and make sure everything seems like it's working so we have the seeded products if you want to add this dog food product choose a banner image which would be this cropped dog food we'll save that inevitably I would like to make this not be like a full page refresh form just have it do a little loading spinner or something anyways if we go back now we can see that's still there I can set it to be really expensive dog food and image is still there which is good the world's best expensive dog food uploading files is hard because it's a subset of forms which are hard still the worst part of doing things on the Internet is forms and so one thing that sucks is like when we refresh we get this sort of like jank so there's two ways that we could solve that we could either show a placeholder until it loads so one way to do that is basically to sort of copy this markup but simplify it a bit and I have to go and look at my layout app to have the cloak on this so if I have like a product checkouts new ok so we can throw the cloak on our app container here so that we can style things underneath it um so he pasted this in so one option would be to copy this mmm maybe we don't even need anything here actually we could just have a div that takes up all that space but we can have kind of some like view helpers so this displays it as blocked when it's cloaked but as soon as the cloak is gone all these helpers get made invisible so we can say V cloak block here this will be displayed as a block level element until we use done compiling so this way no we just have to wait for the image to load after view compiles which is not too bad I don't mind that the other option is to like simulate kind of what happens here where it kind of fades in once the whole thing is ready which is kind of neat too so if we wanted to do that I think we could just comment this out and then on the form we get basically steel y half here so transition fade slow up here and then find the bottom of this for me close up that transition to egg I'm just going to see if that looks cooler might feel weird to the rest of the page it's going to be there and probably you'll still get a weird problem with sizes yeah it's almost like its own flavor bad so this is hidden so if we did be cloak hidden here pretty sure it's still going to be as bad because it's just not going to like you can see the whole footer so that's horrible so our previous option was certainly better where we sort of simulate that so one thing that I've been thinking about which I haven't tried yet but maybe this is a good place to try it is this is a common thing that I run into with view when you're using view in a mostly server rendered application where you have the whole site is rendered on the server but you're sprinkling little view components in you have lots of situations where most of the page is loaded but the gift way for the view components flowed and it pushes things around so this kind of pattern of like creating another element that takes up the same amount of space as the view component is something that I do pretty regularly so what I've been thinking would be kind of neat is to extract all my view components into blade components that contain view components just so I can put like the placeholder version of it there at the same time so I kind of want to see what that would feel like to do because then you can pass through all the props and stuff the same way since you can like have scoped variables I get passed in with blades so let's look at our resources views so I'll put this in products folder addict components seems fine and you can call this inner image upload blades uh PHP so then we can copy all this stuff and paste that in here and then instead of passing through product image path like this we would actually just have to pass through let's copy all this before we screw anything up and just paste that in here so we don't forget it but we can just pass the default image through to this component so this could just be a default image and then when we include that components product stock components it's not a real blade component because I don't need that slot behavior so I'll just use a regular include banner image upload and then we can pass default image I would call it defaults image camelcase and then we can use this logic for that it actually will be kind of nicer because we don't have to use these single quotes everywhere so we can just say product which path then product image URL otherwise no so let's see if that does the exact same thing seems to work yeah let's just like take a random screenshot of something or [Music] it's changed the image to something else here's like some random code seems to still work change it back to our dog food yeah so that's kind of a neat pattern you can include kind of the cloaked state inside your component then and just load everything as blade stuff eat I'm trying to think if there's anything else I want to do right now I think that's a hangover like 45 minutes and got like the image preview stuff working so yeah maybe I'll call it there I'll try and get this I'll think about how I want to be able to delete images and stuff going forward but there we go so this is how we do a kind of image preview stuff I would view jazz so you just have to use this file Reader API make sure that you have this onload callback that you register with the reader so whenever the file thing actually changes you get the results and stick it in there so good to go trying to look at this and think for a second how this is even working like what's tying this to the actual oh yeah you're just passing in like the actual file objects when you use the reader so this doesn't actually care about the element at all yeah drag and drop would be cool something I kind of wonder is if it already works so audial don't know this but most file upload buttons you can already drag and drop onto the actual button get me my doc here so like if you do now that didn't work in that case if we had an actual button visible here that's weird that the back behavior didn't work so this thing that's set the pseudo hidden if we got rid of all that stuff it's weird that you still can't see it let's put it here oh it's because it's on the hover State yeah so let's go to this hover thing okay so see how you have like our gross ugly file thing here you can actually just like drag anything it was long as you drag it onto the actual choose file button that already works which is kind of cool it's like built-in drag-and-drop but of course that doesn't seem to work when it's hidden which is unfortunate or not on the label either which is too bad so I would like to add drag and drop so that'll be something that I think about going forward trying to make like a nice experience for the sort of thing is really really hard so I had like a book in here or something oh it's weird that we don't get the placeholder image now why don't we get the placeholder image you know why because what we did here isn't quite the same is it so default image comes in we want to actually know the string still there which is unfortunate okay No ah okay so I think sadly still have to do some hacky stuff here pass these through as strings and and all these quotes are really confusing okay yeah that seems to be the ticket oh so we still got some quote problems I guess we got to put quotes around this again like that there we go okay anyways that seems done now I'm going to think about how to clean this up more because a kind of sucks Oh we'll figure it out maybe this maybe this logic should actually go in here where there's more context around the fact that I need to get passed in as like a prop yeah the problem is not like not doing the null check it's making sure that we're passing know as a string and not PHP no because it gets turned into JavaScript when it's put in here so yeah it's all kind of like hairy that because view is going to actually as long as you have this : here anything inside the quotes gets treated as a real JavaScript so we have to make sure that we echo out the string null because that will get rendered as this which will turn into actual you know JavaScript know if we pass through just PHP null null here we'd actually get nothing because when you cast null to a string you get nothing but we actually want it as a real null string so anyways yeah I think that's enough for now I'm going to think about cleaning this up a little bit but hopefully there was some interesting stuff in there and I'll try and do another one of these I probably next week well see you people
Info
Channel: Adam Wathan
Views: 14,927
Rating: 5 out of 5
Keywords:
Id: XUXpcbYQ_iQ
Channel Id: undefined
Length: 52min 49sec (3169 seconds)
Published: Sat May 13 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.