like/dislike button with api call not working using vue an mongoDB - vue.js

I am learning vuejs and i am working on my first project which is a social network, and i want to implement a like button that call the api to add a like or remove it if the user has already liked it. It does work in my backend but i can't make it work in the front.
I need to send the userId and add or remove the like when i click on the button
This is the data
data() {
return {
post: {
file: "",
content: "",
likes: 0,
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
the last method i tried
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then(() => {
console.log("response", response);
this.user._id = response.data._id;
if(post.usersLiked == user._id) {
this.post.likes += 0
} else if (post.usersLiked != user._id) {
this.post.likes += 1
};
})
.catch((error) => console.log(error));
}
and this is the model
const postSchema = mongoose.Schema({
userId: { type: String, required: true, ref: "User" },
content: { type: String, required: true, trim: true },
imageUrl: { type: String, trim: true },
likes: { type: Number, default: 0 },
usersLiked: [{ type: String, ref: "User" }],
firstname: {type: String, required: true, trim: true },
lastname: {type: String, required: true, trim: true },
created_at: { type: Date},
updated_at: { type: Date }
});
Any idea what is wrong ? Thank you !

.then(() => { // you missed value response from Promise here
this.user._id = response.data._id;
if(post.usersLiked == user._id)
})
Do you mean this.post.usersLiked === user._id I suppose, so post within your data options should be
post: {
file: "",
content: "",
likes: 0,
usersLiked: false,
// something else reflect to your post schema
},
i want to implement a like button that call the api to add a like or remove it if the user has already liked it
By saying that you just need a simple boolean value to do this
likePost(id) {
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
// Just need to toggle state
this.$set(this.post, 'usersLiked', this.post.usersLiked !== response?.data?._id)
})
.catch((error) => console.log(error));
}

Found the answer, i changed the axios method to this
likePost(id) {
let userId = localStorage.getItem('userId');
axios
.post('http://127.0.0.1:3000/api/post/like/' + id, { userId }, {
headers: {
Authorization: "Bearer " + localStorage.getItem("token"),
},
})
.then((response) => {
console.log(response.data);
this.getAllPost();
})
.catch((error) => console.log(error));
}
i also made a few changes to the data
data() {
return {
posts: [],
post: {
file: "",
content: "",
},
showModal: false,
showModifyPost: false,
user: {
firstname: "",
lastname: "",
_id: "",
},
};
},
and i also made some changes on the controller
exports.ratePost = (req, res, next) => {
console.log(req.body.userId)
//using findOne function to find the post
Post.findOne({ _id: req.params.id }).then(post => {
if (!post.usersLiked.includes(req.body.userId)) {
// making a object with $inc and $push methods to add a like and to add the user's id
let toChange = {
$inc: { likes: +1 },
$push: { usersLiked: req.body.userId },
};
// we update the result for the like
Post.updateOne({ _id: req.params.id }, toChange)
// then we send the result and the message
.then(post =>
res
.status(200)
.json(
{ message: "Liked !", data: post }
)
)
.catch(error => res.status(400).json({ error }));
} else if (post.usersLiked.includes(req.body.userId)) {
// using the updateOne function to update the result
Post.updateOne(
{ _id: req.params.id },
// we use a pull method to take off a like
{ $pull: { usersLiked: req.body.userId }, $inc: { likes: -1 } }
)
.then(post => {
// then we send the result and the message
res
.status(200)
.json(
{ message: "Post unliked", data: post }
);
})
.catch(error => res.status(400).json({ error }));
}
});
};

Related

set the new coordinates on the user after editing the address

