Vue.js consumir API REST

Consumir API REST Django com Vue.js
Voltar
Conteúdo disponível

Objetivo:

Consumir API REST Django de músicas, via projeto Vue.js.

  • Pré-requisitos: Python, PIP, Node.js, npm
  • Instalar Django: pip3 install django djangorestframework markdown django-filter django-cors-headers --break-system-packages
  • Instalar Vue.js: npm install -g @vue/cli

API REST Django:

  1. Baixar API REST GitHub (musicApiRest): Acesse
  2. Entrar na pasta da API REST (musicApiRest) e executar os comandos:
    
    python3 manage.py makemigrations
    python3 manage.py migrate
    python3 manage.py runserver
    
  3. Rotas de acesso:
    
    Listar musics: (GET) http://localhost:8000/api/musics/
    Adicionar music: (POST) http://localhost:8000/api/musics/ (json) { "title": "Nome da música", "artist": "Nome do artista" }
    Ver music 1: (GET) http://localhost:8000/api/musics/1/
    Atualizar music 1: (PUT) http://localhost:8000/api/musics/1/ (json) { "title": "Nome da música", "artist": "Nome do artista" }
    Deletar music 1: (DELETE) http://localhost:8000/api/musics/1/
    

Vue.js:

  1. No mesmo diretório em que encontra-se pasta da API REST Django, criar projeto Vue.js: vue create music-vue
  2. Entrar na pasta do projeto (music-vue), instalar Axios e Router: npm install axios vue-router
  3. Informar conteúdo nos arquivos, conforme abaixo
  4. Executar projeto: npm run serve

/src/assets/styles/global.css:


.error {
    color: red;
    font: 14px;
}

/src/components/AddMusic.vue:


<template>
    <h1>Adicionar Música</h1>
    <form @submit.prevent="addMusic">
        <input type="text" placeholder="Título" v-model="newMusic.title" required>
        <input type="text" placeholder="Artista" v-model="newMusic.artist" required>
        <button type="submit">Confirmar</button>
        <p v-if="errorMessage" class="error">{{ errorMessage }}</p>
    </form>
    <button @click="goBack">Cancelar</button>
</template>

<script>
import { ref } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';

export default {
    setup() {
        const newMusic = ref({ title: '', artist: '' });
        const errorMessage = ref('');
        const router = useRouter();

        const addMusic = async () => {
            try {
                const response = await axios.get('http://localhost:8000/api/musics/', {
                    params: {
                        title: newMusic.value.title,
                        artist: newMusic.value.artist,
                    },
                });

                const musicExists = response.data.some(
                    (music) =>
                        music.title === newMusic.value.title &&
                        music.artist === newMusic.value.artist
                );

                if (musicExists) {
                    errorMessage.value = 'Música já cadastrada';
                } else {
                    await axios.post('http://localhost:8000/api/musics/', newMusic.value);
                    router.push({ name: 'list-music' });
                }
            } catch (error) {
                console.error("Erro ao adicionar música", error);
                errorMessage.value = 'Erro ao adicionar música';
            }
        };

        const goBack = () => {
            router.push({ name: 'list-music' });
        };

        return {
            newMusic,
            errorMessage,
            addMusic,
            goBack,
        };
    },
};
</script>

<style scoped></style>

src/components/ConfirmDialogDelete.vue:


<template>
    <div class="confirm-dialog">
        <p>Excluir música "{{ music.title }}" (id: {{ music.id }})?</p>
        <button @click="cancel">Cancelar</button>
        <button @click="confirm">Confirmar</button>
    </div>
</template>

<script>
export default {
    props: {
        music: {
            type: Object,
            required: true
        }
    },
    methods: {
        cancel() {
            this.$emit('cancel');
        },
        confirm() {
            this.$emit('confirm', this.music.id);
        }
    }
};
</script>

<style scoped></style>

src/components/DeleteMusic.vue:


<template>
    <h1>Exclusão</h1>
    <p>Excluir música?</p>
    <p>Id: {{ music.id }}</p>
    <p>Título: {{ music.title }}</p>
    <p>Artista: {{ music.artist }}</p>
    <button @click="confirmDelete">Confirmar</button>
    <button @click="cancel">Cancelar</button>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';

