In this post you’ll learn how to add dynamic modals to our TaskApp from the previous post using Javascript. This time you’ll create the project from scratch with Spring Initializr.
Optional: You learn how to add you project to a GitHub repository for version control. For that create an account.
Find a working version of the finished App on Heroku(it may take a while to load) and the Source Code here. Feel free to use the code on Github for practice. The Frontend was designed by Bootstrap and can be found here. Provided under MIT License.
Development Environment:
IDE: IntelliJ, Eclipse
Install: MySQL Workbench (How to install the mySQL Workbench)
Deployment: Get a free Heroku Account
Java: Install Java on your computer and follow the installation instructions
Version Control: Install Git on your computer. Get a GitHub Account. Generate an ssh-key on your computer.
Generate project with Spring Initializr:
Get the dependencies: JPA, Web, mySQL, PostgreSQL, Thymeleaf. Download, unpack and import the project into an IDE of your choice. I’ll use IntelliJ.
The Folder Structure will look like this:
For the Frontend download the Admin Dashboard from startbootstrap. Unpack the content and add it to your folder structure like this:
Next, copy this into your .gitignore
This is more extensive than necessary for this project but you may use it for future projects, too.
/target/ !.mvn/wrapper/maven-wrapper.jar ###################### # STS ###################### .apt_generated .classpath .factorypath .project .settings .springBeans .sts4-cache ###################### # Node ###################### /node/ node_tmp/ node_modules/ npm-debug.log.* ###################### # SASS ###################### .sass-cache/ ###################### # Eclipse ###################### *.pydevproject .project .metadata tmp/ tmp/**/* *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath .factorypath /src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ###################### # Intellij ###################### .idea/ *.iml *.iws *.ipr *.ids *.orig ###################### # Visual Studio Code ###################### .vscode/ ### NetBeans ### /nbproject/private/ /nbbuild/ /dist/ /nbdist/ /.nb-gradle/ /build/ ###################### # Maven ###################### /log/ /target/ ###################### # Gradle ###################### .gradle/ /build/ ###################### # Package Files ###################### *.jar *.war *.ear *.db ###################### # Windows ###################### # Windows image file caches Thumbs.db # Folder config file Desktop.ini ###################### # Mac OSX ###################### .DS_Store .svn # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ###################### # Directories ###################### /bin/ /deploy/ ###################### # Logs ###################### *.log ###################### # Others ###################### *.class *.*~ *~ .merge_file* ###################### # Gradle Wrapper ###################### !gradle/wrapper/gradle-wrapper.jar ###################### # Maven Wrapper ###################### !.mvn/wrapper/maven-wrapper.jar
To connect your project to GitHub:
Create an empty new repository on GitHub. Navigate to your repository.
Under “clone or download” copy the ssh-link. Make sure that your have your ssh -key on GitHub. If not follow this video.
In IntelliJ, under VCS: click on “Enable Version Control Integration…“, choose Git and enter your username and password for GitHub.

