This tutorial will show you how to create ajax powered Bootstrap modals in Razor Pages. You’ll learn how to dynamically load partial content, how to send/save your form via ajax and how to validate it using Razor Pages as a backend technology.
Final proof-of-concept source code is in this GitHub repository.
As a side note, if you want to see how to the same thing in ASP.NET Core then go to my other tutorial: ASP.NET Core ajax modals with validation using Bootstrap.
What we're going to create
Let’s start off by creating new Visual Studio solution. I’m using Visual Studio Community 2019. Pick the “Create a new project” menu option, then select “ASP.NET Core Web Application” as shown below.
Follow along by clicking Next and then entering your solution and project name. Click Next once again, it will lead us to another panel in which we can select project type. Select “Web Application” to create Razor Pages project and hit Create.
We’re almost ready to go. Before we start we’re going to remove all extra stuff in default project. This step is not necessary, you can proceed with the tutorial if you wish.
Static resources are located in wwwroot directory, let me show you which one we need and which one we can remove.
First is the css directory, we’re not going to play with CSS, but since your’re probably going to need it at some point we’re going to keep the site.css file. By default it already contains some styles, you can safely clear the file.
Then we have the images directory. We’ll not need it at all, remove it altogether.
We’re left with lib directory, since we’re going to use CDN we can remove it.
Now, let’s clean up Pages directory. For this tutorial we’re only going to need following files:
Other page files can be safely removed.
Good, now we’re going to clean up _Layout.cshtml to include everything we’ll use:
And the last modification is Index.cshtml page, which you can clear. Now we’re ready to go.
Our modal will contain simple contact form with only three fields: first name, last name and email.
First thing we need to do is to create a model/class which with these fields. Create Models directory and then create a Contact.cs file:
Let’s prepare a modal view, which will contain an “add contact” form. All the markup here is a valid Bootstrap modal, I’m not going to describe it here. If you want to know more about markup then please refer to Bootstrap documentation.
We’ll create separate view for modal, which we’ll later fetch and display via ajax request. Right click on Pages directory in Solution Explorer, choose Add/Razor Page, then select Razor Page and click Create. Make sure to uncheck “Generate PageModel class“, we only want the view. I named by file _ContactModalPartial.cshtml
Notice the use of html tag helpers which renders valid inputs. Some of the we’ll need later on, so don’t think about them too much right now.
A few keypoints I want you to notice here are:
- @model directive which points to our model/Contact class
- full modal markup (powered by Bootstrap)
- asp-page-handler form attribute, which points to Razor Pages handler (needed later to save modal contents)
Our modal is ready to be displayed.
Display (modal) partial view using ajax in Razor Pages
Let’s start by creating a button which will show our modal.
Then, we go to our site.js and add some code which handles button clicks:
Good, once we click the button it shows an alert. Now comes the tricky part. We need to fetch modal HTML contents and render it as a modal.
Firstly, we want our modal (_ContactModalPartial.cshtml) to be rendered as HTML. Right now it’s a mix of HTML and Razor sitting somewhere in our project directory structure. Open your IndexModel class (the PageModel for Index page) and create new Razor Pages handler which will return modal. I have named it OnGetContactModalPartial, the OnGet is Razor Pages convention which matches GET requests and the ContactModalPartial is a name of the handler.
Since we want our handler to return only the modal and not the whole page (because whole page inlcudes all of _Layout.cshtml) we need to return PartialView. In Razor Pages we have to create PartialViewResult instance manually. We supply it with correct ViewName and initial ViewData which uses empty Contact model:
If you want to check how this works you can run the project and execute your handler by going to /Index?handler=ContactModalPartial URL (e.g. https://localhost:44365/Index?handler=ContactModalPartial).
The last thing we need to do is to make our button and handler work together. Let’s go back to site.js file. We’re going to make and ajax request which will fetch modal and then we’ll render.
Go ahead and try it.
You know how to show Bootstrap modal using Razor Pages and ajax request, but I want to show you two small, but important improvements to our solution.
Add data-url attribute to button, we use Url.Page helper to generate correct url:
Second improvement which I want to make relates to what we do with modal HTML. Right now we append it directly to document body. If you open a modal and close it then its HTML code will still reside within body. What’s more if you keep clicking the button it will add more and more HTML code to our document. This also leads to a certain bug in our code. Can you spot it? Let me ask you a question. Since we stored all these modals directly in body which modal gets opened? We have multiple modal instances, but we want only one.
I’d like to show you how to prevent that kind of “pollution” and to fix buggy behavior. What I want to do now is to create a placeholder (and empty element) inside document body tag, which will serve as a place where we will put modal contents. I put in Index.cshtml, but you may want to consider putting it inside _Layout.cshtml if you want it work on all pages of your application.
Saving modal form using ajax in Razor Pages
We know how to display modal, but it won’t do anything useful. Let me show you how to save modals contents. For this we’ll also use ajax, which means that when user clicks save button we’ll send background request to save data. After save we’ll close the modal.
We’re not going to set up a database or anything complicated in regards to saving modal form. What we’re going to do is to create a static list containing all contacts:
We’ll need another Razor Pages handler. We’ll use it to retrieve posted form data and save it. This time we use POST request instead of GET, so our handler starts with OnPost:
Now we need to take care of client side interaction. At the moment Save button is not working at all.
Firstly, we add click handler, so we can perform some actions after users click Save button. Notice selector we use to find the button, it uses the data-save attribute with value equal to modal:
Secondy, we find the form inside modal and get/prepare all the data before we send it. After that, we use serialize function to encode input fields in such a way that we can send it:
Thirdly and lastly, we send the data and close the modal. Before we can send it however we need correct url to the Razor Pages handler created earlier. Fortunately, it’s already available to use inside form action attribute, we’ve done it beforehand:
Grab URL from form action attribute and post form:
If you want to verify that it works you’ll have to put a breakpoint inside OnPostContactModalPartial.
How to validate modal fields and display errors
We know to how to display modal and how to save it. Our final task is to validate it and display errors if needed.
I’ll show you how to validate fields using data annotations. For those who don’t know what are data annotations, they are special classes (attributes) used to decorate properties. Each one has it’s own logic used to verify property value against some criteria, for instance we can test if it’s empty or not. You can create your own data annotation attributes, but in this tutorial we’re going to use two built-in data annotations.
We want all of our properties to be required, so we decorate it with Required attribute. For the Email property we also want to make sure that it contains valid e-mail address so we also add EmailAddress attribute:
Validation is on, but regardless of what we put in our form it still gets saved. As a result, we need to modify OnPostContactModalPartial handler to save contact only when validation returned no errors. To do this we check the value of ModelState.IsValid:
Now we save only correct data, however users won’t know if there were any errors, because we don’t display them.
In order to display errors we’ll make OnPostContactModalPartial return PartialView. It’s the same view we use in OnGetContactModalPartial. After post ModelState contains validation errors, which we can access in view file by using HTML tag helpers. In fact we already use them, let’s review _ContactModalPartial.cshtml again to see which parts are used to display error messages:
We did this beforehand, so all we need to do now is to return new HTML, which has these errors rendered and then put this HTML inside modal window so users can see it.
There is one important difference here. We do not create new contact instance in ViewDataDictionary, but instead we put the data we received.
Close modal when form was valid
We made it validate data, but when everything is correct modal won’t close. We need to find a way to mark a modal as valid (correctly saved) or invalid (containing validation errors).
For this we can also use ModelState.IsValid. We can put it inside form, as a hidden input field. In fact it’s already there:
Congratulations for making it this far. We’re done.