I'm trying to edit the lat and lng on edit user. I manage to get the location displayed when I add a new user (I use getLocation() for both "add new user" and "edit user"), but the values of the lat and lng don't update when I run the edit function.
I tried to console.log the values and I do get the values of the user I'm editing in the console as well as the new coordinates but the user itself isn't updating.
any ideas?
(I use "vue-browser-geolocation")
export default {
....
name: "UsersBox",
data() {
return {
users: [],
// notEditing: true,
editingId: "",
showAddBox: false,
user: {
img: "janeth carton.jpg",
name: "",
role: "",
liveLocation: "Riviera State 32/106",
address1: "",
address2: "",
phone: "",
coordinates: {
lat: 0,
lng: 0,
},
},
};
},
async mounted() {
this.users = await this.fetchUsers();
},
methods: {
async saveEdit(userData) {
await this.getLocation(userData);
await fetch(`api/users/${userData.id}`, {
method: "PUT",
headers: {
"Content-type": "application/json",
},
body: JSON.stringify(userData),
})
.then((response) => response.json())
.then((userData) => console.log(userData));
this.editingId = "";
},
async getLocation(newUser) {
const API_KEY = "AIzaSyDkWzva7dAFQTO6rQVxZZawYTrcuo04PfI";
const API_URL = `https://maps.googleapis.com/maps/api/geocode/json?address=${newUser.address1}+${newUser.address2}&key=${API_KEY}`;
const apiResponse = await fetch(API_URL);
const locationData = await apiResponse.json();
console.log(locationData, "location");
console.log(locationData.results[0].geometry.location.lng, "lng");
console.log(locationData.results[0].geometry.location.lat, "lat");
console.log(this.user.coordinates.lat, "edit user lat");
this.user.coordinates.lat = locationData.results[0].geometry.location.lng;
this.user.coordinates.lng = locationData.results[0].geometry.location.lat;
},
....
},
};
</script>

How do i pass data to a function under data in vuejs

I tried to reference this.items in the function in taskList.cls.cls
the main idea is to run a task in vue-terminal then pass the result of the post request to this.items but it's returning undefined
data() {
return {
//this is the data i want to pass the post response to
items: [],
query: '',
taskList: {
// your tasks
cls: {
description: 'Clear Terminal',
cls: this, async(pushToList) {
const p = new Promise(resolve => {
this.query = 'SELECT * FROM notifications'
const token = localStorage.getItem('token')
axios.post('/query', {'query': this.query}, {
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
}
}).then(res => {
//i want to reference
**this.items = res.data.data.result**
//
pushToList({type: 'system', label: 'Running query', message: 'Please wait!!'})
}).catch(err => {
console.log(err)
})
setTimeout(() => {
resolve({type: 'success', label: 'Success', message: 'Success!'})
this.test = "worked"
}, 2000)
})
return p
}
}
},
commandList: {
// your commands
}
}
},
Don't do that, call api on "mount()".

Vuex state array turning an proxy object when it is mutated

