Я следую этому руководству, используя Express.js Postgres и Vue, чтобы создать приложение CRUD для песен с логином и основными ассоциациями: https://www.youtube.com/watch?v=ipYlztBRpp0
Я использую Vuex для управления объектом состояния.Однако по какой-то причине, хотя регистрация новых пользователей работает для создания пользовательской записи, когда я пытаюсь создать закладку, которая, по сути, соединяет UserId с SongId, UserId всегда записывается как ноль.SongId сохранен правильно.Проверка запросов в сети и на вкладках Vue инструментов разработчика показывает, что объект User кажется пустым.
Вот соответствующие файлы.
User.js model
const Promise = require('bluebird')
const bcrypt = Promise.promisifyAll(require('bcrypt-nodejs'))
function hashPassword (user, options) {
const SALT_FACTOR = 8
if (!user.changed('password')) {
return;
}
return bcrypt
.genSaltAsync(SALT_FACTOR)
.then(salt => bcrypt.hashAsync(user.password, salt, null))
.then(hash => {
user.setDataValue('password', hash)
})
}
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true
},
password: DataTypes.STRING
}, {
hooks: {
// beforeCreate: hashPassword,
// beforeUpdate: hashPassword,
beforeSave: hashPassword
}
})
User.prototype.comparePassword = function (password) {
return bcrypt.compareAsync(password, this.password)
}
return User
}
Модель композиций:
module.exports = (sequelize, DataTypes) => {
const Song = sequelize.define('Song', {
title: DataTypes.STRING,
artist: DataTypes.STRING,
genre: DataTypes.STRING,
album: DataTypes.STRING,
albumImageUrl: DataTypes.STRING,
youtubeId: DataTypes.STRING,
lyrics: DataTypes.TEXT,
tab: DataTypes.TEXT
})
return Song
}
Контроллер закладки:
const {Bookmark} = require('../models')
module.exports = {
async index (req, res) {
try {
const {songId, userId} = req.query
const bookmark = await Bookmark.findOne({
where: {
SongId: songId,
UserId: userId
}
})
res.send(bookmark)
} catch (err) {
res.status(500).send({
error: 'An error has occurred trying to fetch the songs'
})
}
},
async post (req, res) {
try {
const {songId, userId} = req.body
const bookmark = await Bookmark.findOne({
where: {
SongId: songId,
UserId: userId
}
})
if (bookmark) {
return res.status(400).send({
error: 'you already have this song bookmarked'
})
}
const newBookmark = await Bookmark.create({
SongId: songId,
UserId: userId
})
res.send(newBookmark)
} catch (err) {
console.log(err)
res.status(500).send({
error: 'An error has occurred trying to create the bookmark.'
})
}
},
async delete (req, res) {
try {
const {bookmarkId} = req.params
const bookmark = await Bookmark.findById(bookmarkId)
await bookmark.destroy()
res.send(bookmark)
} catch (err) {
res.status(500).send({
error: 'An error has occurred trying to delete the bookmark'
})
}
}
}
BookMarksService (для подключения axios)
import Api from '@/services/Api'
export default {
index (bookmark) {
return Api().get('bookmarks', {
params: bookmark
})
},
post (bookmark) {
return Api().post('bookmarks', bookmark)
},
delete (bookmarkId) {
return Api().delete(`bookmarks/${bookmarkId}`)
}
}
и компонент SongsMetaData:
<template>
<panel title='Song Metadata'>
<v-layout>
<v-flex xs6>
<div class="song-title">
{{song.title}}
</div>
<div class="song-artist">
{{song.artist}}
</div>
<div class="song-genre">
{{song.genre}}
</div>
<v-btn
dark
class="cyan"
:to="{
name: 'song-edit',
params () {
return {
songId: song.id
}
}
}">
Edit
</v-btn>
<v-btn
v-if="isUserLoggedIn && !bookmark"
dark
class="cyan"
@click="setAsBookmark">
Bookmark
</v-btn>
<v-btn
v-if="isUserLoggedIn && bookmark"
dark
class="cyan"
@click="unsetAsBookmark">
UnBookmark
</v-btn>
</v-flex>
<v-flex xs6>
<img class="album-image" :src="song.albumImageUrl" />
<br />
<div class="song-album">
{{song.album}}
</div>
</v-flex>
</v-layout>
</panel>
</template>
<script>
import {mapState} from 'vuex'
import BookmarksService from '@/services/BookmarksService'
export default {
props: [
'song'
],
data () {
return {
bookmark: null
}
},
computed: {
...mapState([
'isUserLoggedIn'
])
},
watch: {
async song () {
if (!this.isUserLoggedIn) {
return
}
try {
this.bookmark = (await BookmarksService.index({
songId: this.song.id,
userId: this.$store.state.user.id
})).data
} catch (err) {
console.log(err)
}
}
},
methods: {
async setAsBookmark () {
try {
this.bookmark = (await BookmarksService.post({
songId: this.song.id,
userId: this.$store.state.user.id
})).data
} catch (err) {
console.log(err)
}
},
async unsetAsBookmark () {
try {
await BookmarksService.delete(this.bookmark.id)
} catch (err) {
console.log(err)
}
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.song {
padding: 20px;
height: 330px;
overflow: hidden;
}
.song-title {
font-size: 30px;
}
.song-artist {
font-size: 24px;
}
.song-genre {
font-size: 18px;
}
.album-image {
width: 70%;
margin: 0 auto;
}
textarea {
width: 100%;
font-family: monospace;
border: none;
height: 600px;
border-style: none;
border-color: transparent;
overflow: auto;
padding: 40px;
}
</style>
и хранилище vuexФайл .js:
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
export default new Vuex.Store({
strict: true,
plugins: [
createPersistedState()
],
state: {
token: null,
user: null,
isUserLoggedIn: false
},
mutations: {
setToken (state, token) {
state.token = token
if (token) {
state.isUserLoggedIn = true
} else {
state.isUserLoggedIn = false
}
},
setUser (state, user) {
state.user = user
}
},
actions: {
setToken ({commit}, token) {
commit('setToken', token)
},
setUser ({commit}, user) {
commit('setUser', user)
}
}
})