Django & JavaScript – Part 4 Vue.js & Axios
This is the fourth post in a multipart series that explores web front-end technologies and how they can be used with Django. You can find the previous post here: Django & JavaScript – Part 3 JavaScript & jQuery.
To be able to do React JS & Vue.js justice, I have decided to break them into separate posts with this post focusing on Vue.js and using Axios for API calls.
Requirements
- Single process to host the UI & API
- Django + Django REST framework
- Overarching web framework from day 1
- Intuitive UI/UX that can be dynamically updated without loading a new page
DRF API Class
This API class will be used for all examples using the API, in which DRF handles all PUT/PATCH/POST/GET/DELETE operations.
Vue.js
Vue.js is a JavaScript framework that builds on top of HTML, CSS, and JavaScript to provide a declarative model that helped me to develop the simple examples I am using to compare each framework. Vue.js is a lot more powerful than I will be demonstrating today and has been used to build some very robust single-page and multi-page webpages.
Creating a Vue Object
Since my examples are based on a Python back end and I will not be running node.js, I will be creating all of my Vue.js case as inline JavaScript using <script>
tags in my HTML. The code could also be easily served via separate static .js
files.
To instantiate my Vue objects for the examples, I will need a few pieces of information. I will need to know what my mount point is for Vue. A mount point is what informs Vue.js what parent element will be in scope of this Vue object. I will be using the ID of the element as my mount point. Next, I will be defining my set of data
attributes that I will use when interacting with Vue and the DOM. Because I will be intermingling Django templating and Vue.js templating, I will also need to overload the default delimiters from double curly braces to something that will not conflict. Lastly, I will be defining a set of methods or functions that will be invoked based on triggers in my HTML code.
This initial Vue object will not do much of anything without wiring it up to some HTML and throwing in some trigger events. I will be tying var1
to a click
event that will update the innerHTML of a <span>
element and var2
will be updated based on keyup
events in an <input>
element to replace its respective innerHTML. I am informing the Vue object of the trigger events by specifying v-on:<trigger>="<function to call>"
. For example, v-on:click="update_var1"
in the example below is notifying the Vue object that on clicking the button element I would like to run the update_var
function that is declared in methods.
The end result without CSS making it look fancy is the following.
Axios
Axios is a JavaScript library used to make HTTP promise-based requests from a browser (or server if using node.js). A JavaScript Promise is a construct where you have execution code and callbacks that allows asynchronous methods to return values similar to synchronous methods. The promise is to supply the value at some point in the future. There are three states pertaining to a Promise (pending
, fulfilled
, and rejected
), with fulfilled
being a successful completion of the execution code.
In Axios once the Promise is fulfilled it passes the response
to the .then(response)
method, which is where we implement some magic. In the event the request has an error, we have the ability to .catch(error)
and handle the error appropriately.
In my opinion Axios has done an elegant job creating a simple API client that integrated with my Vue.js code flawlessly.
Example 1 – Build DOM from Button Click
Initial Page Without Profile Data
Page with Profile Data
Initial HTML
Vue.js
In the first example I am creating a Vue object with a mount point of the <div>
that has an ID of user-profile
. Within my first nested element I have also introduced if/else
Vue statements as attributes of the child <div>
elements, v-if="<conditional>"
/v-else="<same conditional>"
. This will translate as: IF the name attribute is truthy (empty string evaluates as false in JavaScript) the table will be visible, ELSE the button to load the profile will be visible.
I have also intermixed Django templating by passing in the user ID of the user making the initial HTTP request to load the page and passing it into v-on:click
event function call. While the Vue object has the delimiters set to {( <var name> )}
to avoid conflicts.
Lastly, I use Axios to perform an HTTP GET to /api/users/users/<user id>/
and use the response data in performing Vue templating. As soon as I set the name
attribute, the Vue object will remove the initial <div>
with the button element and replace it with a new <div>
of the table that I am building. I don’t have to worry about selecting elements to then inject HTML, or changing attributes of the <div>
s to hide one and unhide the other. It’s all handled with the Vue object and the three v-
attributes inside the HTML elements.
Example 2 – Input Validation
User Does Not Exist
User Does Exist
HTML Form
Vue.js
new Vue({
el: "#create-user",
delimiters: ["{(", ")}"],
data: {
user_err: "",
button_enabled: false
},
methods: {
get_user: function (event){
if (event.target.value) {
axios
.get("/api/users/users/?username=".concat(event.target.value))
.then(response => {
if (response.data.count == 1) {
this.user_err = "This username already exists";
this.button_enabled = false;
} else {
this.user_err = "";
this.button_enabled = true;
}
})
.catch(error =>{
this.button_enabled = false;
this.user_err = error;
});
} else {
this.button_enabled = false;
this.user_err = "";
}
}
}
});
In this example I decided to implement error handling, which I did not do on the previous two blog posts. The ease of use and more object-oriented programming make me feel like demonstrating the rejected
status of the Promise. One difference is that I am not mixing templating languages. I still keep the delimiters overloaded, as this page would most likely be processed by some form of render in Django and I still want to avoid conflicts.
For input validation, if a user backspaces to completely empty out the <input>
field, I am resetting the user_err
attribute and removing the Create User button. This is meant to prevent unneeded error messages AND remove the user’s ability to click the button IF the user field is empty.
On the Axios call, I implemented similar implied logic as before—that if one user is returned, I have an exact match on the query params and I CANNOT create the user. The difference here is that if this conditional is hit, I not only display the error but I also remove the Create User button to prevent a user from submitting a known invalid user for creation. I have also implemented a catch
that will remove the button; and the error will be the error encountered by Axios during the call, resulting in a rejected
state of the Promise.
Conclusion
The further along in this series I get, the more I am realizing I never gave JavaScript frameworks the credit they deserve, it’s always been eww JavaScript
. So far, having a zero JavaScript solution like HTMX
, I am thrilled at the idea of all processing being done server-side. I left last week’s post on jQuery
feeling like “heck it might not be so bad.” BUT this week as I reflect on jQuery, it feels as though I spent more time than I would like worrying about DOM element selection/manipulation and less time in development. That’s where getting to Vue.js
has really stood out to me. Even in the simplistic examples provided, I never felt like I was building selectors to manipulate the DOM or access a value. As someone who is more Python
back-end focused, Vue.js
felt more native to me compared to my previous interactions in writing JavaScript.
~ Jeremy
Contact Us to Learn More
Share details about yourself & someone from our team will reach out to you ASAP!