Vue computed property: helper function returns undefined despite being defined - vuejs2

I am using a computed property diameter() to return either:
- a random number (randomise: true)
- a number returned from an object within an array (randomise: false).
I do have a working implementation (see bottom of post) but would like to know why the cleaner implementation doesn't work. With randomise: false, diameter() returns undefined. Why?
vars [
{varName: diameter, varValue: 25.8},
{varName: quantity, varValue: 68}
]
computed: {
diameter() {
if (randomise) {
return math.randomInt(100, 1000) //no problems
} else {
console.log(this.populateValue('diameter')) //undefined
return this.populateValue('diameter')
}
}
}
methods: {
populateValue(variableName) {
this.vars.forEach(element => {
if (element.varName === variableName) {
console.log(element.varValue) //25.8
return element.varValue
}
})
}
}
The following implementation works but why do I have to create an arbitrary property to do so?
diameter() {
if (!this.vars || !this.passVars) {
return math.randomInt(100, 1000) / (10 ** math.randomInt(0, 3))
} else {
this.populateValue('diameter')
return this.blah
}
}
populateValue(variableName) {
this.vars.forEach(element => {
if (element.varName === variableName) {
this.blah = element.varValue
}
})
}

The problem is that return element.varValue is returning from the forEach, not populateValue.
There are various ways to write this. e.g.
for (const element of this.vars) {
if (element.varName === variableName) {
return element.varValue
}
}
By using a for/of loop there is no inner function so the return returns from the function you're expecting.
Alternatives include:
let value = null
this.vars.forEach(element =>
if (element.varName === variableName) {
value = element.varValue
}
})
return value
or:
const match = this.vars.find(element =>
return element.varName === variableName
})
if (match) {
return match.varValue
}

Related

Detox get length of element

Hi i'm using detox and i would like to know how can I get the number of matches to
one element(length).
For example "card" match three times, how can I get the three.
const z = await element(by.id("card"))
https://github.com/wix/Detox/blob/master/docs/APIRef.Expect.md
https://github.com/wix/Detox/blob/master/docs/APIRef.Matchers.md
They don't support it in the API /:
z output:
Element {
_invocationManager: InvocationManager {
executionHandler: Client {
isConnected: true,
configuration: [Object],
ws: [AsyncWebSocket],
slowInvocationStatusHandler: null,
slowInvocationTimeout: undefined,
successfulTestRun: true,
pandingAppCrash: undefined
}
},
matcher: Matcher { predicate: { type: 'id', value: 'card' } }
}
A workaround could be
async function getMatchesLength(elID) {
let index = 0;
try {
while (true) {
await expect(element(by.id(elID)).atIndex(index)).toExist();
index++;
}
} catch (error) {
console.log('find ', index, 'matches');
}
return index;
}
then you can use
const length = await getMatchesLength('card');
jestExpect(length).toBe(3);
Here is my solution in typescript:
async function elementCount(matcher: Detox.NativeMatcher) {
const attributes = await element(matcher).getAttributes();
// If the query matches multiple elements, the attributes of all matched elements is returned as an array of objects under the elements key.
https://wix.github.io/Detox/docs/api/actions-on-element/#getattributes
if ("elements" in attributes) {
return attributes.elements.length;
} else {
return 1;
}
}
Then you can use it like this:
const jestExpect = require("expect");
jestExpect(await elementCount(by.id("some-id"))).toBe(2);

Using set() for computed values correctly in Vue

