loading...
Cover image for Complete CRUD with Spring Boot, Vue.js, Axios

Complete CRUD with Spring Boot, Vue.js, Axios

brunodrugowick profile image Bruno Drugowick ・4 min read

AQAP Series (5 Part Series)

1) Complete API in 5 minutes with Spring Data REST - AQAP Series 2) Spring Boot, Vue.js, Axios and Thymeleaf with Bootstrap in 4 commits 3) Complete CRUD with Spring Boot, Vue.js, Axios 4) The most basic security for Spring Boot with Thymeleaf 5) Distributed Tracing with Spring Cloud Sleuth and Zipkin - AQAP Series

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.

GitHub logo brunodrugowick / spring-thymeleaf-vue-crud-example

Complete CRUD example project with Spring Boot, Thymeleaf, Vue.js and Axios.

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

AQAP Series (5 Part Series)

1) Complete API in 5 minutes with Spring Data REST - AQAP Series 2) Spring Boot, Vue.js, Axios and Thymeleaf with Bootstrap in 4 commits 3) Complete CRUD with Spring Boot, Vue.js, Axios 4) The most basic security for Spring Boot with Thymeleaf 5) Distributed Tracing with Spring Cloud Sleuth and Zipkin - AQAP Series

Posted on by:

brunodrugowick profile

Bruno Drugowick

@brunodrugowick

I love helping people to understand and deal with technology. If I can build something in the process, even better!

Discussion

markdown guide
 

This is really helpful for me , thx!

 

Cool! Glad to be helpful!

Make sure to take a look at the next of the series when I add security, it's very important! Also, there's a trick to connect Spring's csrf protection and Vue.js scripts like that.

Let me know if you need help with that, I have a tutorial but in Portuguese. I can point to the code, though.

 

Here it goes: github.com/brunodrugowick/securing...

The trick is to add the Thymeleaf's csrf input but integrate the token to the Axios library using Vue.js $refs.