Can't figure out why my javascript loop is not working to check presence of object in array - vue.js

I am working on a liking/unliking button, which works as expected, except that for some reason, it does not seem to be able to check if a user has already liked a post.
Here is my computed value where I attempt to perform the check:
isLikedByUser() {
console.log('this.likes count is: ', this.likes.length)
console.log('current user.id is: ', this.me.id)
console.log('these are the likes in the computed value: ', this.likes)
for(const like of this.likes) {
if (like.user.id === this.me.id) {
console.log('true')
return true
} else {
console.log('false')
return false
}
}
}
All of the console.log except the final true/false (which always returns false) work.
here is the console output:
these are the likes: (4) [{…}, {…}, {…}, {…}]
Post.vue?4c00:79 this.likes count is: 4
Post.vue?4c00:80 current user.id is: 1
Post.vue?4c00:81 these are the likes in the computed value: (4) [{…}, {…}, {…}, {…}, __ob__: Observer]
Post.vue?4c00:87 false
The 2nd element of this array is a "like" with users_id = 1, which should match the current user.id and result in a "true" output, so my thought is that the loop is stopping at the first element, but I am not sure.

It's a typo, you should be checking for like.user_id === this.me.id.
for(const like of this.likes) {
if (like.user_id === this.me.id) {
console.log('true')
return true
} else {
console.log('false')
return false
}
}
Also, you can make a less verbose version of the above code by using the some function of JavaScript:
if (this.likes.some(e => e.user_id == this.me.id)) {
console.log(true)
return true
} else {
console.log(false)
return false
}

for anyone coming to this, I figured out the problem. Loop was ending on return, so when the result was false, the for loop would exit before it had checked all of them to find the like that was by the current user. The solution is as follows:
isLikedByUser() {
for(const like of this.likes) {
if (like.user.id === this.me.id) {
return true
}
}
return false
}

Related

Why Am I getting error return in computed property?

I'm using Vuex, inside Getter Foo function I'm returning two values inside array:
return ["Try Again"] or return ["Data result", data], in computed, I'm checking the array length and returning depending on result
computed:{
Foo: function(){
const getFoo = this.$store.getters.Foo;
if(getFoo.length === 1) {
this.existFoo = false
return getFoo[0]
}
this.existFoo = true
return getFoo
}
}
but I'm getting this error, even reading others posts I cannot solve it
34:9 error Unexpected side effect in "Foo" computed property
vue/no-side-effects-in-computed-properties
37:7 error Unexpected
side effect in "Foo" computed property
vue/no-side-effects-in-computed-properties
You are not permitted to change the state in computeds.
Try using another computed instead of existFoo
computed:{
Foo(){
if(this.$store.getters.Foo.length === 1) {
return this.$store.getters.Foo[0]
}
return this.$store.getters.Foo
},
existFoo(){
return this.$store.getters.Foo.length > 1
}
}
Now you should remove existFoo from state
You could use a watcher to watch the store value and set your local variables.
computed: {
getFooFromStore() {
return this.$store.getters.Foo
}
}
watch: {
getFooFromStore: function() {
this.existFoo = this.getFooFromStore[0] ? false : true;
}
}

Vue VeeValidate - How to handle exception is custom validation