export default {
    setup() {
        const router = useRouter();
        const music = ref({});
        const musicId = router.currentRoute.value.params.id;

        const fetchMusicDetails = async () => {
            try {
                const response = await axios.get(`http://localhost:8000/api/musics/${musicId}/`);
                music.value = response.data;
            } catch (error) {
                console.error('Erro ao buscar dados da música:', error);
            }
        };

        const confirmDelete = async () => {
            try {
                await axios.delete(`http://localhost:8000/api/musics/${musicId}/`);
                router.push({ name: 'list-music' });
            } catch (error) {
                console.error('Erro ao excluir música:', error);
            }
        };

        const cancel = () => {
            router.push({ name: 'list-music' });
        };

        onMounted(fetchMusicDetails);
        return {
            music,
            confirmDelete,
            cancel,
        };
    },
};
</script>

<style scoped></style>

/src/components/EditMusic.vue:


<template>
    <h1>Editar música</h1>
    <form @submit.prevent="submitEdit">
        <input type="text" placeholder="Título" v-model="music.title" required>
        <input type="text" placeholder="Artista" v-model="music.artist" required>
        <button type="submit">Confirmar</button>
        <button @click="cancelEdit">Cancelar</button>
    </form>

    <div v-if="errorMessage" class="error">{{ errorMessage }}</div>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRoute, useRouter } from 'vue-router';

export default {
    setup() {
        const music = ref({ title: '', artist: '' });
        const originalMusic = ref({ title: '', artist: '' });
        const errorMessage = ref('');
        const router = useRouter();
        const route = useRoute();
        const musicId = route.params.id;
        const musics = ref([]);

        const fetchMusics = async () => {
            try {
                const response = await axios.get('http://localhost:8000/api/musics/');
                musics.value = response.data;
            } catch (error) {
                console.error('Erro ao buscar música', error);
            }
        };

        const fetchMusic = async () => {
            try {
                const response = await axios.get(`http://localhost:8000/api/musics/${musicId}/`);
                music.value = response.data;
                originalMusic.value = { ...response.data };
            } catch (error) {
                console.error('Erro ao buscar música', error);
            }
        };

        const validateMusic = () => {
            if (music.value.title === originalMusic.value.title && music.value.artist === originalMusic.value.artist) {
                return true;
            }

            const existingMusic = musics.value.find(
                (m) => m.title === music.value.title && m.artist === music.value.artist && m.id !== musicId
            );
            if (existingMusic) {
                errorMessage.value = 'Música já cadastrada';
                return false;
            }

            errorMessage.value = '';
            return true;
        };

        const submitEdit = async () => {
            if (!validateMusic()) {
                return;
            }

            try {
                await axios.put(`http://localhost:8000/api/musics/${musicId}/`, music.value);
                router.push({ name: 'list-music' });
            } catch (error) {
                console.error('Erro ao editar música', error);
            }
        };

        const cancelEdit = () => {
            router.push({ name: 'list-music' });
        };

        onMounted(() => {
            fetchMusics();
            fetchMusic();
            
        });

        return {
            music,
            errorMessage,
            submitEdit,
            cancelEdit,
        };
    },
};
</script>

<style scoped></style>

/src/components/ListMusic.vue:


<template>
    <div v-if="musics.length === 0">
        <p>Não há músicas disponíveis.</p>
    </div>

    <ul v-else>
        <li v-for="music in musics" :key="music.id">
            <div v-if="!confirmingMusicId || confirmingMusicId !== music.id">
                Id: {{ music.id }}
                Título: {{ music.title }}
                Artista: {{ music.artist }}
                <button @click="viewMusic(music.id)">Ver</button>
                <button @click="goToEditMusic(music.id)">Editar</button>
                <button @click="goToDeleteMusic(music.id)">Excluir</button>
                <button @click="startConfirmDelete(music.id)">ExcluirDiretamente</button>
            </div>

            <ConfirmDialogDelete v-if="confirmingMusicId === music.id" :music="music" @cancel="cancelDelete" @confirm="confirmDelete" />
        </li>
    </ul>

    <button @click="goToAddMusic">Adicionar música</button>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
