Related
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 }));
}
});
};
I create a simple Vue.js plugin, and want to test that with npm and jest.
One function should return a document element by id, but it return always null.
Why is that and how can i fix it?
// plugintest.js
const PluginTest = {
install(Vue, options) {
Vue.mixin({
methods: {
getBool: function() { return true; },
getElem: function(id) { return document.getElementById(id); }
}
});
}
}
export default PluginTest;
//plugintest.spec.js
let Vue = require('vue/dist/vue')
import PluginTest from './plugintest.js';
Vue.use(PluginTest);
describe("plugintest.js", () => {
test('test functions', () => {
const wrapper = new Vue({
template: '<div id="div1">Hello, World!</div>' }).$mount();
expect(wrapper.getBool()).toBe(true);
expect(wrapper.getElem('div1')).not.toBe(null); // FAIL!!!
});
});
Error message:
expect(received).not.toBe(expected) // Object.is equality
Expected: not null
19 |
20 | expect(wrapper.getBool()).toBe(true);
> 21 | expect(wrapper.getElem('div1')).not.toBe(null);
| ^
22 | });
23 | });
24 |
at Object.toBe (plugintest.spec.js:21:39)
console.log(wrapper.$el) output:
HTMLDivElement {
__vue__:
Vue {
_uid: 1,
_isVue: true,
'$options':
{ components: {},
directives: {},
filters: {},
_base: [Object],
methods: [Object],
template: '<div id="div1">Hello, World!</div>',
render: [Function: anonymous],
staticRenderFns: [] },
_renderProxy:
Vue {
_uid: 1,
_isVue: true,
'$options': [Object],
_renderProxy: [Circular],
_self: [Circular],
'$parent': undefined,
'$root': [Circular],
'$children': [],
'$refs': {},
_watcher: [Object],
_inactive: null,
_directInactive: false,
_isMounted: true,
_isDestroyed: false,
_isBeingDestroyed: false,
_events: {},
_hasHookEvent: false,
_vnode: [Object],
_staticTrees: null,
'$vnode': undefined,
'$slots': {},
'$scopedSlots': {},
_c: [Function],
'$createElement': [Function],
'$attrs': [Getter/Setter],
'$listeners': [Getter/Setter],
_watchers: [Object],
getBool: [Function: bound getBool],
getElem: [Function: bound getElem],
_data: {},
'$el': [Circular] },
_self: [Circular],
'$parent': undefined,
'$root': [Circular],
'$children': [],
'$refs': {},
_watcher:
Watcher {
vm: [Circular],
deep: false,
user: false,
lazy: false,
sync: false,
before: [Function: before],
cb: [Function: noop],
id: 2,
active: true,
dirty: false,
deps: [],
newDeps: [],
depIds: Set {},
newDepIds: Set {},
expression: 'function () {\n vm._update(vm._render(), hydrating);\n }',
getter: [Function: updateComponent],
value: undefined },
_inactive: null,
_directInactive: false,
_isMounted: true,
_isDestroyed: false,
_isBeingDestroyed: false,
_events: {},
_hasHookEvent: false,
_vnode:
VNode {
tag: 'div',
data: [Object],
children: [Object],
text: undefined,
elm: [Circular],
ns: undefined,
context: [Circular],
fnContext: undefined,
fnOptions: undefined,
fnScopeId: undefined,
key: undefined,
componentOptions: undefined,
componentInstance: undefined,
parent: undefined,
raw: false,
isStatic: false,
isRootInsert: true,
isComment: false,
isCloned: false,
isOnce: false,
asyncFactory: undefined,
asyncMeta: undefined,
isAsyncPlaceholder: false },
_staticTrees: null,
'$vnode': undefined,
'$slots': {},
'$scopedSlots': {},
_c: [Function],
'$createElement': [Function],
'$attrs': [Getter/Setter],
'$listeners': [Getter/Setter],
_watchers: [ [Object] ],
getBool: [Function: bound getBool],
getElem: [Function: bound getElem],
_data: {},
'$el': [Circular] } }
Try using vm.$el.
expect(wrapper.$el).not.toBe(null);
The root DOM element that the Vue instance is managing.
Reference
NOTICE
If you want to access another elements, your code could look something like this:
expect(wrapper.$el.querySelector("div")).not.toBe(null);
Another option is to use VueTestUtils's find mthod: https://vue-test-utils.vuejs.org/api/wrapper/find.html#find
Using VueTestUtils
Try using attachToDocument option.
Reference
import { mount, createLocalVue } from "#vue/test-utils";
import PluginTest from "./plugintest.js";
const localVue = createLocalVue();
localVue.use(PluginTest);
it("plugintest.js", () => {
const component = {
template: "<div id="div1">Hello, World!</div>"
}
const wrapper = mount(component, {
localVue,
attachToDocument: true
});
expect(wrapper.vm.getElem("div1")).not.toBe(null);
});
I think you'r missing the parameter for getElem(), try it like:
expect(wrapper.getElem('div1')).not.toBe(null);
I want to upload a file with Parasails. But I have a error :
<- POST /api/v1/admin/create-article (3ms 400)
| no file attached
| No file was attached.
°
I suppose to my syntaxe is not good.
My html code :
<div class="form-group">
<label for="imgFile">Image:</label>
<input class="form-control-file" id="imgFile" type="file" :class="[formErrors.imgFile ? 'is-invalid' : '']" autocomplete="imgFile">
<div class="invalid-feedback" v-if="formErrors.imgFile">S'il vous plaît, entrez une image valide.</div>
</div>
My action 2 :
module.exports = {
files: ['imgFile'],
friendlyName: 'Create article',
description: '',
inputs: {
imgFile: {
description: 'Upstream for an incoming file upload.',
type: 'ref'
},
titre: {
type: 'string',
required: true,
},
description: {
type: 'string',
required: true,
},
contenue: {
type: 'string',
required: true,
},
postDate:{
type: 'string',
required: false,
},
etiquette:{
type: 'number',
required: false,
},
sharingLink:{
type: 'string',
required: false,
}
},
exits: {
success: {
outputDescription: 'The newly created `Thing`.',
outputExample: {}
},
noFileAttached: {
description: 'No file was attached.',
responseType: 'badRequest'
},
tooBig: {
description: 'The file is too big.',
responseType: 'badRequest'
},
},
fn: async function (inputs, exits) {
var util = require('util');
// Upload the image.
var info = await sails.uploadOne(inputs.imgFile, {
maxBytes: 3000000
})
// Note: E_EXCEEDS_UPLOAD_LIMIT is the error code for exceeding
// `maxBytes` for both skipper-disk and skipper-s3.
.intercept('E_EXCEEDS_UPLOAD_LIMIT', 'tooBig')
.intercept((err)=>new Error('The photo upload failed: '+util.inspect(err)));
if(!info) {
throw 'noFileAttached';
}
var unurl = await sails.helpers.convertUrl(inputs.titre);
await Article.create({titre:inputs.titre, description:inputs.description, contenue:inputs.contenue ,postDate:inputs.postDate ,sharingLink:inputs.sharingLink,url:unurl, etiquette:inputs.etiquette}).fetch();
return exits.success();
}
};
My Root :
'POST /api/v1/admin/create-article': { action: 'admin/create-article' },
I have add this package on my project : sails-hook-uploads
I'm new to Sails and I do not understand this error.
Thanks!
How to upload a file using the new controller actions format in sails.js
Thanks you for your answers !
I think it's a front-end problem. When I make a "console.log" on "inputs" in my action:
{ imgFile:
Upstream {
_fatalErrors: [],
isNoop: true,
_files: [],
timeouts: { untilFirstFileTimer: [Object], untilMaxBufferTimer: [Object] },
_readableState:
ReadableState {
objectMode: true,
highWaterMark: 16,
buffer: [Object],
length: 0,
pipes: null,
pipesCount: 0,
flowing: null,
ended: false,
endEmitted: false,
reading: false,
sync: true,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
destroyed: false,
defaultEncoding: 'utf8',
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null },
readable: true,
domain: null,
_events: { error: [Function] },
_eventsCount: 1,
_maxListeners: undefined,
fieldName: 'NOOP_imgFile' },
titre: 'inputs inputs inputs inputs',
description: 'inputs inputs inputs',
contenue: 'inputs inputs inputs',
postDate: '2019-12-21T09:13',
etiquette: 1 }
On the Chrome dev tool :
response of the tool
I think is not a websocket request is XMLHttpRequest (type;xhr).
module.exports = {
friendlyName: 'Update profile avatar',
description: '',
inputs: {
},
exits: {
success: {
responseType: 'redirect'
},
failure: {
responseType: 'redirect'
},
unhandleError: {
responseType: 'redirect',
}
},
fn: async function (inputs, exits) {
let req = this.req;
let res = this.res;
req.file('avatar').upload({
saveAs: fileName,
dirname: require('path').resolve(sails.config.appPath,
'assets/uploads')
}, function whenDone(err, uploadedFile) {
// your code here
});
}
};
This is my code in controller when using req.file('file-name').upload() function. No need to define file in inputs because using req
I defined an object in data as
export default {
data() {
return {
labelPosition: 'right',
isText: false,
isDate: false,
isExam: false,
isFile: false,
isWrite: false,
stepLists: [],
flowId: '',
txtName: '',
form: {
textName: '',
textPosition: '',
}
the html like this :
when I change the form.textName ,I found it doesn't work
this.$set(this.form, 'textName', temp.name) //not work
this.form={textName:'abc'}
this.form = Object.assign({}, this.form)
//not work
this.$set(this.form,'textName', '---------------------') work well.
I'm trying to add a element to a array, which is called allLogbooks. This array contains objects called Logbook. My issue is, how do I fit in code so that it adds the new element to the array which is declared in my reducer? This is my initial state:
const initialState = {
isFetching: false,
error: undefined,
isRegistered: false,
isUpdated: false,
hasChecked: false,
isError: false,
registerStepOne: true,
registerStepTwo: false,
registerStepThree: false,
registerStepFour: false,
logbooks: undefined,
allLogbooks: [],
userInfo: undefined,
logbookAddSuccess: false,
newLogbook: undefined,
graphFilter: 1
}
This is my reducer:
case ADD_LOGBOOK_SUCCESS:
allLogbooks.push(action.newLogbook);
return {
...state,
isFetching: false,
logbookAddSuccess: true,
isUpdated: true,
isError: false,
}
And this is my action:
function addLogbookSuccess(newLogbook) {
return {
type: ADD_LOGBOOK_SUCCESS
}
}
I then make a POST call to a nodejs server, where it will respond with a message if it was successful, and the logbook which was just created. the following data is what it returns:
{
"success": true,
"logbook": {
"created_by": "",
"created_at": "",
"_id": "",
"__v": 0
}
}
I then dispatch in the API call, as such:
.then(data => data.json())
.then(json => {
Keyboard.dismiss();
dispatch(addLogbookSuccess(json.logbook));
})
.catch(err => dispatch(addLogbookFailed(err)))
I've substituted this by using AsyncStorage to access the array abroad all my views, but I know this is wrong.
I am struggling a little to comprehend your question so I've tried my best. Please let me know if I'm completely off with my answer.
Your reducer:
case ADD_LOGBOOK_SUCCESS: {
const newLogbook = action.payload;
return {
...state,
isFetching: false,
logbookAddSuccess: true,
isUpdated: true,
isError: false,
allLogBooks: [
...state.allLogBooks,
newLogbook
]
}
}
Your action:
function addLogbookSuccess(newLogbook) {
return {
type: ADD_LOGBOOK_SUCCESS,
payload: newLogbook
}
}