I have a custom validation in VeeValidate for EU Vat Numbers. It connects to our API, which routes it to the VIES webservice. This webservice is very unstable though, and a lot of errors occur, which results in a 500 response. Right now, I return false when an error has occured, but I was wondering if there was a way to warn the user that something went wrong instead of saying the value is invalid?
Validator.extend('vat', {
getMessage: field => 'The ' + field + ' is invalid.',
validate: async (value) => {
let countryCode = value.substr(0, 2)
let number = value.substr(2, value.length - 2)
try {
const {status, data} = await axios.post('/api/euvat', {countryCode: countryCode, vatNumber: number})
return status === 200 ? data.success : false
} catch (e) {
return false
}
},
}, {immediate: false})
EDIT: Changed code with try-catch.
You can use:
try {
your logic
}
catch(error) {
warn user if API brokes (and maybe inform them to try again)
}
finally {
this is optional (you can for example turn of your loader here)
}
In your case try catch finally block would go into validate method
OK, first of all I don't think that informing user about broken API in a form validation error message is a good idea :-| (I'd use snackbar or something like that ;) )
any way, maybe this will help you out:
I imagine you are extending your form validation in created hook so maybe getting message conditionaly to variable would work. Try this:
created() {
+ let errorOccured = false;
Validator.extend('vat', {
- getMessage: field => 'The ' + field + ' is invalid.',
+ getMessage: field => errorOccured ? `Trouble with API` : `The ${field} is invalid.`,
validate: async (value) => {
let countryCode = value.substr(0, 2)
let number = value.substr(2, value.length - 2)
const {status, data} = await axios.post('/api/euvat', {countryCode: countryCode, vatNumber: number})
+ errorOccured = status !== 200;
return status === 200 ? data.success : false;
},
}, {immediate: false})
}
After searching a lot, I found the best approach to do this. You just have to return an object instead of a boolean with these values:
{
valid: false,
data: { message: 'Some error occured.' }
}
It will override the default message. If you want to return an object with the default message, you can just set the data value to undefined.
Here is a veeValidate v3 version for this:
import { extend } from 'vee-validate';
extend('vat', async function(value) {
const {status, data} = await axios.post('/api/validate-vat', {vat: value})
if (status === 200 && data.valid) {
return true;
}
return 'The {_field_} field must be a valid vat number';
});
This assumes your API Endpoint is returning json: { valid: true } or { valid: false }

How to loop through an array containing objects and do comparison

I am using ionic 4. I get the result from the API then get the result show like this
[
{"name":John,"age":20},
{"name":Peter,"age":35},
{"name":Alex,"age":15}
]
But I want to get the name only to check whether have same name with my condition or not. But I cannot straight a way get the result from the API, I need to hard code to do comparison. Here is my code:
this.http.get(SERVER_URL).subscribe((res) => {
const data = [
{ name: John, age: 21 },
{ name: Thomas, age: 25 },
];
const ppl= data.find(people=> people.name === 'alex');
console.log(ppl);
});
So, My first question is How to get the name from the API directly, not like now I hard code the result from API. My Second Question is when I do comparison I want to show the result 'already exist' or 'can use this name'. Because if I write my code like this I will get the error Type 'void' is not assignable to type 'boolean':
const ppl= data.find((people)=> {
if(people.name === 'alex') {
this.text = 'already exist'
} else {
this.text = 'can use this name'
}});
console.log(ppl);
Anyone can help me? Thank you very much
Instead of defining data, use the contents of the response; res will have the exact same contents that you are declaring in data.
this.http.get(SERVER_URL).subscribe(res => {
// If successful, res is an array with user data like the following
// [
// {name: "John", age: 21},
// {name: "Thomas", age: 25},
// ...
// ]
if (res.find(user => user.name === 'alex')) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}
});
Taken from the MDN docs on Array.prototype.find():
The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
In that case
res.find(user => user.name === 'alex')
will return a user object if any of the usernames match alex, or undefined if none of the user.name attributes match alex.
undefined evaluates to false and a user object evaluates to true in the conditional.
Keep in mind that you are comparing strings with ===, so, for example, Alex will not match alex, if you want to look into other ways to compare strings, have a look at this question.
You also might want to handle errors, how you handle them is up to you, and it will depend on the response, but you can access the error inside your subscribe like this:
this.http.get(SERVER_URL).subscribe(res => {
if (res.find(user => user.name === 'alex')) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}
}, error => {
console.log(error);
}, () => {
// There is also a 'complete' handler that triggers in both cases
});
Edit. API returns Object not array
If your API returns an Object instead of an array like in your question, you can still iterate over the properties
this.http.get(SERVER_URL).subscribe(res => {
// If successful, res is an array with user data like the following
// {
// key1: {name: "John", age: 21},
// key2: {name: "Thomas", age: 25},
// ...
// }
let match = false;
Object.keys(res).forEach(key => {
if (res[key].name === 'alex') {
match = true;
}
});
if (match) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}
});
Instead of Object.keys() you could use Object.values() to get an array with user objects, then use find() as before, but that seems less efficient, something like this:
if (Object.values(res).find(user => user.name === 'alex')) {
console.log ('Username has been taken');
} else {
console.log('Username is available');
}

