Angular Image Upload with Node and MongoDB (MEAN) 2020

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to this video on image uploading in this video we'll concentrate on how we can upload images to a server using angular node express and mongodb so i'll just give you a little tour of what we're going to build today in our angular application we have this ability to create a profile so i can enter a name here john doe and i can just choose a file these are meant to be images but i've just chosen youtube thumbnails as that's easier to do and if i just choose one of these we can see that we get this image preview here and if we click the create profile button it clears the image preview it makes the request to the server it stores it on the server and it shows us in the ui and we can see that we're using mongodb and if we just refresh this we can see that we've got this second object added and it has this id underscore id that's auto generated by and there's a name and there's a path an image path so the images they don't sit in the database they actually sitting on the server and if we make our get request to the profiles we can see that but if we were to go to a static folder images we can see that it's actually sitting there on the server so we'll build this today so let's get started now i'm going to open up vs code i've created two folders already front end and back end there's nothing in the back end folder at the moment and the front end project that's just simply a boilerplate angular so ng new frontend was the command type there and what i'm going to do in the source folder in the global styles here i'm just going to copy in a few just to make the app look a little bit better and i'll go into the application and i'll just delete all the boilerplate from the app component html file so the first thing we want to do is we want to open up the app module and we're going to need a few imports here we're going to need to import the reactive forms module because i don't want to just upload a image by itself i actually want to do this using angular's react forms because if you're going to make a profile anything it typically comes in a form with string and also the data associated with the image so we can get that from angular core and we're also going to need to do some http requests so i can go ahead and import the http client module and that also comes from angular core but we can access the http section within there so we just need to add these to our imports so we get the reactive forms module and the http client module and it should be noted that this reactive forms comes from at angular forms and the http fly module that comes from angular common and that will get rid of those errors for us okay so we want to go ahead and create a couple components we have this component here to create the form and this area where we can display the profiles so the image and the text so i'm going to go and just create with the angular cli these two components so ngg c i'm going to create a components folder and i'm going to call the first component create profile and i'm just going to go ahead and i'm just going to skip the test here so skip test equal to true and we'll let that load and we can see that it's built this component here and we'll also do an nggc in the components folder and we'll have all the profiles without the test file so we'll skip the test say true and i'm also going to use the angular cli to generate a service here and in the services folder i'm just going to call this the profile service and once again i can just skip the test here this service is going to be used to make the http request so we can delegate that logic outside of the component to declutter it so we have all those there now if i go back to our app component i can go ahead and i can use the prefix app and i can call those components so record those create profile and we've also got app dash or profiles so i'm just going to run an npm start it might just take a little bit to load because of the iv compiler needing to pre-compile things but while that's loading for us we can start to think about how we're going to create this whole upload and i'm just going to create a folder here and i'm going to call this models and inside this models folder we're going to have a profile model and we can just go ahead and export an interface here so this is just going to be the blueprint of our form and our profile so i'm just going to call this i'm going to use underscore id we take a look at we can see that they create an underscore id and just so i don't have to do any mapping or anything like that i'm just going to call that id underscore id with a string type i'm going to have a name which is a string type and an image path which is also a string and we can save that and we can close that up and now that this is loaded for us we can refresh our page here and we just get this simple create profile works and all profiles works with the styles that got copied in and i'm going to link a copy to the github so you can just copy those in so we first want to start to have the ability to upload the form so in the create profile we can go ahead and start to look at that and i can close this module for now and i'll also open up the typescript okay so what we want to do is we want to create a reactive form with a name and an image and we want the ability to upload an image so we're going to first do this on the front end just so we can see it visually before going ahead and connecting it up with our server so let's just go ahead and start to write up the template here so i'm just going to have this h1 and i'm going to say create profile i'm just going to have a hr here and then we have a form and i'm actually going to right because we're doing a reactive form because we need the reactive ability to type script first ability opposed to the template driven form because of the complexities of the image upload so that means we can actually go ahead and start in this typescript file and start to define a few things here so to get our form going we just need to import form group and form control and these are going to come from at angular slash forms now i'm also going to import the profile that's the interface we just created so we can just go back to directories for that and in the models folder we can access the profile and i'm also going to need the profile service but we're yet to create that and we'll just make the image upload without the http request for now and we'll come back to injecting the profile in the constructor so we're implementing on a net here and in the constructor that's where we would have our profile service but on the ng on a net the constructor's reserved for dependence injector ejection so on the ng on a net this is where we will set a few things but first we need to create a form so the form is of type form group and we're going to have two bits of data associated with that we're going to have our profile which is of the type profile and we're also going to have some image data and that's more associated with actual data opposed to the path to the data but it's still going to be referenced as a string so in the ng on init we can say this dot form is equal to a new form group and a form group takes as an object the key values of the name of the fields and the particular form instance so we are going to have name here which is going to be a new form control and initially it's just going to have null set there which is of the form state and as a second argument here we can have new form control and null okay so we're starting to create our form we will also need to have a button and a something that happens on a file select as well but i might just start by writing the corresponding template for this form so because we're using a reactive form we can just bind this to a form group equals to form and we can have some sort of submit action going to be called and i'm just going to call this on submit so in the typescript i'm just going to have on submit and right now i'm just going to say console.log submit profile save that so i want to have four divs here so i can just use emit here and i can just say div times four i get four divs and in the first div we want to have an input and it's going to be of the type text and we don't necessarily concern ourselves with the name or the id but we will want to have a placeholder and we'll just say enter name so on this input field where we're entering the name we can sort of use the form control name that we created in the type script here by creating a new form group and a form control instance we can say okay this is going to be equal to name and in our second div here this is where we can have a input of the type file and once again i can just delete the name and id but this will have some sort of change event and this is built in to the browser and i'm going to call a particular function called on file select so if you select an input type with a file type it can detect changes so when you change the file and we want to do something with that so for now i'm just going to say on file select and because it's coming from the dom it has an associated event with that and that event is passed in so in this case we have the change event or the file change event so that's what we're going to pass in here and we can just create that so we can say on file select here and we can just create a shell here and we know that it's going to be an event of the type event and we can just say console.log something selected or maybe file selected we can go ahead and save that so back to our template there's two things we want to do here the first is within this div here and that's just saying if we have some image data display an image so this image data is going to be on an ng if if we have any image data here we can go ahead and display an image and we can bind the source to the string of that image data and we can just give it an alt that's bound towards the name so we can just say whatever the form is the value the name of that and the second thing that we wanted to do and the final thing in this template is to just go ahead and have a button the submit type button just so we can prevent the default of submitting it to the server and because we've got this submit on the ng on the um form on angular form this will overwrite that default behavior so we can just say something here like this like create profile and save that now if we go back to our template we go to the browser we can see that we have this form here and has an input here and we have the ability to choose a for uh a thumbnail or a profile picture if i click ok we can see just we've got this file selected now we do want to work on that but it's good to note that that event being passed through does actually fire this function so when we select something so we want to just work on the logic of that so in the on file select method we want to choose the file and we can do that by creating a constant variable and what we have to do here is we have to just wrap this event dot target in parentheses so we can typecast it as a html input element because this is just a generic sort of event and this could have multiple forms so we just need to cast it to let it know that okay this event is coming through a html input element the file type so what happens when you select the file is well you could in some cases select multiple files but because we're doing a profile selection we just want one and by default this is just one so because we have one image to select from a possibility of an array of files we can say that for those particular files through that event we just need access to the first one of the zeroth index so another thing we want to do is we just want to check for the allowed file types because obviously we don't want to upload videos or pdfs or anything that's not what we're expecting so i'm just going to write an array here of the allowed file types and these are actually the what's known as the allowed mime types and the mime types is just similar to the file type except we have this little bit of extra information here that prefixes the type or the file extension so for an image for a png file extension it's prefixed with this image slash and we can do the same thing for our jpeg and one thing to note about jpeg is there's two different types of jpegs there's one with the jpeg and one with the jpg so we're just going to allow both of those in so now we have our allowed form types one thing that we actually want to do is we want to on the form we have to do something special because we're not just dealing with text so we have this file here and what we want to do is for our form control field here for the image we just want to patch that value with the particular file and we can do that with this this dot form dot patch value and this is just a special method that will allow us to have our image set as the particular image file rather than some sort of text or string that it usually is so what we want to do is we want to i might just shift that up a line here what we want to do is for our allowed mime types we want to say okay well if you know a file was selected because you know they could open up the file selector and cancel they might choose a file once then switch to another one but then cancel it or something like that so to avoid any errors there we can say we can check okay is there any files and are the allowed mime types and are they is it one of those being included so i can of course use the includes array method on the particular file and the file has a property associated with it called the type and if that file type matches one of our allowed mime types and it exists in that scenario then we could read the file in and there's a special constructor that we need to call here and it's actually a reader instructor so we need to instantiate an instance of that and we can say okay create a new file reader and we have that object now instantiated in the reader variable and what we're doing here is we're just we're reading for something on the load of the image so we need the image to be loaded for us to use it and we can have this sort of callback sort of asynchronous sort of scenario here where we have this arrow function and it can go ahead and you know we can set this image data and this is where it's distinguished between the image path on the model the actual image data is the reader dot result as string and if you're wondering like how like an image can just sort of be read in what happens is it's actually converted to a massive number of you know numbers and letters and stuff like that and they correspond to the pixel and they associate a color with that and based from that text it can be interpreted as an image and the computer can display that image for us so when the files read the file when it's loaded for us we set that image data equal to that and i've just casted it as a string here because it doesn't by default recognize that but if we have the reader what we can do here is we're going to have this data so we can read this data there's a function or a method called read as data url and that's just built in with this file instance and so this reader dot read as data url this can actually read that file for us and convert it for us so we can read that in and we can save that so on the on submit thing on when that button's called we actually want to actually delegate that to a service to make the http requests for us but and we'll do that but i just want to set up this dot form dot reset because we're using a reactive form we can just clear the fields but this is corresponding to the form control fields and while we're using reactive forms we haven't actually explicitly given this template the form control correspondence so due to that it won't reset that image for us but we can just simply set the image data equal to null because that's just in the view there as a viewer so if i save this what i'm able to do now is if i choose a file click open we can just see this image for us being viewed for us here so i want to create the ability to send this data and store it on our server but just before i get into that i just want to create the shell for how it's going to be displayed because we already have some of these display in the um database from before and i'm going to show us how we can connect and set that all up as well but just because there's no data if i was to start from scratch there wouldn't be any data in the database at all i wouldn't be able to show it so easily so what i'm going to do is i'm actually just going to work on this all profiles field just the shell of it so what we want to do is we can start by just giving it a header give the h1 and just say all profiles and we can have a hr to separate the heading from the text and i'm going to have it give here and i'm going to have to go through our profiles to be able to display them so so let's actually just work on the service because i've got that api set up already and then we'll go to building api so in the services in the profile service we can create a few things here the first is creating a private variable here for the profiles and we can give that the type profile an array of profiles that's what we're going to expect back an array of names and image paths and id and i can just bring that in here from our backup directory in the models in the profile interface and we're going to need a read only variable and the read only is just there so we don't accidentally modify it in any way and this is going to be the server that we have here and if i went back one we could go to slash api profiles on port 3000 so let's go ahead and get that and we're going to need to do dependency injections to get the functionality to connect to the http client so to make our http request we need that there if i do a control period control period i can get that from angular common slash http and i'm going to bring that up a little bit so we want to be able to get our profiles so we can have this method here in our service called get profiles and this is going to be this dot http it's going to be get we're going to have a return type here and we're going to say this dot url to make the request to that url now we're going to need to do a few things here before we can subscribe to our observable and i'll just write the type in here so what we're expecting back is our profiles and we can just sort of specify that by saying well we're expecting an object of profiles back and our object of profiles is has a profiles key and a all of the profiles in an array and that's the format we're expecting back so the way i've set it up is we'll need to actually use pipe use rxjss rxjs to do a few of the pipeable operators and in particular we just want to map something here and we just want to get the profile data whatever is returned from the server and what we want to do is we just want to give back the return and because we got this object being returned we just want to map out of that and give back the profiles so we can do that quite simply we can just say profile data dot profiles and they'll just take the curly brackets out of our response and it was like that because of the json format being sent in so what i'm going to do oh i need to i need to import those from rxjs so let's go ahead and get those so we can import the map pipeable operator and it works very similar to how the map works in you know in esx javascript so it goes through the object or array and it can map each of the elements to something so we are just simply getting out of this object here and returning that result that's nested within there so once we have it in the format that we want we can go ahead and we can subscribe here and now that we have the profiles actually should be down a little so this should be here like that so we can subscribe and get the profiles and what we can do here is well we have these profiles in the service and it's in a service so there's one source of where the profile sits so you know if you're showing it in one spot in an application using another component that the state remains in sync and we can just set that variable that's accessible globally equal to the return of the get profiles so with that we can start to write our html so we just need to check to make sure that we actually have our profile so we can just do an ng if here and we can see if the profiles.length is greater than zero and if that's true what we can do is we can have this div and we can say okay for all of the profiles so we can let each profile saved in the database of the profiles array that's coming back and i'm just going to throw in this here it's just a class that i said earlier in this global styles for that each of those profiles we can display an image and we can once again bind our image to the typescript and we can set that to the profile dot image path coming back and if we wanted to we could display an alt tag to say okay we can have a profile.name set for that and i'm going to have a div here within this div i'm going to have a span and this is where i'm going to have the profile dot name so let's save that okay we've got an error so let's see where that error is coming from okay so ng if profiles dot length so did i call it something else let's have a look let's go to the component here ah i've created the service but i need to hook up the component with the service so let's do that so let's go ahead and in our constructor here let's just inject the service that we just created so the private profiles service which is of the type profile service and we need to get that in so on our ng on a net what we want to do is we want to get all of the profiles so we can get those like this we can use the profile service and just call that method we created here get profiles i might just make this terminal a little smaller so let's go ahead and let's just in our template in our class here above the constructor let's just have a variable here which is going to be of the profile array type equal to an array and we need to import this in i want to separate the angular stuff from our stuff with the space so let's just save that okay well we don't get any errors but we're not getting our post here so why are we not getting our post here we're calling the get post profile but we're not actually setting it equal to the profiles so if i was to do something like this this dot profiles set that equal and the reason this is occurring because we're actually subscribing from within the service itself so sometimes we subscribe outside of the service and you know we return something like this and subscribe to it but we're returning an actual subscription here and we're doing this on purpose because we're going to need that later on when we come to our uploading um and related to the image type as well but we'll see that later on so i'm going to need to set up a subscription here and in order to do that i'm going to need to have a subscription made available as an observable so if i go to my component here oh sorry if i go to my service here and i go up a little bit i'm going to create a second variable and it's going to be a private variable because we'll have a get method to access it and i'm going to call this profiles and i'm going to give it a dollar sign to signify that it's an observable and the particular observable that we're using here is a subject and the subject we will need to import that so let's get that in from our xjs and it's natural observable and an observer at the same time it's a special type of observable in which we can actively call so when we upload an image and then that's loaded and we make the http request we can actually update our profiles so you know it actually updates to the list so rather than have one and up click create profile and nothing happen we want to constantly be looking out for a stream and we can actively add to that stream by creating a profile making a http request and observing this particular subscription so the particular subject type that we're after is the profile array that's what we're expecting back and we can have a special method to get this and it's no special than this but in terms of its implementation but what we're doing here is for the particular stream that we're observing we want to return that to where it's accessed as an observable so then that can be subscribed to that subscription me made available so for this profile variable here we can actually say we want this to be as an observable which is a method that converts the subscription that can actively fire it but then we can use it as an observable elsewhere so that means we can also when we subscribe to the get profiles and because we're using http we don't have to unsubscribe and anything built in we don't have to unsubscribe it's only stuff we create ourselves that we have to unsubscribe to but when we subscribe to the http request and we get back our profiles and we subscribe to it this is where we can actually actively call that stream of all the profiles and we can just say okay we can just call the next one method and we can add this dot profiles to that actively asynchronous stream that's going to allow us for that seamless feel of the uploading of the profile so if i return to the all profiles component i'm going to need to have a subscription here so i'm going to say okay i'm going to make this private variable because i only need it in this class and i'm going to call it the profile subscription and this is of the type subscription and this comes from rxjs okay so that means on our on g on a net we can get our profiles which will happen but it won't be set to anything it's just getting all the profiles and we can call that subscription or this profile subscription here and what we can do is we can set this equal to something that's coming from the profile server so this get profile stream as an observable and since it's so i need to put this profile service before that because that method exists on the profile service and now we have this available as an observer or that means we can subscribe to it so let's do that and for our profiles of the profile array type we can actually get back and this is where we can set our profile so this dot profiles this can be equal to profiles that we get from the subscription and this has to be a method so we need to call it like that and because we're creating this subscription ourselves we need to unsubscribe to that so we need to actually implement ng oh sorry on destroy and this will auto import it from angular core the life cycle so when this component is destroyed such that there's no memory leaks we can have this ng on destroy and this profiles subscription variable that we set here this can be set to unsubscribing to it so not set but just unsubscribe to and with that we have our profile service being implemented for the getting of the profiles so if you take a look at our application we can see that great okay we've got our images and our associated name coming in and it's been rendered to the ui so just to sort of recap what's been happening on the front end here we've created two components we've created this create profile component and this all profiles component so i'll start with the create profile component we've used a reactive form here and we have a typical input and we have a not so typical file input and when we select something that's when we can call this on file select and we can submit it through this on submit here but the on file select in the create profile what it's doing for us is it's choosing a particular image the first image and the only image we're setting the particular image to that file type so we have to patch the value and we're just allowing the png and jpeg types so if they are the selected files we can read that file in we can once it's loaded we can set the image data to be the reader result as a string and then we can use the reader to read as data url for the particular file and if we click submit we want to hook that up to submitting to our database and we'll start on our node api and connect it to mongoose and do that and retrieve it and the second component is the all profiles component and the all profiles component this displays all our profiles and we're using a particular service here that we're accessing so this is the sort of tricky part is we are getting the profiles and we are having a particular subscription within the all profiles component and this subscription it's waiting for any changes in the profile so we're giving that file the ability to detect that because we cast it to an as observable meaning we can subscribe to that but this profiles subject it's detects any changes so firstly we're just getting the profiles but when we get the profiles we're actively having this ability to trigger the next profile and this subscription means that when we call the get profiles it's doesn't stop there it's actively subscribed to and we can make further you know we can add a new profile and that detection will be noted by the area that is called in so this subscription in this all profile component has access to that profile stream and it can subscribe to it and then update this profile that's displaying in the ui and set that variable to that so we can display that and of course unsubscribe to that to mitigate against or avoid completely any memory leaks so that's the angular side of things so let's start to work on the back end a little bit and then we're going to need to come back and fix a few things up on the front end but on the back end so let's go ahead and i'll open up a new terminal here and i'm going to cd into the back end and what i'll do is i'm just going to create a node package so i'm just going to initialize and i'm just going to do with the dash y flag to skip all the questions as default so in our node backend what i want to do is i want to do an npm install and i need express just the lightweight framework we're going to use mongoose as our database or our object document module and that's just going to provide us some nice methods to interact with our database easier so rather than writing well it's it was sql rather than write the queries would be similar to having a orm and i think they call it odm or something like that in this and so we're going to have mongoose and the ability to easily save and get things from our database we're going to have body parser to pass the body of our request as json format we're going to have malta malta is a library that helps with the uploading of files onto the backend so rather than just receiving text in the form of json it will allow us to have the image uploads i'm going to have cause and i'm also going to have dot env here just to make requests across origin so if it's local host 4200 to low post 3000 without any problems and the env is going to be used to just have our database password and username in a env file so if i go into our back end here we can see that we've got this package.json file and i'm also going to just do an npm install as a dev dependency of nodemon and what nodemon allows us to do is it allows us to keep the server running while we make changes without calling the server again so we'll let that load and we just want to go to our package.json and we want to just create a new script and the script we can just call it start and we can just sort of delete this and the script that we want is we want to call nodemon and we want nodemon to occur for the index file so we don't have an index file so let's just create one and i'm just going to call it app so app.js and i might close everything um i might just close all these files here for now we'll come back to those so now we have node one and we have our app file we can just say nodemon app.js and we can start to write our app.js file to start our server so okay we will actually create an env file here and an env file is just our environment variables and this is where we can have things like our username and password for the database that we're connecting to so i'm using mongoose and i'm going to have a particular user and these variables there's no strings or anything like that there's no commas it's just uppercase for the key so there's going to be the user so we can call this user we can have password equal to password and we can also have the particular database that we're using here and i've got a mean one so i'm not going to show you how to create a connection oh i'll show you how to create a connection to mongodb but you'll have to install compass by yourself and if you're not sure how to do that i've got a video on a graphql api created using and no using the mean stack so yeah it's quite simple you just need to pretty much download it and click a few buttons around here and you'll get your username and password so it's no big deal but once you've got that saved you can go ahead and just close the env file and now we'll have access to that so we can create our mongoose connection so let's go ahead and import those variables so we can require the dot env and unlike other variables that we require we can just immediately require it and call it by using the config here and this is going to give us access to those environmental variables that we set now i'm going to need to create a server here i'm just going to require a few packages here and i'm just going to copy these in so i'm going to create a constant variable for the path to require the path because we're going to need to access the file system because we're going to in our back end here we're going to have a folder and we're going to call it images and this is where our images will sit uh we're going to have a body parser we're gonna need to pass some json data and we're gonna need to retrieve json data and we've got express of course because you know that's the framework we're using and mongoose and i've also got cores here because i want to rather than set the headers for the particular um you know the methods we need and cross origin and all that sort of stuff i just got this cause package to quickly get through all that so we can just create our app by initializing or using calling this express method that starts our application of course we're going to need a particular port number that we're using for that so the port or the ports because there can be more than one in the event that we deployed this is the process dot environment and this is the same area that we can access our predefined environment variables so we got user password and database but built in we can just access this port here and this will just be the port in the environment so if you deployed it to heroku for example we'll just get the whatever port their servers are running on and we'll get that but if that's not set we need a backup and this is what we'll be using is just our local host and we'll just define that as port 3000 so we want to create our mongoose connection our connection from our node express api to the database so let's go ahead and just for mongoose which provides us an easy way to connect there and what we can do is we can go to our database and we can see um actually i might just type it out it might be easier just to type out because i got all the variables in it so i got the you go to mongodb and this is the same for anyone and that particular server srv colon slash slash and this is where we have environmental variables so i've used the back ticks to use this template literal and we can access the process.environment and that user variable we set in our env file or we can now access it so whatever your user is that's what it'll relate to and then we have this colon here and we can have this next process environment variable relating to the password and then we can have at cluster and i'm just using the free tier so you only get one uh cluster so you'll need to create a cluster if you haven't already created one and this is mvcmf dot db dot net slash and our final variable here our environment variable for the process dot env and this is our database so in our campus we can see that ours is called mean and we're working in the profiles table or equivalent so okay in later versions we just need to pass in these two objects or this object which has two particular mappings and that is use new url parser we just want the new url passer and we want use unified topology and i think it still runs if you don't have these you'll just get a deprecation warning so this returns a promise so we can have dot then and dot catch so in our dot then what we want to do is we want to just okay so we've got this app here on this app if we've made our database connection to we can go ahead and just listen out for that and it's going to be related to the port that we're on and i'm just going to have a simple console.log here and i'm just going to say okay the server is running on port and i'll put the ports that it could be otherwise we can have this sort of dot catch here and whatever that error is we can just say okay console.log could not connect to database server and we can actually pass in the error that we received as well so i just want to add a few more things before we create and test that out we want to have app dot use and use the body parser which is going to pass the json format in the body and we just want to use app dot use and passing cores here so let's just save that and let's just see if we can get our connection we won't be able to do anything but let's just see if we can connect to that server and we're expecting a nice message back okay cool our server is running on port 3000 that means we've connected to our database and our server is running our api is running and is expecting some requests so okay so we need to have our routes in order and i was using this api api slash profiles right but i also need access to these images and the images is known as a static asset and it's just a location on your on the server or on a computer somewhere that looks like this but it's probably in linux or something and has this file structure um and it can just access the image because the image is saved in the images folder on the server so in memory um on a hard drive or something like that and it's not feasible to have large images and even if you had videos or something like that on a database you'd quickly run up that resource and that's more expensive than hard drive data so we can store it in here but we need to by default node doesn't let us just access this we need to say okay if you do access the images and we've seen that before if i went to here images were actually on this slash images route here and then whatever the name of the file is so what we want to do is we want to say have this middleware here app.use and then we're in this app file here where we're creating the server so relative to that would be in the images folder so we can say slash images and then express has a method in it called static and that allows us to access this static resource here from our uh files and we've imported the path variable so we can access wherever because it might not just be on our computer might be on someone else's computer we don't know necessarily where it is located but we know where it is located relative to this uh base so we can just say from the path we can just join that up with where the path is and we can just say okay we're going to be on the base level here in the app file and relative to that we have images and it's just one distinguishment between um this and the join is the join uh it's built into express and it factors in that it will be in the first file folder so since it's going to be in a folder that's taken care of for us so we can just say okay access the images folder and this route this will allow us to access our images once they're uploaded so i might actually just delete a few things here because it might mess things up if i don't delete a few things so i'm just going to drop this collection here and i'm just going to say profiles drop collection drop in progress and if i return back here [Music] the last thing we need to do in our app.js file is just use a particular route so by default it's whatever the file or whatever the name of the website is slash api and because we're going into profiles i'm going to call or go into the profiles route and rather than define it here our files already getting quite big so let's delegate this to a profiles route and that means of course we'll need to import that and create that uh i might just do the import first preemptively as i know what is going to be built so we can have these profiles routes and we can just require that from the routes folder and the profiles folder i'm just doing that preemptively because that's the last thing that needs to be done in this file here we're going to get an error because it's not created yet so let's go ahead and create that so we can create a new file in back ends and we can just say route profiles they'll create the folder for us in addition to the file and open it up i might just do a control c in here because we know it's going to error so we can get in express so let's copy that in i'm going to delegate the bulk of this to a controller so i'm going to create a controllers folder and within the controllers folder i'm going to create a profiles file and let's just start off by writing some routes here in the routes file so we're going to get the profiles controller and we're also going to get a helper function and that helper function's going to relate to malta and that's going to connect the image storage up for us when we upload or make the api request so we need something to storage here but let's just go ahead and create our router and that's coming from express and we can use the router there that means we can make the endpoint a get request because we just want to get the information first and we can say okay slash profiles actually i've deleted those from the database so we actually might want to do a post first but we'll come back to this as well so let's come back to this so the router that we're interested in is the post route and we just want to post to api slash profiles and this is where we're going to need to have some middleware in here and this is going to be the storage middleware so we're going to able to have a controller as well so we're going to need to import a profile profiles controller i'm going to call it post profile but there's also going to be this middleware here that allows for the storage of that and then of course we just need to export that so the router's accessible and i can't remember yeah yeah okay so okay so i'm going to create a model for this and the model is going to be called uh profile and we just need a blueprint of what our structure is going to look like here so let's just get that profile here and this is going to be a schema or a table but they call it a schema in mongoose and we just need to require mongoose to use that functionality and this is where we're going to define the shape of our database so for our profile schema or aka profile table we can call mongoose and we can call the schema function which takes an object and it has a name and the name it's going to have a particular type it's going to be a string and we can set it required to true and i'm going to duplicate this down and i'm going to have an image path and this is going to be of a type string and it's also going to be true and then we just need to export that and we export that model and what we do here is we just use an uppercase letter although when it's created it'll be lowercase um for profiles and also be a plural so make sure it's uppercase first letter and singular here and this will be the profile schema so we have our model available so we can just close that now and return to our controller and in our controller this is where we can require in that model that we just created and we can export we can use node to export the ability to post the profile and i'm going to make this an async function which takes a request and a response and it's an arrow function now so let's think back to what we're expecting here and we know because we just created the model but from the ui perspective we're going to put in a name and a file so we need to get that so we can use some object destructuring and we can get the name which is going to come from the request body object and we can get the image path now i'm just going to hard code it here to the location here of course um if you're deploying this you'll need to change the location to wherever it is i'm just going to do this just to demonstrate and then we just need the file name that we're accessing [Music] so on the request there's going to be a file associated with that and we need the file name i'm just going to put a note here so i might put this on github just note to anyone that you need a set path dynamically and that's for deployment but this is just to demonstrate image upload so we can use that schema and create a new document a new profile and we can do that by accessing that profile that we've imported and define and i can use object destructuring to map the name to whatever this name is and the image path to whatever this image path is and that's why i've named these variables accordingly so now what i need to do is and what's great about mongoose is it will just magically create this schema for us if it's not already created so we don't have to create one first but it also provides us with some convenient methods here so i'm going to have this constant variable create a profile and i'm going to use the await keyword because it's an asynchronous operation that's why i put the async keyword before the request response parameters so i made the function asynchronous so we can await the profile which is an instance of the profile schema so a particular document or row and we can just magically save that so we don't have to do anything like um you know any sql statements or anything like that and just save that like that and because we're waiting for that to happen we can say okay from the response we can just say okay we can give it a status we can give back a response of 201 resource created successfully and we're using body parser with json format so we can return some json here and this is an object and this is going to be the profile here and it's going to be the created profile and mongoose returned some other stuff there so we just want to return the document here so what we'll start to notice is if we were to look at our front end and we will start to look at the data that we're retrieving from the front end in our services file we can start to see okay so we haven't actually created that yet no we must have uh in our components in our create profile we can on submit uh submit data to the database so we haven't set that up okay so we need to set that up but we need to provide the shape here so i just wanted to highlight that we're returning this object here and nested within this object we have this profile which has an object itself of the documents so that's the post profile we're out so that means if we come back to our routes we can get the controller so that's going to be const profiles controller and we can require that in from the controllers slash profiles so we then need to post and have this storage here and i think it's pretty easy just to make the get request at this stage in the controller as well so i might just export that so it's available so i can export the get profiles that's going to be an async operation as well with request response parameters and we can just go ahead and create a constant variable profiles and we need to wait for the profile object and we can just go ahead and find and what fine will do it will find everything from the database so all the documents within the profile section and with that because we're waiting for that to happen after that's done we can say okay we can return a status code of 200 in the json format meaning 200 meaning it's okay and we can just use object destructuring uh object shorthand notation to map the profiles key to the profiles that we're expecting back which is all of the profiles so we can just save that and now we have everything in our controller that we need so we can more or less just close that but we will need to return back to our routes and we need to create the get route so we started that before but then we realized we needed the control there and we have this storage thing and this seems like a little bit of a mystery at the moment so what we want to do is this is going to come from a helper function that we're going to create and define and i'm going to call it storage because it's related to the storage of the image and we can require in backup directory go into helpers down to storage so that's going to break but we've stopped the server so that's all good i've just done that preemptively once again because they'll finalize this file here so if i start to go into back end here i can create a folder i can call this helpers and i'm going to call the helper storage dot js so okay so in the storage this is where we're going to want the multi-package so let's just go ahead and get that in and once again malta is used to have non-text input and handle that so it's going to handle our file uploads in conjunction with text so to use this we're going to need to have a variable here we'll call it disk storage and this uses malta and it uses the disk storage because we want to we need a way to get from the request to store that in our images file here we've seen from the app file we've started to have this ability here to use the images here but we don't actually make that api request this is just saying okay if we're using the images we can access this static resource so we need to configure a helper function to access this disk storage and this disk storage it takes two things in here the first is it's going to take a destination so we need to define where exactly it's going to be located and this takes a request to file on a callback we're not necessarily interested in the request the file but we need to write those down so we have access to the callback function that's all we really need here so what we can do is okay the destination is going to take a callback function and the reason it takes a callback function is we might need to filter a few things first so we need to check that the file types all right and then if that's done we can call back this function and then we can say okay store it in this destination so for that if we look at the callback here here we can see that it takes an error and a destination as a string so we're not expecting any errors and we can have some custom error handling here but we're actually going to delegate this to a filter so we're not expecting anything in here so we're going to filter out the images ourselves in a custom way but once again i want to point to the images folder from the location from the app so without the fold file uh forward slash now the second thing that this will take is the file name so it's all good we've got a spot to store the file but we need to be able to uh have a name for that file so we'll need the request the file and the callback here let me create that now recall what we're doing from the front end when we create something is we're checking for these allowed mime types here now we're going to use that but i was just wanting to highlight that we got this image png slash jpeg and slash jpeg so these are the only things that will be able to make it and we're going to filter that on the back end as well so don't worry about that but we just want access to the file extension type because we don't have direct access to it we just have the mime type which has this image before so we just need to cut that area of interest out and we can just say okay we can create a constant variable here for the mime type and on that file that's been passed in we can just say okay we can have a mind type we can split something here and split it by the forward slash so that will get our mime type in two variables so let's just create a file type variable which will be the mime type and the first um index so the second part so the first will be the image the second will be this dot png for example or png so we need to create our file name and this comes from the file.original name and i don't know why it doesn't come with the file type but it just doesn't for whatever reason that i'm aware of so we just need to create this so the file name so let's say i uploaded x dot png the file original name will be x we're going to add in this dot and the file type that we just defined through the mime type so we can just chuck in file type here and finally we can have a callback function and once again there's no errors that we're expecting we're gonna have a custom filter for that and we can just say okay we can have this file name so that's that and i just need a comma here so now we just need a filter and that's what i was talking about a custom filter here and i'm just going to call this outside of this disk storage i'm going to have a constant here file filter which takes the traditional or not so traditional but the similar request file and callback function now we're going to do exactly what we did on the back on the front end we're going to check the allowed mine types so let's get that so allowed mime types and the allowed we'll do a turnaround here so if the allowed mime types includes the mime type from the file and that's the lowercase t here we can call back something with a null error that we can have true here and what the true signifies here is okay it's included that means it's passed through the filter it's all right and then we can store it in the images folder the destination for the particular file type which is the name of the file and the extension but if it doesn't meet the criteria and it's not one of these you try and upload a pdf or something like that then we can just have this callback function it's not going to have the errors directly but it's going to have the false so the filter won't go through and that essentially handles that error that could occur so the final thing we need to do is we need to have this storage here this variable storage and it uses malta and it's takes an object and it has a key of storage and we just set that to this disk storage variable that we created and we can have a file filter and that's going to be set to file filter now we're expecting single images here so you know we're filtering the image type we're getting the images png jpeg jpeg we've got the location we've got the file name but we're in particular getting one so we need to just specify that we're expecting a single image and finally i can just export that helper as the variable storage and save that so i can pretty much close that now and finally returning to our routes here over here we can now see that okay so we're importing this storage route and we are handling that route recall that in our controller you know again the image and the image path and you know we're creating a new profile in the database but to transfer from the string that we're reading in from the file so that's separate to the image and image path here but it's actually the file that's coming in on the request we have this special storage uh helper here and that helper as we just configured uses malta and it accesses our server's disk storage and filters a few things and stores it like that so we've got our back end all set up so we can just return to our front end and i might just do an npm start just to check that's all smooth i haven't forgot anything there okay it's not all smooth and just to fix that up that should be module dot exports here so i can save that so we've got the ability to get our profiles and subscribe to those as an observable when we get new profiles so let's go ahead and finish this up by adding the new profile so let's start off in the profile service in the type script here and let's just add this function here add profile and this is going to take a name which is of the string type and an image which is of the file type and this returns this doesn't really return anything in this case because we need to use this elsewhere in the other components so what we can do here is we can create a constant variable and we'll just call this one profile data and this is going to be a new form data and we're not using json here because we have this extra property of the image which relates to a file and we've seen in the back end how we're handling that with malta so let's actually just close some of that up so with the form data we what we can do is for that variable profile data we can actually just append something onto that and if we take a look at append it takes a name and it takes a value which can be either a string or a blob and that blob is relating to the type of the file or the image so it's a blob of data essentially it's a bunch of characters like a very long string of characters that correlates to a image and we can firstly get our name and that's going to take a the name that's added to it and let's just shift alt down this line so for our image we have an image here that's being passed in which is a file called the blob type and what we can do is we can actually add a third argument here and we can add name here because we have the image as a blob but we need to give it a name so we give it the name of whatever the name the user is just so it has a name so we can make the http request now and what we'll do is we'll do this dot http and then we'll use the post method and this post method is going to return a object where the profile key is going to return the profile so we can send the request by sending it to this url and alongside with that url we'll need to attach the data associated with that endpoint now what we can do is we can subscribe to this observable and with this profile data we can actually create a constant here so the profile of the type profile and we know the object structure of that it's going to have an id underscore id which is going to be related to the profile data and in particular the profile object and underscore id so this is all coming in response from the api but the name we have that passed in directly that's not asynchronous so we can just add that in from the input field because the id is generated by the server or in particular we have a image path here and an image path is going to be the profile data dot profile dot image path and now we've constructed this profile object we can because we've added a profile we want to append this to the profiles and what we can do is well we have two variables we have this profiles here which is associated with all the profiles that we're displaying but we also have this observable version of the profiles which is a subject meaning we can actively push that and since we've already set up this get profile stream and we subscribe to that observable elsewhere if we make that adjustment and call the next method on that subscription this will seamlessly update our our profiles that are in the ui so rather than having that behavior where you add a profile nothing happens unless you reload the page we have this constant stream listening for this change so what we'll do is after this subscribe method we can just say this dot profiles and we can just go ahead and we can push the profile so that's that and we can also so this needs to actually be up in here and we can just call this dot profiles the observable the subject and we can since it's an observer as well as an observable we can directly call the next method so we can actively uh create what we want to be pushed and we can just say okay this profile is next of this profiles so now we have access to that what we can do is we can go to our create profile and we can start to actually submit the profile to the database so we can just call that profile service that we just created the add profile method on this dot profile which means we'll need to inject that into the constructor so let's do that so we can say private profile service the type profile service and we can just go ahead and import that so if we return to our on submit we can see this dot profile service dot add profile and we know that because we have a reactive form here that we created for the name in the image we can access that form value and we need the form value which is the object and so we can access the name and we can also just copy this in here so we can access the value of the image and then of course we can reset everything so let's return to our user interface and let's refresh here now we've got nothing in the we've got no profiles being displayed let's add john the pink as the username and we can just choose this file we can just choose this angular thumbnail i'll click open and i'll click the create profile and we can see that it's immediately gone to our list of profiles and if we have a look in the server we can see everything's running which indicates that it successfully stored this image and it just did it has the image name now you may want to add a hash or a string of values so you can't duplicate this but this more or less conveys the point of how we can store data in the image form on the server and if we take a look at the we refresh this and we look at the database and we look at the profiles we can see that we've got this id that's been automatically generated by underscore id the name jumper ping and the image path and that image path is actually on the server so i have it here and we can see this image actually on the server so let's just do one more and then we'll do a recap so i'm going to add john doe here choose a file and i'm going to just choose any of these i'll choose this node one so we've got angular node here we get the preview we click create profile it clears everything here and we get our profiles here in the form of a card with the image and once again if we you can see in the images here that we have that so i just want to recap we did quite a lot in this video so let's just recap everything so we created this create profile ability and this displaying of all the profiles we started by on the front end we just made the component for this create profile and that create profile had the ability to select files so we can select the files and we allowed for pngs jpegs and jpegs the two different jpegs top styles and if those are the correct file types on the front end when the file is loaded we can save this image data here so that's the create profile where we started off with and then we also have the all profiles component we essentially just displayed all the profiles and most of the work in terms of the http request was actually made in the profile service so we had to get all the profiles and we made the http request to our endpoint our server here and we subscribe to those and in response we got the profiles and we're able to actively call a subscription so we have this constant stream of the profiles as an observable so when we come to adding the profile we entered the name and the image as a file we could send this data as form data and we could subscribe to that and push that to the profiles and actively call the next observer there for that subscription so that's more or less what happened on the front end and then to hook that up or on the back end what we did is we firstly created our server and i imported a few things i had the environment variables and then we connected to the database using mongoose so we just put in the username the password and the database and then we'll successfully connected and then we had this body parser here and one thing different regular apis that received just the text fields was that we had this express dot static for accessing the file systems for wherever the server is located so we can see where the server is located here and we had some routes for the profile endpoint and we had two we had the get and the post and the get route those had a get profiles controller so if we take a look at the controller here you know it's just a simple request we had this schema defined in the model so we got access to the mongoose methods to be able to find all them and just return 200 status code with the profiles here and likewise in the routes we also have the ability to use the post profile on the profiles controller this required a little bit more work we essentially got the name from the request and we got the image path we set that we might want to set that dynamically just to note and then we created we used our schema we created our uh document and then we saved that asynchronously using async and await we created a resource with a 201 and then we can just return everything that was in that document so one addition for the posting ability is it had this storage helper and if we look at the helpers in the storage here we can see okay we need this malta package because this will allow us to access the disk storage so we need to define how this storage is accessed and we give it the destination relative to the app file and that is related to the images folder now we have two in there and we also give it the file name now we may want to make this a hash but just for demonstration purposes i just left it like this and then we're also on the back end we're filtering so if we're doing on the front end but just to make sure we've got doing on the back end as well so we've got the png and the two jpeg formats and with those we're able to use malta and this storage variable and we assign it so the storage is related to the storage and the file filters related to the file filter for a single image and then we export this helper and then we use it as a middleware in our routes and that pretty much sums up everything that we need for the image upload so you can tell that the image uploads a little bit more involved than the regular requests for json data and json formatted data but the application is definitely going to need some sort of form of image upload if you have a login system or and you have a profile page or anything like that so it's really handy to know how to use it and i hope this video helped so if it did please subscribe to my youtube channel i've got plenty of content on mean and mern and everything in the javascript ecosystem coming up so thanks for watching and i'll see you in the next one cheers
Info
Channel: Jon Peppinck
Views: 11,608
Rating: undefined out of 5
Keywords: angular, angular.js, angular 5, image upload, file upload, httpclient, angular tutorial, angularjs tutorial, angular.js tutorial, angular course, angularjs course, angular.js course, MEAN stack, image upload angular, angular image upload, Multer, node image upload, mean image upload, mean stack, angular 8, angular 9, angular 10, mean file upload, angular file upload
Id: R5uK9hN-VB0
Channel Id: undefined
Length: 110min 26sec (6626 seconds)
Published: Wed Sep 02 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.