application.properties and data.sql:
Connects our app to a Database “taskapp_with_modal_db”. The default port is 8080. With the current cofigurations the app will insert the data from data.sql (code below, create file in same directory as application.properties) and create a new DB after every restart of the app (it will drop the old DB if it exists).
spring.datasource.url=jdbc:mysql://localhost/taskapp_with_modal_db?useSSL=false #place your password and username from your MySQL Workbench spring.datasource.username=root spring.datasource.password=root spring.jpa.show-sql=true #init data after every restart spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.hibernate.ddl-auto=create spring.datasource.initialization-mode=always ## Hibernate Logging logging.level.org.hibernate.SQL= DEBUG
INSERT INTO tasks (task_id, content, status, title) VALUES ( 1000, '(1)Generate a new project with Spring Initializr using Thymeleaf, MySQl, Web, and JPA', 'CLOSED', 'Setup a new project'), ( 1001, '(2)Create Folders for your model, repositories, controllers and services', 'REOPENED', 'Folder Structure'), ( 1002, '(3)Write a Task POJO with the following properties: title, status of type Status(Enum), content', 'OPEN', 'Task Entity'), ( 1003, '(4)Write an Enum Class for Status containing OPEN, CLOSED, REOPENED', 'OPEN', 'Status Enum'), ( 1004, '(5)Create a Service Interface and its Implementation', 'OPEN', 'Service Layer'), ( 1005, '(6)Complete the Application.properties file and create a new schema in your sql workbench', 'CLOSED', 'Databasa and Application Properties'), ( 1006, '(7)Write a Crud Repository for your Task entity', 'OPEN', 'Repository'), ( 1007, '(8)Write a TaskController', 'REOPENED', 'Controller');
Create a new Schema “taskapp_with_modal_db” in your mySQL Workbench. Change password and username in the application.properties file
Once you have the data.sql file, you will be asked to “Configure data source“.
Create a new data source as described in the picture below. Enter your password and username from your mySQL Workbench and the name of the Databse (taskapp_with_modal_db). Be sure to test the connection before closing the window.
Creating the Backend:
Create a new folder “model” and the files “Task.java” and “Status.java”. Each Task will have one Status.
Task.java:
@Entity @Table(name = "tasks") public class Task { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "task_id") private Long id; @NotEmpty private String title; @Lob @NotEmpty @Type(type = "org.hibernate.type.TextType") //heroku config private String content; //status may not be null @NotNull @Enumerated(value = EnumType.STRING) private Status status; public Task() { this.status = Status.OPEN; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } public void closeTask(){ this.status = Status.CLOSED; } public void reopenTask(){ this.status = Status.REOPENED; } }
Status.java
public enum Status { OPEN("open"), CLOSED("closed"), REOPENED("reopened"); private String typeOfStatus; Status(String typeOfStatus){ this.typeOfStatus = typeOfStatus; } public String getTypeOfStatus() { return typeOfStatus; } public static Stream<Status> stream() { return Stream.of(Status.values()); } }
Create a new folder “repository” and the file TaskRepository.java:
@Repository public interface TaskRepository extends CrudRepository<Task, Long>{ }
For the Service Layer create a new folder “service” and its files “TaskService.java” and “TaskServiceImpl.java”.
TaskService.java:
@Service public interface TaskService { /** * GET all tasks from DB * @return all tasks from Database */ Set<Task> getTasks(); /** * finds a task by its ID * @param taskId Database ID of task * @return task */ Task findById(Long taskId); /** * creates new Task and saves it in Database * @param taskDetails field values * @return new Task */ Task createTask(Task taskDetails); /** * updates Task from Database with field values in taskDetails * @param taskId Database ID of task * @param taskDetails field values * @return updated Task */ Task updateTask(Long taskId, Task taskDetails); /** * deletes Task from Database * @param taskId Database ID of task */ void deleteTask(Long taskId); /** * Sets Task.Status to CLOSED * @param taskId Database ID of task */ void closeTask(Long taskId); /** * Sets Task.Status to REOPENED * @param taskId Database ID of task */ void reopenTask(Long taskId); /** * Filters all Tasks by Status * @param status Enum: OPEN;CLOSED;REOPENED * @return Set of Tasks */ Set<Task> getTasksByStatus(Status status); }
TaskServiceImpl.java
@Service public class TaskServiceImpl implements TaskService{ @Autowired private TaskRepository taskRepository; @Override public Set<Task> getTasks() { Set<Task> taskSet = new HashSet<>(); taskRepository.findAll().iterator().forEachRemaining(taskSet::add); return taskSet; } @Override public Task findById(Long taskId) { Optional<Task> taskOptional = taskRepository.findById(taskId); if(!taskOptional.isPresent()){ throw new RuntimeException("Task Not Found!"); } return taskOptional.get(); } @Override public Task createTask(Task taskDetails) { Task newTask = taskRepository.save(taskDetails); return newTask; } @Override public Task updateTask(Long taskId, Task taskDetails) { Task task = findById(taskId); task.setTitle(taskDetails.getTitle()); task.setContent(taskDetails.getContent()); task.setStatus(taskDetails.getStatus()); taskRepository.save(task); return task; } @Override public void deleteTask(Long taskId) { taskRepository.delete(findById(taskId)); } @Override public void closeTask(Long taskId) { Task task = findById(taskId); task.closeTask(); taskRepository.save(task); } @Override public void reopenTask(Long taskId) { Task task = findById(taskId); task.reopenTask(); taskRepository.save(task); } @Override public Set<Task> getTasksByStatus(Status status) { Set<Task> allTasks = getTasks(); Set<Task> filteredTasks = new HashSet<>(); for(Task t : allTasks){ if(t.getStatus().equals(status)){filteredTasks.add(t);} } return filteredTasks; } }
Next, create a folder “controller” and the file TaskController.java:
@Controller public class TaskController { @Autowired private TaskService taskService; /** * GET all tasks from Database * @return template view for all tasks */ @RequestMapping(value = {"/tasks", "/"}, method=RequestMethod.GET) public String dashboard(Model model) { //display all Tasks Set<Task> tasks = taskService.getTasks(); model.addAttribute("tasks", tasks); Set<Status> statusList = new HashSet<>(); Status.stream().forEach(statusList::add); model.addAttribute("statusList", statusList); return "index"; } /** * Shows Tasks by Status * @param model contains TaskObject * @param taskStatus may have the values "open/closed/reopened" * @return Set of Tasks with specific status */ @RequestMapping(value = "/{status}", method=RequestMethod.GET) public String displayByStatus(Model model, @PathVariable("status") String taskStatus) { if(taskStatus.equals(Status.OPEN.getTypeOfStatus())){ model.addAttribute("tasks", taskService.getTasksByStatus(Status.OPEN)); } else if(taskStatus.equals(Status.CLOSED.getTypeOfStatus())){ model.addAttribute("tasks", taskService.getTasksByStatus(Status.CLOSED)); } else if(taskStatus.equals(Status.REOPENED.getTypeOfStatus())){ model.addAttribute("tasks", taskService.getTasksByStatus(Status.REOPENED)); } Set<Status> statusList = new HashSet<>(); Status.stream().forEach(statusList::add); model.addAttribute("statusList", statusList); return "index"; } /** * handles Status Changes * @param taskId Task Id * @param action may contain "close/open/reopen" * @param request helps redirect to previous site * @return redirection */ @RequestMapping(value = "/task/{id}/{action}", method=RequestMethod.GET) public String handleStatus(@PathVariable("id") Long taskId, @PathVariable("action") String action, HttpServletRequest request){ Task task = taskService.findById(taskId); if (action.equals("close")) { if(task.getStatus() == Status.OPEN) {taskService.closeTask(taskId);} if(task.getStatus() == Status.REOPENED) {taskService.closeTask(taskId);} } if(action.equals("reopen") && task.getStatus() == Status.CLOSED) {taskService.reopenTask(taskId);} String referer = request.getHeader("Referer"); return "redirect:"+ referer; } /** * Save NEW Task in Database * @param taskDetails field values * @return redirect to Dashboard */ @RequestMapping(path = "/task/create", method = RequestMethod.POST) public String createTask(Task taskDetails) { Task newTask = taskService.createTask(taskDetails); return "redirect:/"; } /** * updates Task in DB wirh field Values from EDIT Modal * @param taskDetails Task Object with field Values from EDIT Modal * @return redirect to dashboard */ @RequestMapping(path = "/update", method = RequestMethod.POST) public String updateTaskWithModal(Task taskDetails) { taskService.updateTask(taskDetails.getId(), taskDetails); return "redirect:/"; } /** * @ResponseBody: object returned is automatically serialized * into JSON and passed back into the HttpResponse object * (Source: https://www.baeldung.com/spring-request-response-body) * * @param taskId taskId * @return Task from DB */ @RequestMapping(path = "/findTask/{id}", method = RequestMethod.GET) @ResponseBody public Task findTask(@PathVariable("id") long taskId){ return taskService.findById(taskId); } /** * Deletes Task from Database * @param taskId TaskId * @return redirect to Dashboard */ @RequestMapping(path = "/task/{id}/delete", method = RequestMethod.GET) public String deleteTask(@PathVariable("id") long taskId, HttpServletRequest request) { taskService.deleteTask(taskId); return "redirect:/"; } }
This concludes the Backend. For the Frontend copy the following into your index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <th:block th:include="fragments/header"></th:block> <body id="page-top"> <!-- Page Wrapper --> <div id="wrapper"> <th:block th:include="fragments/sidebarLeft"></th:block> <!-- Content Wrapper --> <div id="content-wrapper" class="d-flex flex-column"> <!-- Main Content --> <div id="content"> <th:block th:include="fragments/navbar"></th:block> <!-- Begin Page Content --> <div class="container-fluid"> <div class="row"> <div class="col"> <div style="margin-bottom: 20px;"> <button class="btn btn-primary newBtn"> <i class="fas fa-plus-circle"></i> New Task </button> </div> </div> </div> <!-- Content Row --> <div class="row"> <!-- List of Tasks--> <th:block th:include="tasks/tasks"></th:block> </div> <div class="row"> <th:block th:include="tasks/editModal"></th:block> </div> <div class="row"> <th:block th:include="tasks/newModal"></th:block> </div> </div> <!-- /.container-fluid --> </div> <!-- End of Main Content --> <th:block th:include="fragments/footer"></th:block> </div> <!-- End of Content Wrapper --> </div> <!-- End of Page Wrapper --> <!-- Scroll to Top Button--> <a class="scroll-to-top rounded" href="#page-top"> <i class="fas fa-angle-up"></i> </a> <th:block th:include="fragments/scripts"></th:block> </body> </html>
In the resources>templates>fragments folder create the following files:
header.html
<head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>TaskApp</title> <!-- Custom fonts for this template--> <link th:href="@{/vendor/fontawesome-free/css/all.min.css}" rel="stylesheet" type="text/css"> <link th:href="@{https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i}" rel="stylesheet"> <!-- Custom styles for this template--> <link th:href="@{/css/sb-admin-2.min.css}" rel="stylesheet"> </head>
navbar.html
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top shadow"> <!-- Sidebar Toggle (Topbar) --> <button id="sidebarToggleTop" class="btn btn-link d-md-none rounded-circle mr-3"> <i class="fa fa-bars"></i> </button> <!-- Topbar Navbar --> <ul class="navbar-nav ml-auto"></ul> </nav>
sidebarLeft.html
<ul class="navbar-nav bg-gradient-primary sidebar sidebar-dark accordion" id="accordionSidebar"> <!-- Sidebar - Brand --> <a class="sidebar-brand d-flex align-items-center justify-content-center" th:href="@{/}"> <div class="sidebar-brand-text mx-3">TaskApp</div> </a> <!-- Divider --> <hr class="sidebar-divider my-0"> <!-- Nav Item - Dashboard --> <li class="nav-item"> <a class="nav-link" th:href="@{/}"> <i class="fas fa-fw fa-tachometer-alt"></i> <span>Dashboard</span></a> </li> <!-- Divider --> <hr class="sidebar-divider"> <!-- Heading --> <div class="sidebar-heading"> Tasks by Status </div> <!-- Nav Item - Charts --> <li class="nav-item"> <a class="nav-link" th:href="@{/{status}(status='open')}"> <i class="fas fa-fw fa-chart-area"></i> <span>OPEN</span></a> </li> <!-- Nav Item - Charts --> <li class="nav-item"> <a class="nav-link" th:href="@{/{status}(status='reopened')}"> <i class="fas fa-fw fa-chart-area"></i> <span>REOPENED</span></a> </li> <!-- Nav Item - Tables --> <li class="nav-item"> <a class="nav-link" th:href="@{/{status}(status='closed')}"> <i class="fas fa-fw fa-table"></i> <span>CLOSED</span></a> </li> <!-- Divider --> <hr class="sidebar-divider d-none d-md-block"> <!-- Sidebar Toggler (Sidebar) --> <div class="text-center d-none d-md-inline"> <button class="rounded-circle border-0" id="sidebarToggle"></button> </div> </ul>
scripts.html
This snippet controls what kind of data will be loaded into the modals. The Edit Modal will display the data of a single Task. The New Task Modal will have the initial values of status: OPEN.
<!-- Bootstrap core JavaScript--> <script th:src="@{/vendor/jquery/jquery.min.js}"></script> <script th:src="@{/vendor/bootstrap/js/bootstrap.bundle.min.js}"></script> <!-- Core plugin JavaScript--> <script th:src="@{/vendor/jquery-easing/jquery.easing.min.js}"></script> <!-- Custom scripts for all pages--> <script th:src="@{/js/sb-admin-2.min.js}"></script> <!-- EDIT modal--> <script> $(document).ready(function(){ $('.dropdown .eBtn').on('click', function(event){ //prevents redirection to blank page with JSON-Task-Object event.preventDefault(); var href = $(this).attr('href'); $.get(href, function(task){ $('.myForm #id').val(task.id); $('.myForm #title').val(task.title); $('.myForm #content').val(task.content); $('.myForm #status').val(task.status); }); $('.myForm #editModal').modal(); }); }); </script> <!--NEW Task Modal--> <script> $(document).ready(function(){ $('.newBtn').on('click', function(event){ $('.newTaskForm #id').val(''); $('.newTaskForm #title').val(''); $('.newTaskForm #content').val(''); $('.newTaskForm #status').val('OPEN'); $('.newTaskForm #newTaskModal').modal(); }); }); </script>
In templates>tasks create the following files:
editModal.html
<div class="col-xl-8 col-lg-7"> <div class="myForm"> <form th:action="@{/update}" th:method="post"> <div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="editModalLabel" aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">Edit</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <div class="form-group"> <input type="hidden" value="" class="form-control" id="id" name="id"/> </div> <div class="form-group"> <label for="title">Title:</label> <input value="" type="text" class="form-control" id="title" name="title"/> </div> <div class="form-group"> <label for="status">Status:</label> <select id="status" class="form-control" name="status"> <option th:each="status : ${statusList}" th:text="${status}" th:value="${status}"> </option> </select> </div> <div class="form-group"> <label for="content">Content:</label> <textarea value="" class="form-control" name="content" id="content" rows="5"> </textarea> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <input type="submit" class="btn btn-primary" value="save"/> </div> </div> </div> </div> </form> </div> </div>
newModal.html
<div class="col-xl-8 col-lg-7"> <div class="newTaskForm"> <form th:action="@{/task/create}" th:method="post"> <div class="modal fade" id="newTaskModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="exampleModalLabel">Edit</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <div class="form-group"> <input type="hidden" value="" class="form-control" id="id" name="id"/> </div> <div class="form-group"> <label for="title">Title:</label> <input value="" type="text" class="form-control" id="title" name="title"/> </div> <div class="form-group"> <label for="status">Status:</label> <select id="status" class="form-control" name="status"> <option th:each="status : ${statusList}" th:text="${status}" th:value="${status}"> </option> </select> </div> <div class="form-group"> <label for="content">Content:</label> <textarea value="" class="form-control" name="content" id="content" rows="5"> </textarea> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> <input type="submit" class="btn btn-primary" value="save"/> </div> </div> </div> </div> </form> </div> </div>
tasks.html
<div class="col-xl-11 col-lg-11"> <div th:if="${#lists.isEmpty(tasks)}" class="card shadow mb-4"> <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> <h6 class="m-0 font-weight-bold text-primary"> </h6> </div> <!-- Card Body --> <div class="card-body"> <p>Your Task List is empty.</p> </div> </div> <div th:each = "task : ${tasks}" class="card shadow mb-4"> <!-- Card Header - Dropdown --> <div class="card-header py-3 d-flex flex-row align-items-center justify-content-between"> <!-- Info Button for OPEN Tasks--> <div th:if="${task.status == T(com.example.taskapp.model.Status).OPEN}" class="btn btn-primary btn-icon-split"> <span class="icon text-white-50"> OPEN </span> <span class="text" th:text="${task.title}"></span> </div> <!-- Info Button for CLOSED Tasks--> <div th:if="${task.status == T(com.example.taskapp.model.Status).CLOSED}" class="btn btn-success btn-icon-split"> <span class="icon text-white-50"> CLOSED </span> <span class="text" th:text="${task.title}"></span> </div> <!-- Info Button for REOPENED Tasks--> <div th:if="${task.status == T(com.example.taskapp.model.Status).REOPENED}" class="btn btn-warning btn-icon-split"> <span class="icon text-white-50"> REOPENED </span> <span class="text" th:text="${task.title}"></span> </div> <h6 class="m-0 font-weight-bold text-primary"> </h6> <!-- Dropdown --> <div class="dropdown no-arrow"> <a class="dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <i class="fas fa-ellipsis-v fa-sm fa-fw text-gray-400"></i> </a> <div class="dropdown-menu dropdown-menu-right shadow animated--fade-in" aria-labelledby="dropdownMenuLink"> <div class="dropdown-header">Actions:</div> <div th:if="${task.status == T(com.example.taskapp.model.Status).OPEN}"> <a class="dropdown-item" th:href="@{/task/{id}/{action}(id=${task.id}, action='close')}">CLOSE</a> </div> <div th:if="${task.status == T(com.example.taskapp.model.Status).CLOSED}"> <a class="dropdown-item" th:href="@{/task/{id}/{action}(id=${task.id}, action='reopen')}">REOPEN</a> </div> <div th:if="${task.status == T(com.example.taskapp.model.Status).REOPENED}"> <a class="dropdown-item" th:href="@{/task/{id}/{action}(id=${task.id}, action='close')}">CLOSE</a> </div> <div class="dropdown-divider"></div> <!--<a class="dropdown-item" th:href="@{/task/{id}/edit(id=${task.id})}"><i class="far fa-edit"></i> Edit</a>--> <a class="dropdown-item" th:href="@{/task/{id}/delete(id=${task.id})}"><i class="fas fa-trash-alt"></i> Delete</a> <a class="dropdown-item eBtn" th:href="@{findTask/{id}(id=${task.id})}"><i class="fas fa-pencil-alt"></i> Edit</a> </div> </div> </div> <!-- Card Body --> <div class="card-body"> <p th:text="${task.content}"></p> </div> </div> </div>
This concludes the Frontend. Run the app with “mvn:spring-boot:run” in your terminal.
Deploy your App to Heroku:
application.properties.file: Delete all and place the following
## Heroku Properties spring.datasource.url=${JDBC_DATABSE_URL} spring.datasource.username=${JDBC_DATABSE_USERNAME} spring.datasource.password=${JDBC_DATABSE_PASSWORD} spring.jpa.show-sql = false spring.jpa.generate.ddl = true spring.jpa.hibernate.ddl-auto = create spring.datasource.initialization-mode=always
Next open the command line runner and navigate to your application folder
(1) enter: “heroku login” and provide your email and password (you need to have an account on Heroku by now)
(2) then: “git init” and “git add .” which will add all your files to the version control
(3) git commit -m “your commit message”
(4) heroku create “name of your app”
(5) git push heroku master
(6) heroku open – will open your deployed app in your browser