I develop a project which gets datas from database. I use Vuex for state management.
Vuex Store File
const store = createStore({
state: {
notUser: {
name: "",
email: '',
password: ''
},
user: {
name: '',
email: '',
messages: [],
about: '',
place: '',
age: '',
role: '',
blocked: false
},
problem: {
title: '',
content: ''
},
problems: [],
errorMessage: {
error: false,
message: '',
success: false
},
},
mutations: {
errorHandler(state, error) {
state.errorMessage.error = true
state.errorMessage.message = error.response.data.message
},
defineUser(state, req) {
state.user = req.data.user
console.log(state.user)
},
getProblems(state, problems) {
state.problems = problems
console.log(state.problems)
}
},
actions: {
register({ commit }, notUser) {
axios({
method: 'post',
url: 'http://localhost:3000/api/auth/register',
data: notUser,
withCredentials: true,
headers: {
"Accept": "application/json"
}
})
.then(res => {
this.state.errorMessage.success = true
console.log(res.data.data.user)
})
.catch(err => {
this.state.errorMessage.success = false
console.log(err.response)
commit('errorHandler', err)
})
},
userLogin({commit}, notUser) {
axios({
method: 'post',
url: 'http://localhost:3000/api/auth/login',
data: notUser,
withCredentials: true,
headers: {
"Accept": "application/json"
}
})
.then(res => {
this.state.user = res.data.data.user
this.state.errorMessage.success = true
console.log(this.state.user)
})
.catch(err => {
this.state.errorMessage.success = false
console.log(err.response)
commit('errorHandler', err)
})
},
checkUser({commit}, access_token) {
axios({
method: 'post',
url: 'http://localhost:3000/api/auth/VpW02cG0W2vGeGXs8DdLIq3dQ62qMd0',
data: access_token,
withCredentials: true,
headers: {
"Accept": "application/json"
}
})
.then(res => {
console.log(res)
commit('defineUser', res)
return true
})
.catch(err => {
console.log(err.response)
commit('errorHandler', err)
return false
})
},
sendProblem({commit}, problem) {
axios({
method: 'post',
url: 'http://localhost:3000/api/problem/add',
data: problem,
withCredentials: true,
headers: {
"Accept": "application/json"
}
})
.then(res => {
console.log(res)
return true
})
.catch(err => {
console.log(err.response)
commit('errorHandler', err)
return false
})
},
getAllProblems({commit}) {
axios({
method: 'get',
url: 'http://localhost:3000/api/problem/getall',
withCredentials: true,
headers: {
"Accept": "application/json"
}
})
.then(res => {
commit('getProblems', res.data.data)
return true
})
.catch(err => {
console.log(err.response)
commit('errorHandler', err)
return false
})
}
// registerUser({commit}, user) {
// commit('register', user)
// }
},
Vue Component: Where Vuex store is being used
computed: {
...mapState(["user", 'problems'])
},
mounted() {
return this.getAll()
},
methods: {
...mapActions(['getAllProblems']),
goToAdd() {
this.$router.push('/add')
},
async getAll() {
this.getAllProblems()
}
}
The problem is when I try to request with getAllProblems action, it should mutate problems variable with getProblems(). Actually it does. But after problems variable changes, it turns something a proxy object. Here are images:
Here is an image of proxy object:
The original data coming from database:
Thanks for comment of #Hasan Hasanova
Okay got it. I called api before website is mounted and used function to get variables from store. The other problem was happened because of using wrong syntax of v-for. Here is the code:
computed: {
allProblems() { // this is the problems array that i was trying to get
return this.$store.state.allProblems
},
loader() {
return this.allProblems == null ? true : false
}
},
beforeMount() {
this.$store.dispatch('getAllProblems', {root: true})
},
And here is the template code :
<div v-if="allProblems.length > 0" class="middle-side">
<div v-for="(problem) in allProblems" :key="problem.id" class="card">
<router-link :to="{ name: 'ProblemDetail', params: { id: problem._id, slug: problem.slug }}">
<div class="card-header">
<div class="card-header-title">
<div class="user-image">
<img src="../../assets/problem.png" />
</div>
<span class="user-name">{{ problem.user.name }}</span>
</div>
...
Thanks for all.
I have the same problem as yours, but I solved it first by converting it before the getter's return, converting it to JSON to string, and converting a string to JSON again before returning it.
const str = JSON.stringify(data)
return JSON.parse(str)
You want to use mapActions to call the action. Then get your data via state, instead of returning the function, since the action is calling a mutation via commit.
computed: {
// you have access to `problems` in the template. Use `v-if` before you `v-for` over the array of problems.
...mapState(["user", 'problems'])
},
mounted() {
this.getAllProblems();
},
methods: {
// ...mapActions(['getAllProblems']),
goToAdd() {
this.$router.push('/add')
}
}
For some reason that happens during the passing of res.data.data to mutations. So if you're expecting a single row result set you should do like:
POPULATE_THIS_STATE_VAR(state, data) {
state.thisStateVar = data[0]
}
... and if you're expecting an array of objects to the result set like what you have, you could do like:
POPULATE_THIS_STATE_VAR(state, data) {
if (data) {
for (let i = 0; i < data.length; i++) {
state.thisStateVar .push(data[i])
}
}
}

React Native video file upload

I am currently uploading videos and images using base64 encoding but it was highly recommended to use an alternative to this. I am using RNFetchBlob to read the encoded file and then attach it to SuperAgent for uploading. I have seen some examples of using FormData to attach the file but cannot find a complete working example. If someone could provide a code example on how to achieve this I would greatly appreciate it.
RNFetchBlob.fs.readFile(filePath, 'base64')
.then((base64data) => {
let base64Image = `data:video/mp4;base64,${base64data}`;
let uploadRequest = superagent.post(uploadURL)
uploadRequest.attach('file',base64Image)
Object.keys(params).forEach((key) => {
uploadRequest.field(key,params[key])
})
uploadRequest.on('progress', function(e) {
this.props.setVideoUploadProgress(e.percent)
}.bind(this))
uploadRequest.end((err,resp) => {
})
})
I am using react-native-image-picker to allow users to select or record a video, which gives me a URI of the video file path. Then I use RNFetchBlob to upload it to the server.
RNFetchBlob.fetch('POST', 'Upload API endpoint', {
...this.getHeader(),
'Content-Type': 'multipart/form-data'
// Change BASE64 encoded data to a file path with prefix `RNFetchBlob-file://`.
// Or simply wrap the file path with RNFetchBlob.wrap().
}, [
// element with property `filename` will be transformed into `file` in form data
{ name: 'file', filename: 'video.mp4', data: RNFetchBlob.wrap(this.state.videoUri) },
// custom content type
]).uploadProgress({ interval: 250 }, (written, total) => {
let uploaded = (written / total) * 100
this.setState({
uploadProgress: uploaded.toFixed(1)
})
})
.then((response) => {
if (response.ok) {
this.setState({
uploading: false,
uploadSuccess: true,
uploadFailed: false,
})
}
}).catch((err) => {
this.setState({
uploading: false,
uploadSuccess: false,
uploadFailed: true,
})
})
Basically you have to give the path of your image, audio or video to fetch blob. The following code worked for me:
RNFetchBlob.fetch(
'POST',
`${BASE_URL}vehicle/vehicleRegistration`,
{
Authorization: 'Bearer ' + authToken,
'Content-Type': 'multipart/form-data,octet-stream',
},
[
{
name: 'photo',
filename: 'vid.mp4',
data: RNFetchBlob.wrap(vehicleImage.uri),
},
{
name: 'email',
data: user.email,
},
{
name: 'userId',
data: user.id,
},
{
name: 'vehicleType',
data: values.vehicleType,
},
{
name: 'make',
data: values.make,
},
{
name: 'buildYear',
data: values.buildYear,
},
{
name: 'model',
data: values.model,
},
{
name: 'nickName',
data: values.nickName,
},
{
name: 'engineSize',
data: values.engineSize,
},
],
)
.uploadProgress((written, total) => {
console.log('uploaded', written / total);
})
.then(response => response.json())
.then(RetrivedData => {
console.log('---retrieved data------', RetrivedData);
Toast.show({
text1: 'Success',
text2: 'Vehicle Added to Garage!',
type: 'success',
});
})
.catch(err => {
console.log('----Error in adding a comment----', err);
Toast.show({
text1: 'Request Failed',
text2: err?.response?.data?.message,
type: 'error',
});
});

Mocha Testing a Nested Model

Trying to write a test for a nested model but can't get it working:
Model:
const EmployeeSchema = new mongoose.Schema({
firstName: {type: String, required: true},
lastName: { type: String, required: true}
});
const CompanySchema = new mongoose.Schema({
name: { type: String, required: true },
streetAddress: { type: String, required: true },
country: { type: String, required: true },
employees:[EmployeeSchema]
}, { timestamps: true});
Controller:
function create(req, res, next) {
const company = new Company({
name: req.body.name,
streetAddress: req.body.streetAddress,
country: req.body.country
});
company.employees.push(req.employees);
company.save()
.then(savedCompany => res.json(savedCompany))
.catch(e => next(e));
}
Test:
describe('## Company APIs', () => {
let company = {
name: "Test Company",
streetAddress: "123 Fake Street",
country: "A Country"
};
company.employees.push({firstName: "Jane", lastName: "Doe"});
describe('# POST /api/company', () => {
it('should create a new company', (done) => {
request(app)
.post('/api/company')
.send(company)
.expect(httpStatus.OK)
.then((res) => {
expect(res.body.name).to.equal(company.name);
expect(res.body.streetAddress).to.equal(company.streetAddress);
expect(res.body.country).to.equal(company.country);
company = res.body;
done();
})
.catch(done);
});
});
The above gives: TypeError: Cannot read property 'push' of undefined
I've tried a few other things but this is the most promising result, for some reason I just can't seem to populate the embedded model as part of setting up the unit test.
I ended up resolving this, hopefully this helps someone in the future.
Test:
it('should associate an employee with the company', (done) => {
var employee = new Employee();
company.employees.push(employee);
request(app)
.put(`/api/company/${company._id}`)
.send(company)
.expect(httpStatus.OK)
.then((res) => {
expect(res.body.employees).to.be.an('array')
expect(res.body.employees).to.contain(employee.id)
done();
})
.catch(done);
});
Controller:
Adding this to handle multiple additions:
if (req.body.employees != null) {
req.body.employees.forEach(function(employee) {
company.employees.push(employee);
}, this);
}