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:

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:

@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:

<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:

<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:

<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 (slightly modified since this is an ongoing analysis).

The repository and the aforementioned commits, also slightly modified, here.

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 Jason King por Pixabay

This post is also available on DEV.