Now let's ensure
that submitting this form works.
And for this,
we have to go to the new-product.ejs file
and add a form action and method.
Now the action should be /admin/products.
So that's the path to which the request will be sent.
And the method,
since we wanna create and store new data on the server,
should be Post,
since as you learned multiple times before
that is the convention.
You typically use post requests for submitting data,
and that has the advantage that then the data
is also included in the request body.
Now, there is something special about this form though.
We have this image input in there.
And you did learn before in the course section
about file uploads that in such cases,
you must change the enctype,
the encoding type of the form.
The default is application/x-www-form-urlencoded
and therefore you don't need to set that default.
You use that for all the regular forms
that do not have a file in them.
If you do have input type="file" in a form,
and that file should be submitted
with all the other data to the server,
then you need multipart/form-data as enctype.
Hence, we have to choose this here.
That will ensure that the file is sent to the server
and there in the second step we can extract it.
Now there is one other thing that is important
that we need to do in this form.
And that's our CSRF token.
All the forms where post requests
are sent need a CSRF token;
otherwise, sending that data will fail.
Now previously, we always solved this problem
by adding a hidden input.
This worked, but unfortunately,
this does not work if the enctype
is set to multipart/form-data.
Therefore, we can't add the CSRF token as we did before,
but thankfully the csurf package,
which we are using for handling the CSRF token,
has a plan B for us.
Besides adding such an input
and therefore adding the token into the body
that's submitted with the request,
we can also add it as a query parameter on the path,
on the URL the request is sent to.
Simply by adding a question mark here after action
or after the path we set up an action
and adding _csrf field here
where I then dynamically inject
with help of EJS,
locals.csrfToken.
So that token that is generated by the package,
the CSRF package,
is added as a query parameter
named _csrf to the outgoing request.
This will make sure that this form can be submitted.
Now, these are the final touches on the frontend.
Of course, now we have to ensure that on the backend
we are receiving that submitted data.
And most importantly,
that we're also receiving that file.
That is the part that is missing right now.
Now in the file upload section,
I introduced you to a package called multer,
which you can use for handling file uploads.
And indeed, that is still the package
I would recommend using.
You can simply visit the Multer NPM
or GitHub page to find detailed instructions on it.
But in the end, we wanna install it and then we will use it
just as we learned it in the file upload section.
Hence here, back on our server,
I'll quit that running server for now,
and run NPM install multer with or without --save.
Doesn't matter too much.
It will be added as a dependency
in package.json either way,
but this will now install it.
And then thereafter,
we can restart this all with NPM start.
And now we got multer added here as a dependency.
Now we can use it as you learn it.
That means we add it as a middleware
to the routes where we expect a file
and where we wanna extract the file.
Now you've also learned in the multer,
or in the file upload core section,
that you often need to configure multer.
Especially so that you are able to rename the file
and keep the file extension.
Therefore, I will actually add a custom middleware
that will take care about the multer configuration.
I'll name it image-upload.js.
In there I wanna start by requiring multer,
this package we just installed.
And you might recall that you then need to,
in the end, execute multer as a function.
This gives you back an upload object,
which you can then use to access different
pre-configured middlewares that could be added
to your routes.
Now multer takes a object with configuration options,
but I'll come back to those later.
Now, upload this object you get back after calling multer
has various methods for extracting
different kinds of files from requests.
And we need the single method because that allows us
to extract a single file by field name
from the incoming request.
And here I'll pass the field name
as a parameter value to signal.
And in my case, that's image,
because that is the name I assigned
in the new-product-ejs file to this input.
It has a name of image here
and therefore that is the name, image,
for which we have to look with help
of the multer middleware.
Now this gives me then the configured multer middleware
because upload.single will return
the actual middleware function.
And therefore it's this configuredMulterMiddleware,
which I now want to export
with module.exports = configuredMulterMiddleware.
But this is not all we need to do.
This would extract the image,
but it would store it in the wrong path
and it would store it under the wrong name.
Now you did learn in the file upload section
that on multer you can actually set a storage option
and this then takes a disk storage object
as expected by multer.
You can create such a multer disk storage object
by accessing multer
which I'm importing here .diskStorage.
This is a function you can execute
or a method you can execute,
which itself takes a configuration object.
And then this configuration object,
which you pass to disk storage,
you can configure the destination and the file name.
Now destination can be a function
where you run your own logic
for determining the destination.
Alternatively, you can just specify a path here.
In my case, I'll choose product-data/images as a path here.
Now we just have to make sure that we have such a folder
in our project here then.
So therefore,
I will quickly
add a product-data folder
and in there an images folder,
which initially of course is empty,
but that is the path at which I'm pointing here
where finally the images, the uploaded images,
should be stored in.
Now, the second option we can add for disk storage
is the file name option.
And here we can provide a function,
an anonymous function here in this case,
but you could of course also use a function
that you define somewhere else at which you now point,
which takes the request,
the file extracted by multer
and a callback function which you have to call
in your function once you're done
coming up with a file name.
And I will call that function right away
and queue that function.
You then first pass information
about a potential error you faced.
If you've got no error,
you pass null and here we have no error.
And then the second value you pass to call callback
is the file name you wanna use for the uploaded file.
And here I will actually install
another third-party package.
And that is the uuid package,
which I can use for generating unique identifiers
so that I don't have any clashing file names later,
but every file gets its own file name.
And this package will generate them for me.
For this I'll just import uuid by requiring uuid.
And then on this required package
we should access the v4 version.
So version four.
And then uuid here actually is a function you can execute,
which will give you a string that contains a unique ID.
Now I will concatenate that with a dash
and then with the original file name,
which will contain the file extension.
We can access this on the file identified by multer.
This file object holds information about that file.
And it does, for example, holds the original name,
which includes the file extension.
And this then generates a unique file name
with the file extension that gets stored in this folder.
Now, with all of that done,
we can apply this image upload middleware
to the route we want to use for handling
the submitted new product form.
So that's in admin routes there.
I will import the imageUploadMiddleware
by requiring ../middlewares/image-upload like this.
And then here we need to register a post route
for /products
because in the new-product.ejs file
we are sending a request to /admin/products.
As you learned, /admin can be omitted
in the admin.routes.js files.
So it's just /products, but a post request,
that's very important.
And then here,
I wanna point at adminController.createNewProduct,
but you also learned that these
router middleware registration methods get posts and so on,
take multiple parameter values, at least optionally,
and you can specify a list of middleware functions here,
which will then be executed from left to right.
And here, before we reach create new product
in my admin controller,
I actually want to execute imageUploadMiddleware.
And I don't execute it myself here like this.
Instead I just point at this middleware function,
this configured multer middleware function,
because express will then execute it for me
for the incoming requests.
But this middleware function, which we wrote ourselves,
is now basically the multer middleware function
with some preconfiguration from our site.
And that ensures that the uploaded files
should be extracted.
It should be extracted and stored,
but we can't test this yet because in the admin.controller
createNewProduct, doesn't do anything.
Of course, we should be able to create a new product here
and therefore again in this administration controller,
createNewProduct action,
we should accept the request and response,
and then we should get all the entered data
and store that in the database.
We'll do that database storing with help of a model,
which we'll create soon.
For the moment I just want to console log my request body
and after that,
also console log request file.
The file field should be available thanks to multer
and it should contain information about the stored file.
And I will then simply redirect to /admin/products.
That's all I do for now.
Of course, this will soon change so that we can do more.
With that however,
if you now reload this add new product page,
and we give this a try and I enter test here
and I choose a file.
And for this simply I prepared a basic image of the keyboard
I'm using for recording this very video.
And at the moment we have no preview.
We'll add one later.
Then I can add a summary.
This is a summary and the price like this.
And then, this is a detailed description
will become important later.
At the moment this doesn't matter too much
because it won't be saved to a database anyways.
I could show you that the reset button works,
but I don't want to enter it all again,
so I'll just click save.
I'm redirected, and here on the server side in the log,
we now see that logged data.
We see the request body,
which contains all the form data I entered.
And then here, we see the image data
that is provided by multer.
We can also see that in the product data images folder,
this image is saved.
It has this unique ID, dash,
and then the original file name with the extension.
And if I click on it,
we can see the image here in VS Code in the preview.
So this was saved here.
And then here in the console,
we got all the details multer provides to us.
Information about the type of image,
the path where it was stored,
the file name and the full path to the image
and the size of the image as well.
So that is all working.
We are getting that data on the server side,
which is amazing.
Now, of course, we don't want to log it.
Instead we want to save it in a database.