vue this.array.find in methods/computed properties gives undefined - javascript

I have an array (approvalOptions) that i receive from getters in a component, from which i need to find a certain value.
Things i have tried:
Use it in methods (read from ...mapGetters) like so
methods: {
approvalDisplayName(value) {
console.log(this.approvalOptions)
return this.approvalOptions.find(it => it.key === value).displayName;
},
},
use it in computed (read from ...mapGetters) (code sample same as above, only difference being the use of computed properties' block instead of methods'
use it in methods (read from data properties) like so
data() {
return {
approvalOptions: [...(objects with keys and values here)]
}
},
methods: {
approvalDisplayName(value) {
console.log(this.approvalOptions)
return this.approvalOptions.find(it => it.key === value).displayName;
},
},
All three times I get an error in the console that approvalOptions.find(...) doesnt exist. The console.log also prints an array in the console so i really don't understand what is going on here.

Maybe this can help you
computed: {
approvalOptions() {
return [
{ key: 1, displayName: "Name 1" },
{ key: 2, displayName: "Name 2" }
];
}
},
methods: {
approvalDisplayName(options, value) {
return options.find((it) => it.key === value).displayName;
}
},
created() {
console.log(this.approvalDisplayName(this.approvalOptions, 1));
}

Related

Is there a shorter way to write the below code?

I'm wondering if there is another shorter way to write the below code:
let playerListQuery = {
variables = {
input: {
pagination,
order: { field: PlayerOrderField.CreatedAtDesc },
where: {
// some others...
},
},
}
};
function updateSearch(value) {
if (!value) return;
playerListQuery.variables = {
...playerListQuery.variables,
input: {
...playerListQuery.variables.input,
where: {
...playerListQuery.variables.input.where,
or: [
{ nameContains: searchValue },
{
teamHas: [
{
or: [
{ nameContains: searchValue },
{ addressContains: searchValue },
],
},
],
},
],
},
},
};
}
As you can see I'm only interested to change the where field in updateSearch.
Is there a shorter way?
You could do a deep copy of your query then send the modified copy.
Something like so:
function updateSearch(value) {
if (!value) return;
const newQuery = deepCopy(playerListQuery);
newQuery.variables.input.where = { /* *** */ };
return newQuery;
}
As for the function that does the deep copy, if your JS object is serializable, JSON.parse(JSON.stringify(yourObject)) is a quick and easy way to deep copy
If you have more complex needs, you could look into libraries (e.g.: lodash's cloneDeep function).

Vue.js - watch particular properties of the object and load data on change

I have Vue component with prop named product, it is an object with a bunch of properties. And it changes often.
export default {
props: {
product: {
type: Object,
default: () => {},
},
},
watch: {
'product.p1'() {
this.loadData()
},
'product.p2'() {
this.loadData()
},
},
methods: {
loadData() {
doApiRequest(this.product.p1, this.product.p2)
}
},
}
The component should load new data when only properties p1 and p2 of product are changed.
The one approach is to watch the whole product and load data when it is changed. But it produces unnecessary requests because p1 and p2 may not have changed.
Another idea is to watch product.p1 and product.p2, and call the same function to load data in each watcher.
But it may happen that both p1 and p2 changed in the new version of the product, it would trigger 2 calls.
Will it be a good solution to use a debounced function for data load?
Or rather use single watcher for the whole product and compare new p1 and p2 stringified with their old stringified versions to determine if data loading should be triggered?
There are several approaches to this, each with pros and cons.
One simple approach I do is to use a watch function that accesses each of the properties you want to watch and then returns a new empty object. Vue knows product.p1 and product.p2 were accessed in the watch function, so it will re-execute it any time either of those properties change. Then, by returning a new empty object instance from the watch function, Vue will trigger the watch handler because the watch function returned a new value (and thus what is being watched "changed").
created() {
this.$watch(() => {
// Touch the properties we want to watch
this.product.p1;
this.product.p2;
// Return a new value so Vue calls the handler when
// this function is re-executed
return {};
}, () => {
// p1 or p2 changed
})
}
Pros:
You don't have to stringify anything.
You don't have to debounce the watch handler function.
Cons:
You can't track the previous values of p1 and p2.
Take care if this.product could ever be null/undefined.
It will always trigger when p1 or p2 are changed; even if p1 and p2 are set back to their previous values before the next micro task (i.e. $nextTick()); but this is unlikely to be a problem in most cases.
You need to use this.$watch(). If you want to use the watch option instead then you need to watch a computed property.
Some of these cons apply to other approaches anyway.
A more compact version would be:
this.$watch(
() => (this.product.p1, this.product.p2, {}),
() => {
// changed
}
})
As some of other developers said you can use computed properties to monitor the changing of product.p1 or product.p2 or both of them and then calling loadData() method only once in each case. Here is the code of a hypothetical product.vue component:
<template>
<div>
this is product compo
</div>
</template>
<script>
export default {
name: "product",
watch: {
p1p2: function(newVal, oldVal) {
this.loadData();
}
},
props: {
productProp: {
type: Object,
default: () => {},
},
},
computed: {
p1p2: function() {
return this.productProp.p1 + this.productProp.p2;
}
},
methods: {
loadData() {
console.log("load data method");
}
},
}
</script>
I renamed the prop that it received to productProp and watched for a computed property called p1p2 in that. I supposed that the values of data are in String format (but if they are not you could convert them). Actually p1p2 is the concatenation of productProp.p1 and productProp.p2. So changing one or both of them could fire the loadData() method. Here is the code of a parent component that passes data to product.vue:
<template>
<section>
<product :productProp = "dataObj"></product>
<div class="d-flex justify-space-between mt-4">
<v-btn #click="changeP1()">change p1</v-btn>
<v-btn #click="changeP2()">change p2</v-btn>
<v-btn #click="changeBoth()">change both</v-btn>
<v-btn #click="changeOthers()">change others</v-btn>
</div>
</section>
</template>
<script>
import product from "../components/product";
export default {
name: 'parentCompo',
data () {
return {
dataObj: {
p1: "name1",
p2: "name2",
p3: "name3",
p4: "name4"
}
}
},
components: {
product
},
methods: {
changeP1: function() {
if (this.dataObj.p1 == "name1") {
this.dataObj.p1 = "product1"
} else {
this.dataObj.p1 = "name1"
}
},
changeP2: function() {
if (this.dataObj.p2 == "name2") {
this.dataObj.p2 = "product2"
} else {
this.dataObj.p2 = "name2"
}
},
changeBoth: function() {
if (this.dataObj.p2 == "name2") {
this.dataObj.p2 = "product2"
} else {
this.dataObj.p2 = "name2"
}
if (this.dataObj.p1 == "name1") {
this.dataObj.p1 = "product1"
} else {
this.dataObj.p1 = "name1"
}
},
changeOthers: function() {
if (this.dataObj.p3 == "name3") {
this.dataObj.p3 = "product3"
} else {
this.dataObj.p3 = "name3"
}
}
},
}
</script>
You can test the change buttons to see that by changing dataObj.p1 or dataObj.p2 or both of them the loadData() method only called once and by changing others it is not called.
for you do ontouch event in vuejs while using it with your HTML inline
and you have an object model that house you other variable and you need to validate the any of the variable you will need to put them in watcher and use qoute ('') to tell vuejs that this is from a the model.email
hope this is useful
data() {
return {
model: {},
error_message: [],
}
},
watch: {
'model.EmailAddress'(value) {
// binding this to the data value in the email input
this.model.EmailAddress = value;
this.validateEmail(value);
},
},
methods: {
validateEmail(value) {
if (/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(value)) {
this.error_message['EmailAddress'] = '';
} else {
this.error_message['EmailAddress'] = 'Invalid Email Address';
}
}
},

Assignment of Nuxt Axios response to variable changes response data content

async fetch() {
try {
console.log(await this.$api.events.all(-1, false)); // <-- First log statement
const res = await this.$api.events.all(-1, false); // <-- Assignment
console.log(res); // <-- Second log statement
if (!this.events) {
this.events = []
}
res.data.forEach((event, index) => {
const id = event.hashid;
const existingIndex = this.events.findIndex((other) => {
return other.hashid = id;
});
if (existingIndex == -1) {
this.events.push(events);
} else {
this.events[existingIndex] = event;
}
});
for (var i = this.events.length - 1; i >= 0; --i) {
const id = this.events[i].hashid
const wasRemoved =
res.data.findIndex((event) => {
return event.hashid == id
}) == -1
if (wasRemoved) {
this.events.splice(i, 1)
}
}
this.$store.commit('cache/updateEventData', {
updated_at: new Date(Date.now()),
data: this.events
});
} catch (err) {
console.log(err)
}
}
// The other functions, maybe this somehow helps
async function refreshTokenFirstThen(adminApi, func) {
await adminApi.refreshAsync();
return func();
}
all(count = -1, description = true) {
const func = () => {
return $axios.get(`${baseURL}/admin/event`, {
'params': {
'count': count,
'description': description ? 1 : 0
},
'headers': {
'Authorization': `Bearer ${store.state.admin.token}`
}
});
}
if (store.getters["admin/isTokenExpired"]) {
return refreshTokenFirstThen(adminApi, func);
}
return func();
},
Both log statements are giving slightly different results even though the same result is expected. But this only happens when is use the function in this specific component. When using the same function in other components, everything works as expected.
First data output:
[
{
"name": "First Name",
"hashid": "VQW9xg7j",
// some more correct attributes
},
{
"name": "Second name",
"hashid": "zlWvEgxQ",
// some more correct attributes
}
]
While the second console.log gives the following output:
[
{
"name": "First Name",
"hashid": "zlWvEgxQ",
// some more correct attributes, but this time with reactiveGetter and reactiveSetter
<get hashid()>: reactiveGetter()
​​ length: 0
​​​​ name: "reactiveGetter"
​​​​ prototype: Object { … }
​​​​<prototype>: function ()
​​​<set hashid()>: reactiveSetter(newVal)
​​​​length: 1
​​​​name: "reactiveSetter"
​​​​prototype: Object { … }
​​​​<prototype>: function ()
},
{
"name": "Second name",
"hashid": "zlWvEgxQ",
// some more correct attributes and still without reactiveGetter and reactiveSetter
}
]
As it can be seen, somehow the value of my hashid attribute changes, when assigning the response of the function call.
The next weird behavior happening here, is that the first object where the hashid field changes also gets reactiveGetter and reactiveSetter (but the second object in the array does not get these).
So it looks to me like something is happening with the assignment that I don't know about. Another guess would be that this has something to do with the Vuex store, because I do not change the Vuex tore in the other place where I use the same function.
It is verified that the backend always sends the correct data, as this is dummy data, consisting of an array with two objects with some attributes. So no other data except this two objects is expected.
Can someone explain to me why this behavior occurs?
There are few problems...
Do not use console.log with objects. Browsers tend to show "live view" of object - reference
this.events.findIndex((other) => { return other.hashid = id; }); is wrong, you are using assignment operator (=) instead of identity operator (===). That's why the hashid of the first element changes...

How would I replace the key options inside an object with a spread inside of a map function in react

I have the following code which is changing the value of the options key in the inputType multipleChoice object the main object is called cards and I need to set the state to the newTasksIdsArray
I want the multipleChoice.options to have the newArray values so I can setthe state to them
Update: more info
I guess what I am trying to say is I need the a variable to setlocalstroage eventually to be the entireObject with the optiosn array replaced with whatever would be in newTasksIdsArray which i have generated, just not included because It't probably not neccesary
Thanks!
I need my final result to be
[
{
"inputType":"multipleChoice",
"uniId":"bzR7bpwzjMxcBEdSF",
"label":"Preferred Method of Contact",
"value":"2813348004",
"multipleChoice":{
"options":NEW ARRAY GOES HERE
}
},
...
KEEP REST OF OBJECT
]
let test = [];
cards.map((card) => {
if (card.inputType === "multipleChoice") {
//console.log(card);
card.multipleChoice.options = newTasksIdsArray;
test.push(...cards);
}
});
console.log(test);
setCards(test)
//update localstorage with test as well
This is the cards object
[
{
"inputType":"multipleChoice",
"uniId":"bzR7bpwzjMxcBEdSF",
"label":"Preferred Method of Contact",
"value":"2813348004",
"multipleChoice":{
"options":[
{
"uniId":"gJ8N6sAJrZZvCcPkp",
"label":"Cell Phone",
"checked":false
},
{
"uniId":"Ha9rmssmRkGzpRTn7",
"label":"Email",
"checked":true
}
]
}
},
{
"inputType":"shortText",
"uniId":"AkvioWe6D2ahgDCbW",
"label":"First Name:",
"value":"Kanye",
"multipleChoice":{
}
},
{
"inputType":"phoneNumber",
"uniId":"xwbBBnT2D69QJHHuL",
"label":"Cell Phone Number",
"value":"2813348004",
"multipleChoice":{
}
},
{
"inputType":"email",
"uniId":"62fDs7JtTF4MxMvww",
"label":"Work Email",
"value":"kanye#usa.gov",
"multipleChoice":{
}
},
{
"inputType":"address",
"uniId":"pKAwHmRJKCcKMz8LN",
"label":"Home Address",
"value":"123 White House Avenue",
"multipleChoice":{
}
},
{
"inputType":"dropDown",
"uniId":"K3o689k8G2ZrWEfQc",
"label":"How did you find us?",
"value":"2813348004",
"dropDown":{
"uniId":"3r9gzPXXjidq9p4fw",
"options":[
{
"uniId":"7hGYT4jv89WxFveaj",
"label":"Google"
},
{
"uniId":"J2K2W6P4BR7ZEGEao",
"label":"Referral"
}
]
},
"multipleChoice":{
}
}
]
I guess the cards state was already updated by the reaasignment of the value in the map function so this just works fine to store to localstorage
cards.map((card) => {
if (card.inputType === "multipleChoice") {
card.multipleChoice.options = newTasksIdsArray;
}
});
window.localStorage.setItem("inputs", JSON.stringify(cards)

Destructuring and computed propery name working together, es6

I call a function with this code,
gqlActions('customer', 'Add', this.props, values);
or
gqlActions('customer', 'Update', this.props, values);
this funcions is used for add and update actions.
On the function I use computed property, for example in
const tableAction= `${table}${action}`;
[tableAction]: valuesOptimistic,
It's working ok, my problem is in destructuring before, to use that variable after:
update: (store, { data: { [tableAction] }}) => {
data.customers.push([tableAction]);
it's not valid syntax... , before i've used hardcode for 'Add' action :
update: (store, { data: { customerAdd }}) => {
data.customers.push(customerAdd);
},
or
update: (store, { data: { customerUpdate }}) => {
data.customers.push(customerUpdate);
},
becase I send 'update' property to work for a library that sends me the value accord to [tableAction] that I've defined in:
optimisticResponse: {
[tableAction]: valuesOptimistic,
}
I mean parameter in denormalization is variable (update or add). I hope be clear.
my full function:
export const gqlActions = (table, action, props, values) => {
const valuesOptimistic = {
...Object.assign({}, values, __typename: table'})
};
const tableAction= `${table}${action}`;
props.mutate(
{
variables: values,
optimisticResponse: {
[tableAction]: valuesOptimistic,
},
update: (store, { data: { [tableAction] }}) => {
data.customers.push([tableAction]);
},
},
)
}
}
You need to use destructuring using computed property names
update: (store, { data: { [tableAction]:action }}) => {
data.customers.push(action);
}

Categories

Resources