We’ll structure this training material module by module, building from the basics of Vue to integrating APIs and handling frontend aspects of authorization for an on-premise backend.
Understanding the Shift: From jQuery/Imperative to Vue/Declarative
In jQuery and plain JavaScript, you often use an imperative approach:
- Select an element (
$('#myDiv')
). - Perform an action (
.text('new content')
). - Repeat for every change.
Vue uses a declarative approach:
- You declare how your HTML should look based on your data.
- When the data changes, Vue automatically updates the HTML for you efficiently.
This data-driven approach is a key difference.
Module 1: Introduction to Vue.js Basics
Goal: Understand what Vue is, how to set it up, and bind data to the DOM.
- What is Vue.js?
- A progressive framework for building user interfaces. “Progressive” means you can adopt it gradually – use it for a small component, or build a full single-page application (SPA).
- Focuses on the View layer (the user interface).
- Reactivity System: Vue automatically knows which parts of the DOM need updating when your data changes. No more
$('#element').text(newData)
calls everywhere!
- Setting Up Vue:
- Option A: CDN (Simplest for starting)
- Just add a
<script>
tag to your HTML:<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
- This is great for small examples or integrating Vue into existing, non-SPA projects (like enhancing a specific part of a page built with jQuery).
- Just add a
- Option B: Vue CLI / Vite (For full applications)
- Recommended for building single-page applications (SPAs).
- Provides build tools (Webpack or Vite), development server, hot-reloading, etc.
- Requires Node.js and npm/yarn.
- Install:
npm install -g @vue/cli
(Vue 2/3) ornpm init vue@latest
(Vue 3 using Vite – recommended). - Create project:
vue create my-project
(CLI) ornpm init vue@latest
(Vite) and follow prompts. - This is overkill for just adding a small feature, but standard for a new Vue app.
- Option A: CDN (Simplest for starting)
- Your First Vue Instance/App:
- In Vue 2:
var app = new Vue({ el: '#app', // Mounts the Vue instance to the element with id="app" data: { message: 'Hello Vue!' // Your data } });
HTML<div id="app"> {{ message }} </div>
- In Vue 3 (using the Composition API style, common with Vite):
import { createApp, ref } from 'vue'; createApp({ setup() { const message = ref('Hello Vue 3!'); // Reactive data using ref return { message }; } }).mount('#app'); // Mounts the app
HTML<div id="app"> {{ message }} </div>
- The
data
(orref
in Vue 3) object is where you define the state of your application. {{ message }}
is a simple data binding: Vue replaces this with the value ofmessage
. Ifmessage
changes, the HTML updates automatically. This is a core concept replacing manual DOM manipulation.
- In Vue 2:
- Data Binding and Directives:
{{ ... }}
: Text interpolation. Displays data as text.v-bind
: Binds an attribute to an expression. Shorthand is:
. HTML<img v-bind:src="imageUrl"> <img :src="imageUrl">
This replaces manually setting$('#myImage').attr('src', imageUrl)
.v-model
: Creates a two-way binding on form input elements. Changes in the input update the data, and changes in the data update the input. HTML<input type="text" v-model="userName">
This simplifies getting/setting input values compared to$('#myInput').val()
.
Module 2: Core Vue Concepts – Directives and Logic
Goal: Control rendering and handle events based on data.
- Conditional Rendering (
v-if
,v-else-if
,v-else
,v-show
)v-if
: Renders the element only if the expression is true. The element is added/removed from the DOM. HTML<p v-if="isLoggedIn">Welcome back!</p>
v-show
: Always renders the element, but uses CSSdisplay: none
to hide/show it based on the expression. Good for frequent toggling. HTML<button v-show="isLoading">Loading...</button>
- Compare to jQuery: This replaces manually using
$('#element').show()
or.hide()
or.remove()
. Vue handles it based on the data state.
- List Rendering (
v-for
)- Renders a list of elements based on an array.
<ul> <li v-for="item in items" :key="item.id"> {{ item.text }} </li> </ul>
:key
is important for Vue to track each element’s identity for performance.- Compare to jQuery/JS: This replaces loops where you manually create HTML strings or elements and append them (
$.each
,for
loops,.append()
). Vue iterates over the data array and renders the template for each item automatically.
- Event Handling (
v-on
)- Listens for DOM events and runs JavaScript when they are triggered. Shorthand is
@
.
<button v-on:click="handleClick">Click Me</button> <button @click="handleClick">Click Me</button>
- Define
handleClick
in themethods
property of your Vue instance (or insetup()
in Vue 3). - Compare to jQuery/JS: This replaces
element.addEventListener('click', ...)
or$('#button').on('click', ...)
manually attached after elements exist.v-on
is part of the template and reactive.
- Listens for DOM events and runs JavaScript when they are triggered. Shorthand is
- Computed Properties and Watchers:
- Computed Properties: Cached values that only re-calculate when their dependencies change. Useful for complex calculations or transforming data to display. JavaScript
data: { firstName: 'John', lastName: 'Doe' }, computed: { fullName: function() { return this.firstName + ' ' + this.lastName; // Depends on firstName and lastName } }
HTML<p>{{ fullName }}</p>
- Watchers: Execute code when a specific piece of data changes. Useful for performing side effects (like making an API call when a search term changes). JavaScript
data: { searchTerm: '' }, watch: { searchTerm: function(newVal, oldVal) { // Perform an action when searchTerm changes console.log('Search term changed from', oldVal, 'to', newVal); // maybe trigger an API call here } }
- Compare: Computed properties are for deriving data to display. Watchers are for reacting to data changes with actions. In plain JS, you’d manually write logic inside event handlers to perform these updates or side effects. Vue’s system is more declarative and often more performant.
- Computed Properties: Cached values that only re-calculate when their dependencies change. Useful for complex calculations or transforming data to display. JavaScript
Module 3: Components – Building UI Blocks
Goal: Learn to break down your UI into reusable, self-contained components.
- Why Components?
- Makes your application modular and maintainable.
- Reusability (e.g., a Button component, a Product Card component).
- Encapsulation (each component manages its own data, template, and logic).
- Defining and Using Components (Single File Components –
.vue
files – recommended with CLI/Vite)- This is the standard way to build Vue applications. A
.vue
file contains<template>
,<script>
, and<style>
sections.
<template> <button @click="handleClick">{{ label }}</button> </template> <script> export default { props: { // Define props to receive data from parent label: String }, methods: { handleClick() { // Logic here console.log(this.label + ' clicked!'); this.$emit('button-clicked'); // Emit an event to the parent } } } </script> <style scoped> /* Scoped CSS only applies to this component */ button { background-color: blue; color: white; } </style>
- Using the component in a parent:
<template> <div> <my-button label="Save" @button-clicked="handleSave"></my-button> <my-button label="Cancel" @button-clicked="handleCancel"></my-button> </div> </template> <script> import MyButton from './MyButton.vue'; // Import the component export default { components: { // Register the component MyButton }, methods: { handleSave() { console.log('Save action triggered'); }, handleCancel() { console.log('Cancel action triggered'); } } } </script>
- This is the standard way to build Vue applications. A
- Props (Passing Data Down):
- Props are used to pass data from a parent component down to a child component (
label
in the example above). - They are read-only in the child. You shouldn’t modify a prop directly inside a component.
- Props are used to pass data from a parent component down to a child component (
- Events (
$emit
– Passing Data Up):- Events are used for a child component to communicate back up to its parent (
button-clicked
in the example). - The child emits an event (
this.$emit('event-name', data)
), and the parent listens for it usingv-on
or@
.
- Events are used for a child component to communicate back up to its parent (
Module 4: Working with APIs (Connecting to Backend/Database)
Goal: Make API calls from your Vue application to fetch and send data, replacing your AJAX calls.
- Client-Server-Database Model:
- Your Vue.js application (the Client/Frontend) runs in the user’s browser.
- It does not connect directly to your database (e.g., SQL Server, MySQL, etc.). This would be a major security risk.
- Instead, it communicates with your Backend Application/API (the Server). This backend is responsible for:
- Receiving requests from the frontend.
- Implementing business logic.
- Connecting securely to the Database.
- Performing database operations (CRUD – Create, Read, Update, Delete).
- Sending data back to the frontend (usually in JSON format).
- Your Vue app will replace your manual
$.ajax()
orWorkspace
calls with calls made within Vue components, often using a library like Axios.
- Introducing Axios (Recommended API Client)
- Axios is a popular, promise-based HTTP client that works well with Vue.
- Install:
npm install axios
- Using
Workspace
is also an option, but Axios has a slightly cleaner API and features like interceptors (useful for auth).
- Making API Calls in Vue:
- You typically make API calls within component methods or lifecycle hooks (like
mounted
orcreated
– when the component is ready). - Example: Fetching Data (GET)
<template> <div> <h2>Users</h2> <ul> <li v-for="user in users" :key="user.id">{{ user.name }}</li> </ul> <p v-if="loading">Loading users...</p> <p v-if="error">{{ error }}</p> </div> </template> <script> import axios from 'axios'; export default { data() { // Use a function for component data return { users: [], loading: false, error: null }; }, mounted() { // Lifecycle hook: called after the component is mounted to the DOM this.fetchUsers(); }, methods: { fetchUsers() { this.loading = true; // Show loading indicator this.error = null; // Reset error axios.get('/api/users') // Replace with your backend API endpoint .then(response => { // Handle success - update the 'users' data property this.users = response.data; // Assuming API returns an array of users in response.data }) .catch(error => { // Handle error - update the 'error' data property console.error('Error fetching users:', error); this.error = 'Failed to load users.'; }) .finally(() => { // This runs whether success or error this.loading = false; // Hide loading indicator }); } } } </script>
- Example: Sending Data (POST)
<template> <form @submit.prevent="createUser"> <input type="text" v-model="newUser.name" placeholder="Name"> <button type="submit">Create User</button> <p v-if="successMessage">{{ successMessage }}</p> <p v-if="error">{{ error }}</p> </form> </template> <script> import axios from 'axios'; export default { data() { return { newUser: { name: '' }, successMessage: '', error: null }; }, methods: { createUser() { this.successMessage = ''; this.error = null; axios.post('/api/users', this.newUser) // Send newUser data in the request body .then(response => { console.log('User created:', response.data); this.successMessage = 'User created successfully!'; this.newUser.name = ''; // Clear the input // Optionally, emit an event to a parent component to update a list of users }) .catch(error => { console.error('Error creating user:', error); this.error = 'Failed to create user.'; // Handle specific error responses (e.g., validation errors) if (error.response && error.response.data) { console.error('API Error Details:', error.response.data); this.error += ' ' + (error.response.data.message || ''); // Display API error message } }); } } } </script>
- Compare to AJAX: This is the Vue/Axios way of doing what you did with
$.ajax({...})
. You define the API call within a method or lifecycle hook, and then update the component’s data (this.users
,this.error
,this.successMessage
) based on the response. Vue’s reactivity automatically updates the UI.
- You typically make API calls within component methods or lifecycle hooks (like
Module 5: Frontend Aspects of On-Premise Authorization
Goal: Understand how the frontend handles authentication tokens and secures UI elements based on user status in relation to your backend authorization system.
Note: True security is primarily a backend responsibility (validating credentials, issuing secure tokens, verifying permissions on every API request). The frontend’s role is to manage the user session state and interact with the backend’s security endpoints. “On-premise” means your authentication server and API are within your own network, but the frontend logic is largely the same as cloud-hosted for standard token-based auth.
- Common Authorization Flow (Token-based, e.g., JWT):
- User enters credentials (username/password) in a login form on the frontend.
- Frontend sends credentials to a backend Login API endpoint (e.g.,
POST /api/login
). - Backend verifies credentials against the user database.
- If valid, the backend generates a token (e.g., JWT) and sends it back to the frontend in the API response.
- Frontend stores this token (e.g., in
localStorage
orsessionStorage
). - For subsequent API calls that require authentication (e.g., fetching user data, creating a record), the frontend includes the stored token in the request headers (commonly in the
Authorization: Bearer [token]
header). - Backend receives the request, extracts the token, validates it.
- If the token is valid and represents a user with required permissions, the backend processes the request. Otherwise, it returns an error (like 401 Unauthorized or 403 Forbidden).
- Implementing Login in Vue:
- Create a login component with input fields for username/password.
- Use
v-model
for two-way binding the input values to component data. - On form submission (
@submit.prevent
), make anaxios.post()
call to your backend’s login endpoint, sending the username and password. - Example (Login Component):
<template> <form @submit.prevent="login"> <h2>Login</h2> <input type="text" v-model="username" placeholder="Username"> <input type="password" v-model="password" placeholder="Password"> <button type="submit">Login</button> <p v-if="error" style="color: red;">{{ error }}</p> </form> </template> <script> import axios from 'axios'; export default { data() { return { username: '', password: '', error: null }; }, methods: { login() { this.error = null; axios.post('/api/login', { // Your on-prem login endpoint username: this.username, password: this.password }) .then(response => { // Assuming backend sends { token: '...' } on success const token = response.data.token; localStorage.setItem('user-token', token); // Store the token // Redirect user to a protected page or update app state console.log('Login successful, token stored.'); // Example: this.$router.push('/dashboard'); // Requires Vue Router }) .catch(error => { console.error('Login failed:', error); this.error = 'Login failed. Please check credentials.'; localStorage.removeItem('user-token'); // Clear any old token }); } } } </script>
- Storing Authentication State:
localStorage
orsessionStorage
is common for storing the token in the browser so it persists across page refreshes or browser sessions (sessionStorage).- For managing auth state within your Vue application reactively and across components, Vuex (Vue 2) or Pinia (Vue 3) are recommended state management libraries. You’d store the token and user info in a central store, making it easily accessible and reactive throughout the app. (This is a more advanced topic).
- Including Tokens in API Requests:
- Every time your frontend needs to access a protected backend resource, it must send the token.
- Manually adding headers to every
axios
call is repetitive. Axios Interceptors are perfect for this. You can configure Axios to automatically add theAuthorization
header to every outgoing request if a token exists. - Example (Setting up an Interceptor, typically done once when your app starts):
// In your main app file (e.g., main.js or app.js) import axios from 'axios'; axios.interceptors.request.use(config => { const token = localStorage.getItem('user-token'); // Get token from storage if (token) { // Add the token to the Authorization header config.headers['Authorization'] = `Bearer ${token}`; } // You can also check if the request is going to your backend's domain // to avoid sending token to external APIs if necessary. // const backendUrl = 'YOUR_BACKEND_BASE_URL'; // e.g., 'http://localhost:5000/api' // if (config.url.startsWith(backendUrl)) { // if (token) { // config.headers['Authorization'] = `Bearer ${token}`; // } // } return config; // Return the modified config }, error => { // Do something with request error return Promise.reject(error); }); // You can also add a response interceptor to handle 401/403 errors globally axios.interceptors.response.use(response => response, error => { if (error.response.status === 401 || error.response.status === 403) { console.log('Unauthorized or Forbidden. Redirecting to login...'); // Handle token expiration or unauthorized access - e.g., redirect to login page // Remove token from storage, clear user state etc. localStorage.removeItem('user-token'); // Example: router.push('/login'); // Requires Vue Router } return Promise.reject(error); }); // Now, any axios calls you make will automatically include the token if it's in local storage // Example in a component: // axios.get('/api/protected-data').then(...).catch(...) // Token is added automatically
- Conditionally Rendering UI:
- Use
v-if
orv-show
directives based on a computed property or data value that indicates whether the user is authenticated. - Example:
<template> <nav> <router-link to="/">Home</router-link> <router-link to="/public">Public Page</router-link> <span v-if="isAuthenticated"> <router-link to="/dashboard">Dashboard</router-link> <button @click="logout">Logout</button> </span> <span v-else> <router-link to="/login">Login</router-link> </span> </nav> </template> <script> // Assuming you have a way to check auth status, // maybe a computed property that checks localStorage or a Vuex/Pinia store state export default { computed: { isAuthenticated() { // A simple check (more robust check needed in real app) return !!localStorage.getItem('user-token'); } }, methods: { logout() { localStorage.removeItem('user-token'); // Remove token // Clear user state from store if using Vuex/Pinia // Example: this.$router.push('/login'); // Redirect to login page console.log('Logged out, token removed.'); } } } </script>
- Compare: Instead of manually checking if a global
isLoggedIn
variable is true and then showing/hiding elements with jQuery, Vue ties the visibility directly to theisAuthenticated
data/computed property in the template.
- Use
- Route Protection (Advanced – Requires Vue Router):
- For SPAs, you’ll use Vue Router to manage different pages.
- Navigation Guards in Vue Router allow you to check if a user is authenticated before they can access certain routes (e.g., redirecting them to the login page if they try to go to
/dashboard
while logged out). This is a crucial part of frontend authorization in SPAs.
Module 6: What’s Next? (Taking it Further)
- Vue Router: Essential for building multi-page SPAs with navigation, dynamic routing, and route protection.
- Vuex (Vue 2) / Pinia (Vue 3): State management libraries. Excellent for managing shared application state like the authenticated user’s information, loading states, etc., especially in larger applications. Replaces relying on
localStorage
directly or prop drilling. - Vue CLI / Vite & Build Process: Understand how your
.vue
files are compiled into static assets (HTML, CSS, JS) for deployment. - Environment Variables: Managing API endpoints, etc., for different environments (development, staging, production).
- Error Handling: More robust ways to display API errors and handle different response statuses.
- Testing: Writing unit and integration tests for your Vue components.
Key Takeaways for You:
- You’ll be shifting from directly manipulating the DOM to managing data that drives the DOM updates via Vue’s reactivity.
v-bind
,v-on
,v-if
,v-for
,v-model
are core directives that replace much of your imperative jQuery code.- API calls in Vue are typically done using libraries like Axios within component methods or lifecycle hooks. You update component data based on the API response, and Vue updates the UI.
- Frontend authorization primarily involves managing the authentication token (storing it, sending it with requests) and conditionally rendering UI elements. The security relies on the backend validating the token on every protected API call.
- For non-trivial applications, learn about Single File Components (
.vue
files), build tools (Vite/Vue CLI), and state management (Vuex/Pinia).
This should give you a solid roadmap to start learning Vue.js and how to apply it to connect to your backend API for data and handle frontend authentication aspects. Good luck!