Binding class doesn't seem to work properly based on attached variable value

My banner-visible binding class is conditioned by my showMainBanner computed property:
<div :class="['view view-index', { 'banner-visible' : showMainBanner }]">
Here's how I define my computed property:
computed: {
showMainBanner () {
return (Cookies.get('banner_dismiss')) ? false : this.$store.state.config.showMainBanner
}
}
When I reload the page, I can see the value of my computed property by console logging it in the mounted() hook:
mounted () {
console.log('showMainBanner = ' + this.showMainBanner)
}
In the console on page load I see: showMainBanner = false
Yet, the banner-visible class is still there, despite the value of showMainBanner being false.
How is that possible?
The most puzzling thing is this: when I navigate the page away through a link, and come back to it through a link, this problem does NOT occur. But if I refresh the page, it does.
EDIT:
I changed my code to something even more explicit:
showMainBanner () {
if (Cookies.get('banner_dismiss')) {
console.log('RETURN FALSE')
return false
} else {
console.log('RETURN TRUE')
return this.$store.state.config.showMainBanner
}
}
After refreshing the page, I see in the console RETURN FALSE and I still see showMainBanner = false from the console.log() call in mounted() hook. So clearly, showMainBanner is definitely equal to false.
Is there black magic going on here?
Cookies is not reactive. After the first time showMainBanner is evaluated it does not get evaluated again by Vue.
When you navigate away and come back, Vue will evaluate the computed property again and this time the Cookie is already set.
Your issue is because your cookie returns a string:
console.log(typeof Cookies.get('banner_dismiss'))
//=> string
Try to change your condition like:
computed: {
showMainBanner () {
let dismiss = Cookies.get('banner_dismiss') === 'undefined' ? false : JSON.parse(Cookies.get('banner_dismiss'))
return dismiss ? false : this.$store.state.config.showMainBanner
}
}
Or if you want to avoid JSON's errors in case of empty Cookie, you can add a plain condition:
computed: {
showMainBanner () {
let dismiss = Cookies.get('banner_dismiss') === 'true'
return dismiss ? false : this.$store.state.config.showMainBanner
}
}

Howto remove a entry from the tree in a Less visitor plugin

I tried the following:
module.exports = function(less) {
function RemoveProperty() {
this._visitor = new less.visitors.Visitor(this);
};
RemoveProperty.prototype = {
isReplacing: true,
isPreEvalVisitor: true,
run: function (root) {
return this._visitor.visit(root);
},
visitRule: function (ruleNode, visitArgs) {
if(ruleNode.name[0].value != '-some-aribitrary-property')
{
return ruleNode;
}
else
{
return new less.tree.Rule([], [], 0,"");
}
}
};
return RemoveProperty;
};
return new less.tree.Rule([], [], 0,""); still result in a empty output like : ; also return nothing will give me an error: TypeError: Cannot read property 'splice' of undefined.
It can.. but its not ideal from a performance perspective.. return an empty array
visitRule: function (ruleNode, visitArgs) {
if (ruleNode.variable) {
return [];
}
return ruleNode;
},
If you check out the toCSS visitor it does this alot.
But I think it should allow undefined too.. Will look at adding that soon.