import ConfirmDialogDelete from './ConfirmDialogDelete.vue';

export default {
    components: {
        ConfirmDialogDelete
    },
    setup() {
        const musics = ref([]);
        const confirmingMusicId = ref(null);
        const router = useRouter();

        const fetchMusics = async () => {
            try {
                const response = await axios.get('http://localhost:8000/api/musics/');
                musics.value = response.data;
            } catch (error) {
                console.error('Erro ao buscar música', error);
            }
        };

        onMounted(fetchMusics);

        const viewMusic = (id) => {
            router.push({ name: 'music-detail', params: { id } });
        };

        const goToDeleteMusic = (id) => {
            router.push({ name: 'delete-music', params: { id } });
        };

        const goToEditMusic = (id) => {
            router.push({ name: 'edit-music', params: { id } });
        };

        const goToAddMusic = () => {
            router.push({ name: 'add-music' });
        };

        const startConfirmDelete = (id) => {
            confirmingMusicId.value = id;
        };

        const cancelDelete = () => {
            confirmingMusicId.value = null;
        };

        const confirmDelete = async (id) => {
            try {
                await axios.delete(`http://localhost:8000/api/musics/${id}/`);
                musics.value = musics.value.filter((music) => music.id !== id);
                confirmingMusicId.value = null;
            } catch (error) {
                console.error('Erro ao excluir música', error);
            }
        };

        return {
            musics,
            confirmingMusicId,
            viewMusic,
            goToDeleteMusic,
            goToEditMusic,
            goToAddMusic,
            startConfirmDelete,
            cancelDelete,
            confirmDelete,
        };
    },
};
</script>

<style scoped></style>

/src/components/ShowMusic.vue:


<template>
    <h1>Música:</h1>
    <p>Id: {{ music.id }}</p>
    <p>Título: {{ music.title }}</p>
    <p>Artista: {{ music.artist }}</p>
    <button @click="goBack">Voltar</button>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';

export default {
    setup() {
        const music = ref({});
        const router = useRouter();
        const musicId = router.currentRoute.value.params.id;

        const fetchMusicDetails = async () => {
            try {
                const response = await axios.get(`http://localhost:8000/api/musics/${musicId}/`);
                music.value = response.data;
            } catch (error) {
                console.error("Erro ao buscar dados da música:", error);
            }
        };

        onMounted(fetchMusicDetails);

        const goBack = () => {
            router.push({ name: 'list-music' });
        };
        return {
            music,
            goBack,
        };
    },
};
</script>

<style scoped></style>

/src/router/index.js:


import { createRouter, createWebHistory } from "vue-router";
import ListMusic from '../components/ListMusic.vue';
import ShowMusic from '../components/ShowMusic.vue';
import EditMusic from '../components/EditMusic.vue';
import AddMusic from '../components/AddMusic.vue';
import DeleteMusic from '../components/DeleteMusic.vue';

const routes = [
    {
        path: '/',
        name: 'list-music',
        component: ListMusic,
    },
    {
        path: '/music/:id',
        name: 'music-detail',
        component: ShowMusic,
    },
    {
        path: '/edit/:id',
        name: 'edit-music',
        component: EditMusic,
    },
    {
        path: '/add',
        name: 'add-music',
        component: AddMusic,
    },
    {
        path: '/delete/:id',
        name: 'delete-music',
        component: DeleteMusic,
    },
];

const router = createRouter({
    history: createWebHistory(process.env.BASE_URL),
    routes,
});

export default router;

/src/App.vue:


<template>
    <div id="app">
        <h1>Lista de músicas UB Social</h1>
        <router-view></router-view>
    </div>
</template>

<script>
export default {
    name: 'App',
};
</script>

<style scoped></style>

/src/main.js:


import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import './assets/styles/global.css';

const app = createApp(App);
app.use(router);
app.mount('#app');

vue.config.js:


const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
    transpileDependencies: true
})

Elaborado por Mateus Schwede
ubsocial.github.io