I'm having an issue with setting a computed property (which is an array). I have the following computed property in my Vue component:
posts: {
get () {
if ( this.type == 'businesses' || this.type == 'business' ) {
return this.$store.getters.getAllBusinesses.map(_business => {
return _business
})
} else if ( this.type == 'shops' || this.type == 'shop' ) {
return this.$store.getters.getAllShops.map(_shop => {
return _shop
})
} else if ( this.type == 'events' || this.type == 'event' ) {
return this.$store.getters.getAllEvents.map(_event => {
return _event
})
} else {
return this.$store.getters.getAllBusinesses.map(_business => {
return _business
})
}
},
set (posts) {
console.log(posts)
this.posts = posts // Doesn't work - this causes a maximum call stack error, recursively setting itself obviously.
return posts // Doesn't work either - this.posts doesn't seem to be changing...
}
},
The console.log(posts) is exactly what I want - a filtered array of posts, see the below console log output.
My question is simply this: how do I go about updated the computed posts value?
If it is useful, I am doing the following manipulation to the posts:
let filteredPosts = []
this.posts.filter(_post => {
_post.category.forEach(category => {
if ( this.categoryFilters.includes(category.slug) ) {
filteredPosts.push(_post)
}
})
})
let uniqueFilteredPosts = [...new Set(filteredPosts)];
this.posts = uniqueFilteredPosts
This is purely to filter them. What I am console.logging is absolutely correct to what I want. So this might be a red herring.
Any pro-Vue tips would be greatly appreciated!
If you want to use a computed setter, you normally assign to whatever values underlie the computed's get function. See the example at the link.
In your case, you examine this.type and return something from the store based on it. The setter should probably examine this.type and set something in the store based on it.

Vue filtered list also orderBy something afterwards?

I have the following computed property :
new Vue({
el: '#app',
data() {
return {
search : '',
users : [{name : "John Doe", email : "xerox#hotmail.us"}, {name : "Jane Doe"}],
}
},
computed : {
filteredUsers() {
if (!this.search) return this.users
var find = function(object, search) {
for (var property in object) {
if (object.hasOwnProperty(property)) {
if (typeof object[property] == "object"){
find(object[property]);
} else if (object[property].includes !== undefined){
if (object[property].includes(search)) return true;
}
}
}
return false;
}
return this.users.filter(user => {
return find(user, this.search)
})
}
}
})
What can I do to order this ones by their name?
I do some filtering now in a computed property, but before I didn't do that and just used this function to do the ordering:
orderedList() {
return _.orderBy(this.users, 'name')
},
How can I combine the two functions or add the orderBy part to the filteredUsers function?
#boussadjira's answer is fine, but if you really don't want the extra function, just use the following as the return from filteredUsers()
return _.orderBy(
this.users.filter(user => find(user, this.search)),
'name'
);
(and similarly in the short-circuit return when search is empty)
Using linq js lib.
computed: {
filteredUsers {
return Enumerable.From(this.users).OrderBy(x => x.name).ToArray();
}
}

Use props inside method in vue.js

I have props like below
props: ['applicants',],
I would like to use props like below
methods : {
formated (item) {
var _self = this;
_self.values.length = 0;
if(item == "") {
console.log(this.applicants); //I am getting output here
_self.values = this.applicants
}
else {
console.log(this.applicants); //I am not getting output here
this.applicants.filter(applicant => { })
}
}
}
Why it is happening like this?
You don't need to create a reference to this. Assuming this.values is an array:
methods: {
formatted (item) {
this.values = item == "" ? this.applicants : this.applicants.filter(applicant => { })
}
}

using contains instead of stringStartsWith knockout js

I have the folliwng on my model:
self.filteredItems = ko.computed(function () {
var filter = this.filter().toLowerCase();
if (!filter) {
return this.sites();
} else {
return ko.utils.arrayFilter(this.sites(), function (item) {
return ko.utils.stringStartsWith(item.Name().toLowerCase(), filter);
});
}
}, self);
I use it for a search on my page but rather than stringStartsWith I'd like some sort of .contains instead so I get results where my searchterm is contained anywhere in the string rather than just at the beginning.
I imagine this must be a pretty common request but couldnt find anything obvious.
Any suggestion?
You can use simply the string.indexOf method to check for "string contains":
self.filteredItems = ko.computed(function () {
var filter = this.filter().toLowerCase();
if (!filter) {
return this.sites();
} else {
return ko.utils.arrayFilter(this.sites(), function (item) {
return item.Name().toLowerCase().indexOf(filter) !== -1;
});
}
}, self);