Complete CRUD with Spring Boot, Vue.js, Axios
Tags: java, vue, axios, spring
Published: 2020-02-09 02:35:04

Following up on the last post of AQAP Series, here's the complete create-read-update-delete (CRUD) app relying on Spring (Boot), Vue.js and Axios.

See it in action:

{% youtube 72TV4XjlsCU %} 

I didn't mention Thymeleaf because there's no changes to the pages served by the back-end on this post.

I'll illustrate the code using the Role entity, but as always the complete code and the app running is available at the end.

Without further ado...

# Adding REST operations

We start adding two new operations on the `RoleController.java`:

```java
@PostMapping("roles")
public Role save(@RequestBody Role role) {
    return roleRepository.save(role);
}

@DeleteMapping("roles/{id}")
public void get(@PathVariable Long id) {
    roleRepository.deleteById(id);
}
```

The `save` method takes care of both `create` and `update` operations. Spring is smart enough to update when there's an ID present and to create a new entity otherwise.

# The Role Form

This is our HTML form now:

```html
<form v-on:submit.prevent="postRole">
    <div class="card mb-auto">
        <div aria-controls="roleForm" aria-expanded="false" class="card-header" data-target="#roleForm"
             data-toggle="collapse" id="formHeader" style="cursor: pointer">
            <div class="float-left">New/Edit Role</div>
            <div class="float-right">+</div>
        </div>
        <div class="card card-body collapse" id="roleForm">
            <div class="form-group row">
                <label for="roleName" class="col-sm-4 col-form-label">Role Name</label>
                <input id="roleId" type="hidden" v-model="role_id">
                <input id="roleName" class="form-control col-sm-8" placeholder="Role Name" type="text"
                           v-model="role_name"/>
            </div>
            <div class="form-group row">
                <div class="col col-sm-4"></div>
                <input class="btn btn-primary col col-sm-8" type="submit" value="Save">
            </div>
        </div>
    </div>
</form>
```

Two things to notice here:

- `v-on:submit.prevent="postRole"` is a Vue.js tag to specify the method to run when submitting the form and to prevent the default behaviour of page reloading on submit.
- `v-model` is another Vue.js tag. This binds an input with Vue.js data.

# New Edit and Delete buttons

On the `Actions` column of our HTML table, just add two new buttons:

```html
<td>
    <button class="btn btn-primary" v-on:click="editRole(role)">Edit</button>
    <button class="btn btn-danger" v-on:click="deleteRole(role)">Delete</button>
</td>
```

Notice the same `v-on` tag, but now with an action of `click`. This binds the button click to a Vue.js method.

# The Vue.js Magic... again.

Our Vue.js script is now a little scary:

```javascript
<script>
    var app = new Vue({
        el: '#main',
        data() {
            return {
                roles: null,
                role_id: '',
                role_name: '',
            }
        },
        mounted(){
            this.getRoles();
        },
        methods: {
            getRoles: function () {
                axios
                    .get("/api/v1/roles")
                    .then(response => (this.roles = response.data))
            },
            postRole: function (event) {
                // Creating
                if (this.role_id == '' || this.role_id == null) {
                    axios
                        .post("/api/v1/roles", {
                            "name": this.role_name,
                        })
                        .then(savedRole => {
                            this.roles.push(savedRole.data);
                            this.role_name = '';
                            this.role_id = '';
                        })
                } else { // Updating
                    axios
                        .post("/api/v1/roles", {
                            "id": this.role_id,
                            "name": this.role_name,
                        })
                        .then(savedRole => {
                            this.getRoles();
                            this.role_name = '';
                            this.role_id = '';
                        })
                }
            },
            editRole: function (role) {
                this.role_id = role.id;
                this.role_name = role.name;
                document.getElementById('roleForm').setAttribute("class", document.getElementById('roleForm').getAttribute("class") + " show");
            },
            deleteRole: async function (role) {
                await axios
                    .delete("/api/v1/roles/" + role.id);
                this.getRoles();
            }
        },
    })
</script>

```

But it's quite simple, actually. Let's explore what matters:

- `el: '#main'` specifies that Vue.js is going to operate on this HTML element id. In our case this is `div` containing the form and table.
- Inside `data()` we can specify variables that we are going to manipulate on the script and that the user may interact with. In our case notice that we have defined variables that represent the content of the form that the user interacts with.
- `mounted()` is called when Vue.js is ready (mounted on the element specified in `el` above). Here we call a method `getRoles()`. This method requests data to the API and sets it to a variable that is used to create the table of contents (using `v-for` explained on the last post).
- `methods` contains all the methods that interact with the API. Notice how they equate to the CRUD operations:
  - `getRoles` is the `read` operation.
  - `postRole` is the `create` operation.
  - `editRole` is the `update` operation.
  - `deleteRole` is the `delete` operation.

# The app

You can see the app running [here](https://spring-thymeleaf-vue-crud.herokuapp.com/) (slightly modified since this is an ongoing analysis).

The repository and the aforementioned commits, also slightly modified, [here](https://github.com/brunodrugowick/spring-thymeleaf-vue-crud-example/commits/master).

{% github https://github.com/brunodrugowick/spring-thymeleaf-vue-crud-example no-readme %}


# AQAP Series

As Quickly As Possible (AQAP) is a series of quick posts on something I find interesting. I encourage (and take part on) the discussions on the comments to further explore the technology, library or code quickly explained here.

---

Image by <a href="https://pixabay.com/pt/users/jaykingsta14-4885997/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2358636">Jason King</a> por <a href="https://pixabay.com/pt/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=2358636">Pixabay</a>