Post pandemic, outdoor activities are getting back on track and Coach Finder, a Vue.js webapp, allows learners to find their ideal coaches to learn new sports and coaches to advertise themselves! With this app, you can:
- Browse all coaching offers or use filter to browse specific postings
- Contact a coach by leaving a message and your email
- Register as a coach and post what you want to teach and provide student level requirements and a detailed description
- Check student requests and get his/her email for further discussion
Here is a working live demo : Coach Finder
In this section, I will walk through all the important Vue.js skills I learned from this amazing online course.
- pages folder - Holds components that are loaded as pages through routing e.g. Login/Signup components
- components folder - Other components that are not loaded throgh routing, e.g. Base UI components
- store folder - Vuex modules. Split into separate modules to make each file cleaner
Used throughout the whole project
- Interpolations - {{ expression }}
- Directives - :show, v-if, v-else, v-model โฆ
- Passing data between child and parent components - props and custom events
- Slots (source: src/components/ui/BaseButton.vue)
<template>
<button v-if="!link" :class="mode">
<slot></slot>
</button>
<router-link v-else :to="to" :class="mode">
<slot></slot>
</router-link>
</template>
- Created routing logic using the createRouter function from vue-router module.
- Used nested route for contacting a specific coach
Split into three types of modules and using namespace. (source: src/store/index.js)
import coachesModule from './modules/coaches/index.js'
//...
const store = createStore({
modules: {
coaches: coachesModule,
//...
},
});
Each type of module has an entry index.js that stores states and imports mutations, actions and getters
import mutations from './mutations.js'; import actions from './actions.js'; import getters from './getters.js';
export default { state() {return {//...}; }, mutations, actions, getters }
Used getters to hide state accessing logic. (source: src/store/modules/coaches/getter.js)
isCoach(_, getters, _2, rootGetters) {
const coaches = getters.coaches;
const userId = rootGetters.userId;
return coaches.some(coach => coach.id === userId);
},
Used mutations to modify states. (source: src/store/modules/requests/mutations.js)
addRequest(state, payload) {
state.requests.push(payload);
},
Used actions for asynchronous logics and committing mutations. (source: src/store/modules/requests/actions.js)
async contactCoach(context, payload) {
//...
const response = await fetch(someURL, {
method: 'POST',
body: JSON.stringify(newRequest)
});
//...
context.commit('addRequest', newRequest);
},
- Set up a Firebase database
- Used javascript built-in fetch() function to send http requests
const response = await fetch(url, {
method: 'PUT',
body: JSON.stringify(coachData)
}
);
(source: src/components/ui/BaseDialog.vue)
<transition name="dialog">
<dialog open v-if="show">
//...
</dialog>
</transition>
.dialog-enter-from,
.dialog-leave-to {
opacity: 0;
transform: scale(0.8);
}
.dialog-enter-active {
transition: all 0.3s ease-out;
}
.dialog-leave-active {
transition: all 0.3s ease-in;
}
.dialog-enter-to,
.dialog-leave-from {
opacity: 1;
transform: scale(1);
}
- Using Firebase authentication
- Lock resources based on user logged in or not
- Add navigation guards (source: src/router.js)
router.beforeEach(function (to, _, next) {
if (to.meta.requiresAuth && !store.getters.isAuthenticated) {
next('/auth');
} else if (to.meta.requiresUnauth && store.getters.isAuthenticated) {
next('/coaches');
} else {
next();
}
});
Add lazy loading (source: src/main.js)
const BaseDialog = defineAsyncComponent(() =>
import('./components/ui/BaseDialog.vue'));
Install the dependencies and devDependencies and run in development environment.
npm install
npm run serve