 Hello developers, my name is Matt Ravel and today I'd like to show you how to integrate bootstrap into an Angular 9 application. I'm going to begin by showing you this GitHub repo that has all of the completed code. That's an octa developer, octa angular bootstrap example, and in this repository is a demo dot adoc. This is the instructions that I'm going to use to guide me and you through this screencast. If we look at the raw version, I have an ASCII doctor plugin installed that shows a much prettier view. What I'll show you today is how to first integrate bootstrap into an Angular 9 app, and then convert the app to sass, because CSS is a little more fun when you're using sass in those variables. It's more like a programming language. We'll make the app look good, we'll add form validation, and then we'll write some code to develop a searchable, sortable, and pageable data table. The last part sounds hard, but actually Spring Data JPA makes that very easy. We can actually do it in under 10 lines of code, so that's thanks to Kotlin and Spring Data JPA on the back end. This builds from a previous tutorial. If you were to read build a crud app with Angular 9 and Spring Boot 2.2, you'll see how we developed that, and there's a screencast there as well. There will also be a number of live templates I use in this, and IntelliJ live templates are basically a way to just type a few characters and spit out a whole bunch of code. Those are available in my GitHub repo at mrable.idlive templates, and you'll see we have some prerequisites, Node, 12, Java 11, and an Octa developer account. I'm going to put all those instructions on the left there, and then on the right, I'll make sure we have Node 12 installed, and Java 11, and an Octa developer account. I already have one, but if you do not, you can go to developer.octa.com, click on create free account, and go ahead and sign up for one. Enter your email first name, last name, and that's about it. Mine is at dev13320. You can see, and I can sign into there while we go back to these steps. So to begin, I'm going to clone the previous tutorial that I mentioned, this Angular 9 plus Spring Boot 2.2 crud example. So using git clone, and we'll put that in an octa-angular bootstrap example directory. It already exists, because I like to practice. Try that again. And now cd into there, and go into the notes directory, that's the Angular app. If you were to look back, you can see there's notes and notes API. The notes API is a Spring Boot application written in Kotlin. So if we go into notes and do MPMI, this will install all the dependencies in that project. Next I'll integrate bootstrap for the CSS and ng bootstrap, which is a component library with Angular that you don't need jQuery. You can just use Angular to add behavior to bootstrap. So MPMI bootstrap, and you don't have to specify the versions, but these are the only ones that I am certain work, because I tested it. So 6.0.0, you're welcome to try some other ones, but can't guarantee everything will work. And then we'll open both projects in IntelliJ, and then we'll start by importing the Gradle build file for the Spring Boot project. So import Gradle project, and then we'll go into the notes project in source app, app.module. We will add that ngb module. So under imports, ngb module, make sure you get the one from ng bootstrap, there we are. And now we should be able to run our client using cd into the notes directory and ngserve-o to open your default browser. So you can see it opens up my default browser, which is Firefox in this case, but it doesn't actually have anything in it. Let's look in the console and see what's in there. Oh, it shows an error. So it looks like your application needs Angular localized, so we'll grab that command. We'll put this on the far left, back to our terminal, and in notes we'll run ng add Angular localized. So ng bootstrap depends on that. And now you can see our app is running, and we have a login button. But let's worry about making this screen look a little better. So we'll put this down on the bottom here, and we'll keep that Firefox on the top. The first thing we're going to need to do is add the CSS for bootstrap into our styles.css. Now you can see we've had some changes there. Let's change app component to use some of the bootstrap classes. So I'll change this h1 to a nav, and then we'll add some classes, nav bar, and nav bar expand when it's large. And then we'll make it dark, and we'll make background dark as well. Okay, so now we're getting somewhere, but our title doesn't look good, so let's add some classes to that. We'll use nav bar brand and text light. Now we need to wrap that around the title. Now we're getting somewhere, and we'll put a little padding around our router outlet here. We'll use container fluid here, and we'll put some padding on the top, pt3. Then we'll go and grab the router outlet. Now we're getting somewhere, and now we have something that looks better. Now I want to show you how to secure this. So it's already set up using Okta for security, and it's not really Okta, it's OpenIDConnect. So any OpenIDConnect provider should work to secure this particular application. I'm using Spring Boot on the back end and Angular 9 on the front end, and so I'm going to go ahead and configure Spring Boot first. So we have to register a new app on Okta. So we'll go to my account at dev13320, log in there. Like I said, we're going to start with the Spring Boot app. So I'm going to add a new web application, and I'll just call it Spring Boot. And it's localhost8080, log in, OAuth2 code, Okta. And so that's built into Spring Boot. If you were to use a different provider, you would use something other than Okta at the end, but the rest of it is standard Spring Security configuration for OAuth and OpenIDConnect. So we'll go ahead and click Done. And then we can use the client ID and client secret at the bottom here to configure the security for our Spring Boot app. So we'll go into the notes API directory, and we'll create a Okta.env. No? Okay. We'll just do it from here. Okta.env file, and we'll paste those values in there. So for the domain, find as dev13320.okta.com. You can also find this value under API Authorization Servers right here, issue where you are. Now I need my client ID and client secret. Grab those, and secret. And now we have the Spring Boot side of things set up. So we can source that, Okta.env, and run Gradle Boot Run. And if you're to load up that app at localhost8080, I'm already logged into Okta so it didn't redirect me. If I go to API Notes, you can see there's a few notes that are already entered. And if we're to go to User Notes or even User, you'll see all my information. So that's all working on the server side. Now let's configure the client. So we'll need to create another app for the client. This will be a spa single page application. And you'll want to make sure and use 4200 here. And Loginary Direct is 4200 and plus a callback. You can make this whatever you want, but you have to make sure your Angular app works with it or is configured for it. And if you don't get this right the first time, if you don't put 4200 in there, you might get a cores error across origin resource sharing error. You can go in here to the trusted origins and add 4200 later. If you put it in there as a base URI, when you first create the app, it'll actually do it for you. And it doesn't really matter what you name it. So mine's named My Spa and you can see I'm using Pixi, proof key for code exchange, the most secure form of OAuth for browser applications. And so here we have under source app, auth routing, that's where everything's configured. So I can put a client ID in there. And it's already got my issuer in there for the last tutorial. So now it should have rebuilt the client. So let's try logging in and now we're logged in. So if we were to look at the notes and create a new note, you can see that's all working. However, it doesn't look that great. So let's fix that. But first let's actually commit our code. So we'll shut everything down here, go back to the root directory, get add, get commit, add bootstrap, and oadc configuration. And so the reason I did that is because now I want to convert this project to sass. And so if you were to do this locally and you messed up somehow or you had some issues, then you could revert back and try it again. So I'm going to use sass to customize bootstrap. The first thing I'm going to do is run this ngconfig command, which uses schematics to change the extension, but we're not in the notes directory. So let's switch to that. And then let's find all the files that end in CSS. And we don't want to look in node modules because that's a big ugly mess of source files. So it's still looked in there, so I forgot to add that. You can see we only have three files. So if you wanted, you can manually rename them, but you can also use a rename command and just substitute for CSS SCSS. And you can see it renamed those files for me. If I were to look at version control, you can see that it's removed some and added some. Now we can replace all references to those CSS files with using sed in our source app directory. And then also in the angular.json file, there is a reference to styles.css. So there's two of those. Change the first one and the second one to scss. And then also the styles refers to bootstrap. We can change this to scss and scss from bootstrap. And the reason you might want to do that is to override some of the default variables that bootstrap uses. So let's see an example of what that looks like. In the source directory, we can create a variables scss file. We could override some of bootstraps, for instance, it's primary and it's secondary colors, and there's light and darkened macros that you can use that makes it very easy to adjust things. In styles.scss, we can import before bootstrap variables. Now if we were to go back to our app and run it and do serve, start it back in while we're here, you can see it changes the primary and secondary colors. So that's pretty nice, but we don't really need that right now because I'm not very good at picking colors. So we'll comment that out, take it back to what it was, and now let's make things beautiful. I'm going to start by changing the app.component.html to use a better looking navbar. So it already has a navbar, but let's put one with some menus in it. So like I said, I'm using live templates for this. You can install my live templates and have the same bsnavbar command. And you'll notice now we have this navbar up here and we're using an angular icon that was already in the project. And we have this toggler that should allow us to toggle this menu. And you don't see the menu right now, but if you make it bigger, it shows home, octadev, and the GitHub repo for the project, as well as the logout button since we're already logged in. And then it'll say if we're logged out to please log in to manage your notes. And you'll notice that there's still that login button there. Well, we can remove it, or you could leave it. But in home component, we'll want to take out the login and the logout buttons. Now you won't see them. So the one problem with this is that this menu doesn't actually work. So to fix that, we can use some components from ngbootstrap. So in the button, which toggles it, we'll add a click event handler. And we'll say is collapsed equal to the opposite of whether it's collapsed. And then we can use IntelliJ to create that variable for us. And we'll set it to true. So it's collapsed by default. And then we'll go ahead and change this notes to be capitalized so it looks a little better. And it's still not working. Let's go back to our template and we need to use the ngb collapse directive here and say it's all dependent on whether that variable is collapsed or not. So in this case, we can now click it and you'll see we have an expandable menu for our mobile application. Now let's make the note list a little better looking. So log in here, view our notes, and you can see there's some things that need to be fixed here. So we'll start by going into our list and we have this breadcrumb here that's floating at the top. And so this was all generated from Angular Crud, which is a module on MPM. So if I went to Angular Crud and just googled for it, that's an MPM module. And so if you were to look at its GitHub homepage, you can see how the site looks. So when it generates the default templates, they actually look good on this site, but they need some work in ours. So that's what we're doing here is fixing that. So now we have our menu that looks better on the top. We're using this card here and we need some different classes. So let's do a card body. And then this can be a card title here and we'll take out that div and we'll make this into card text. Okay, so now we're starting to get somewhere and we can take out this div and this doesn't need to be a card. That goes to the text, so we'll move that to the bottom and reformat things and add some divs down here at the bottom. Now we're getting somewhere. It's starting to look better. You'll notice this form here doesn't quite look good on Mogul or it takes up a lot of space, so let's make that into an inline form. Bootstrap has excellent form support form inline. There we are, that makes sense. And now things are a little squished together so we can use some margin classes to clean that up a bit. So on this title here, we can say that we want some margin on the left and we want it to be small and we want some margin on the right and we want it to be a little bigger. So now things are looking better and this one I think should probably be two as well. Okay, and then we have this feedback. We'll give some padding around that too with the margin two or some margin around it. All right, so now let's go into our form and you can see that one needs some work too. So again, we'll take off that float right at the top. These are already bootstrap classes that are in there and then we'll make a similar change here. Two card body and card title. Take out that div and then card text and then down at the bottom add those divs. So that's starting to look better. And you can see we already have form group and form control. In here those are classes from Bootstrap that make these look nice and we'll put some padding down by the button or some margin again. So we'll go ahead and do it on the cancel button, margin left two. So those are spaced a little better and now let's make the title field required. So I'm going to take that one out and it's that same input field but now it's got this ng class that says hey, if it's been touched or it's invalid then show this message here that says title is required. And so if you actually touch it and then tab out it adds a red outline around it as well. So that's good for validation and down here this edit form is not clickable. If you do put something in and you save it then it's not going to work because our backend isn't up and running. So what happened over here? Oh, we forgot to source our octa.env. Now we can hit save and it'll work and it'll go back to our notes list and we can see it. So now let's make it so we can actually use this search right here because right now if we put, you know, d in there it's going to bring back. So we need to do some work on the server side. So in our user controller that controls the user's notes we can add a title parameter and we'll make it optional. So that's how you do that with Kotlin. With Spring MVC and Java you would have to actually add a request parameter and specify default value. So I really like how Kotlin makes this easy. And then we can say return if title is null or empty and go ahead and just return the default one and that's this find all by user else repository find all by user and title containing and then whatever value pass it. So let's ignore the case just like you often do and say principle.name and then we'll also want to pass in that title and then we'll create this on our repository. So this is that note repository that's in our demo application and spring data makes it so this interface and this method will actually create the query for you and pass in and substitute the appropriate variable. So this will find all by the user and any title that matches what we pass in. So now we can restart our server and let's add a searching for title in here for the next time. Probably enter some more data. So let's enter a two and some data and since we restarted it lost all the data so we'll add one again and now if we were to search for two we would just get two and if we search for one it won't even be case sensitive. So we're searching by titles all working and now I'd like to add sorting you can't click on these column headings so let's add sorting. So you can do this mostly in Angular and the note list will add a sortable directive so you can see here this is a directive its selector is any TH or table heading that has a sortable attribute on it sends an event of the direction that you want it to go in. So in our note list component we'll go ahead and add first of all the headers as a view children and an on sort and the on sort method is what does all the logic so we'll grab that and just move it down a bit right after ng on init and so this gets that event and it goes ahead and resets the other headers and then sets the column and direction so in note filter we'll need to add that column as a string and direction as a string and reformat that and now we can modify in note service to take those into account so here's find right here and we'll first of all pass the sort to the server side using filter.column and comma filter.direction and then we'll need to change this up bit, this return method so it's not just returning a list of notes there's a content that has a list of notes and the the new response that we'll get to shortly and so that's a map from rx.js operators there so now we can update the note list to have sortable not on the id because we don't care about that one but let me change this just to a number because I think it looks better and we'll also have onsort and we'll pass in the event to that and we'll copy and paste that down to the next column our text column and now let's add finally some css in our main css and so this will show an arrow using this data image and we do need to do some configuration on the server side so spring data has excellent support for searching and paging and sorting, you can do size and it'll bring back a certain size you can also have previous and next links and pages and sorting so the sort is sort equals name, comma, description or descending rather name, comma, descending or ascending so that's what we'll be sending so we do need to do some updating to our user controller add pageable as an argument not from awt print, that is a mistake that's easy to make so grab it from org spring framework data domain and then you'll pass it in to the repository and then in demo application you can add it here so again pageable and again it tried to use awt so import from spring and while we're in here we'll need to return a page now instead of a list we need to change this to a page as well and let's modify data initializer so there's a bunch of data in there and we don't have to keep adding it so we can change this list of to be a 4x and 0 of a thousand and we'll save note x and for our current user so that's whatever your email address is mine is matt.raybel at octa.com we can go ahead and restart our server our client is automatically restarting itself whenever we make changes if we were to go to localhost4200 and log in you can see we're logged in there's an error back here it didn't like so I figured it out I forgot to delete the sort variable when the direction is not passed in so by default you don't want a sort parameter so now you can see we have all of our notes and if we click on the title column we forgot to add to the note module so we have to add sortable here that sortable header directive now we can click on the title and you can see it's sorting as we expected to now let's add pagination with ngbootstraps ngbpagination component so we'll start in the filter so notes filter by adding a page the default will be 0 and a size because that's what spring data returns by default and just after the table in our note list component we'll add some pagination code so we're using ngbpagination the max size we're just going to say is 10 so that's how many pages will be displayed across the screen collection size will be based on the total and then filter page and size and when we change the page call this on page filter and then we'll also have how many items can be on each page so we'll need to add first of all it's not recognizing this ngbootstrap component and that's because we need to add it to the note module make sure you have a comment there and then you'll see it recognizes that and now we need to modify our list to have those variables so first of all the total and this is a convention to add dollar sign when you have an observable it's kind of like a hint to yourself number and then we'll modify the search method to set it so whenever it calls search set this total to this note service and we'll have a variable in there called size and then we'll add the on change and on sort and we'll whenever we sort let's reset the filter page to be back at one or the first page so the on change we'll just set the page size and the page change we'll go ahead and execute the filter and I had to do the minus one to get everything to work with spring data correctly and then to get the UI to display it correctly I had to do that so I think that works or it works good enough for a demo right and then we'll update the note service to add a size variable again it's an observable change our find method to have those sizes and the page and the comma there and we'll also when it comes back need to set this dot size dot next to response dot total elements and we can set this to any so there's no complaining down here and we're getting some more so we got a fitter error here where is that that's in our list component so fix that now everything is compiling now if we go here and go down to the bottom and you can see we can paginate to the third page and we can also make this bit wider and see that we can do just ten items per page or a hundred items per page so that's all working you have a fully functional data table that has sorting filtering and pagination you select huh so one of the things I wanted to show you is you can actually do all of this very quickly and easily with Jay Hipster and the Kotlin support for Jay Hipster so there is a Kotlin blueprint that overrides Jay Hipster's default Java back in and it uses angular and bootstrap and spring boot all by default so I already have Jay Hipster installed you can see 6.6.0 I also have Jay Hipster installed or Generator Jay Hipster Kotlin and so what I can do is create an application right from this file here so go back to my home directory kill all Java and we'll create an easy notes directory and then I'll create this notes.jdl in it and what this does is it configures a base name and it overrides any defaults that Jay Hipster has so authentication type by default is JWT I like OAuth better we're going to use Gradle we're going to use Elasticsearch so we get that search functionality and we'll use protractor to prove that everything's working we're going to include all entities our entity is defined here it's just a note with a title that's required and a text field that's a blob so that a bigger text area on the client and then our relationship is going to be many notes to one user and we'll add pagination as well and then we can use khipster import jdl notes.jdl so now we can start the docker containers that we'll need for keycloak in Elasticsearch so keycloak is the OAuth provider by default just because you can run it in a docker container so that's pretty darn convenient and I'm using omize.sh and I have jhipster plugin installed so there's some shortcuts that I can use so keycloak up we'll go ahead and start keycloak so if I was to show you what that looks like there it is and then I can also do jhes up for Elasticsearch in a docker container and then I can start the application using Gradle W and then once it's started I can go to gd80 and sign in to keycloak using admin admin and you'll see it populates it with a bunch of data by default and if I were to add a new note first note and type to my admin user then I can search for it but I also want to show you that you can prove your whole app is working using mpm run this uses protractor and runs tests for everything so we'll go ahead and run mpm i just to make sure that's installed and then mpm run e2e and so you can see all those tests pass so pretty slick you know and what 20 lines of code or so we were able to develop a whole spring boot app with Kotlin on the back end and angular on the front end and use bootstrap to style it all you can do it manually like I showed you or you can do it with jhipster so thank you for watching today you can find my team octadev on twitter you can find me at mrable on twitter and we also have a youtube channel we'd love you to subscribe and we hope you have a wonderful day