I have some song information that I need to add metadata too. I am looking to combine this using one Observable execution (sorry if my terminology is wrong). But I can't get the metadata in the final map
First Attempt:
songsViewData = combineLatest([
this.songs,
this.genre,
]).pipe(
map(([songs, genre]) => {
let query = {
filename: songs[genre],
song_id_range: 50,
filenames_included: true
}
// This doesn't return the inner object. Just an observable
return this.getSongData(query).subscribe((metaData) => {
return (songs[genre]).map((song) => {
return {
id: song,
songTitle: metaData[song].songTitle,
artistName: metaData[song].artistName
}
})
})
})
)
Second Attempt:
songsViewData = combineLatest([
this.songs,
this.genre,
]).pipe(
switchMap(([songs, genre]) => {
let query = {
filename: songs[genre],
song_id_range: 50,
filenames_included: true
}
return this.getSongData(query) // This gets the metadata
}),
map(([songs, genre, metaData]) => { // I want to access the metadata here
return (songs[genre]).map((song) => {
return {
id: song,
songTitle: metaData[song].songTitle,
artistName: metaData[song].artistName
}
})
})
)
You can use a forkJoin in which you wrap both the switchMap and also the other values you need. This only works if this.getSongData completes. Otherwise, take combineLatest instead.
songsViewData = combineLatest([
this.songs,
this.genre,
]).pipe(
switchMap(([songs, genre]) => {
let query = {
filename: songs[genre],
song_id_range: 50,
filenames_included: true
}
return forkJoin([of([songs, genre]), this.getSongData(query)])
}),
map(([[songs, genre], metaData]) => {
return (songs[genre]).map((song) => {
return {
id: song,
songTitle: metaData[song].songTitle,
artistName: metaData[song].artistName
}
})
})
)
Related
I have a function which takes some time to complete, because of that , I want to turn it into a promise.
My goal is to be able to call the function/promise and after it is fullfilled I can use use a .then() to do whatever I want.
I want getPlantMaterials() to be resolved , after I get the API response.
I'am not sure how to correctly convert this into a promise, though :
getPlantMaterials() {
this.isLoadingPlantMaterials = true;
this.$apollo
.query({
query: gql`
query getMaterialByPotSizeId($plantPotSizeId: ID) {
tenantPlantMaterials(
plantPotSize_Id: $plantPotSizeId
) {
edges {
node {
id
quantity
material {
id
name
materialType {
name
id
}
brand
vendor
size
unit
inventory
id
}
}
}
totalCount
}
}
`,
variables: {
plantPotSizeId: this.selectedPlant.plantPotSize.id,
materialId: null,
quantity: null
}
})
.then(response => {
this.selectedPlantMaterials =
response.data.tenantPlantMaterials.edges;
this.selectedPlantMaterials.map(item => {
item.node.totalAmount =
item.node.quantity * this.totalPlantQuantity;
});
console.log(
"this.selectedPlantMaterials",
this.selectedPlantMaterials
);
})
.finally(() => {
this.isLoadingPlantMaterials = false;
});
},
I am trying to update a preference, example UI attached. The default is yes but a user should have the option to select no. I know I am a little way off but I just need some help identifying where I am going wrong, any help would be really appreciated.
Parent:
<CommunicationPreference
v-for="(communication, index) in communicationPreferenceType"
:key="index + communication.name"
:consent="communication.consent"
:name="communication.name"
#accept-consent="acceptConsent"
#decline-consent="declineConsent"
/>
methods: {
async acceptConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences')
},
async declineConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences')
},
}
CommunicationPreference.vue component:
<Button
:text="Yes"
:type="consent === true ? 'primary' : 'secondary'"
#clicked="acceptConsent"
/>
<Button
:text="No"
:type="consent !== true ? 'primary' : 'secondary'"
#clicked="declineConsent"
/>
methods: {
acceptConsent(consent) {
this.$emit('accept', consent === true)
},
declineConsent(consent) {
this.$emit('decline', consent === false)
},
},
Store:
async updateCommunicationPreferences({ commit, state }) {
const { communicationTypeName } = state.communicationTypeName
if (!communicationTypeName) {
return
}
try {
const response = await this.$axios.put(`/communication-consent/${communicationTypeName}`)
const { data: updatedCommunicationPreferences } = response.data
commit('SET_UPDATED_COMMUNICATION_PREFERENCES', updatedCommunicationPreferences)
} catch (error) {
commit('ADD_ERROR', { id: 'updateCommunicationPreferences', error }, { root: true })
}
},
As mentioned in the comments, the name of the method called is incorrect.
As mentioned by #qimolin, the values related to each option are not being passed to the function that saves it, this can be done by passing a value at calling the action.
methods: {
async acceptConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent: true })
},
async declineConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent: false })
}
or even that simplified with a single method
<CommunicationPreference
v-for="(communication, index) in communicationPreferenceType"
:key="index + communication.name"
:consent="communication.consent"
:name="communication.name"
#accept-consent="acceptConsent(true)"
#decline-consent="declineConsent(false)"
/>
methods: {
async updateConsent(consent) {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent })
}
}
and that parameter must be captured on action
async updateCommunicationPreferences({ commit, state }, payload) {
const { consent } = payload // true or false. This is the value selected by the user.
const { communicationTypeName } = state.communicationTypeName
if (!communicationTypeName) {
return
}
try {
const response = await this.$axios.put(`/communication-consent/${communicationTypeName}`)
const { data: updatedCommunicationPreferences } = response.data
commit('SET_UPDATED_COMMUNICATION_PREFERENCES', updatedCommunicationPreferences)
} catch (error) {
commit('ADD_ERROR', { id: 'updateCommunicationPreferences', error }, { root: true })
}
},
I've read many similar questions and have tried a bunch of code. Unfortunately, I'm not getting my code to run :-(
So, the situation is as follows: In a route of a node.js server, I have to respond with a filtered array of Objects. Unfortunately, whatever I do, I always get an empty array [] back. The filter is a bit tricky in my opinion, as it consists of a string comparison AND an async call to a library function. With the console output, I can clearly see that the correct element is found, but at the same time I see that I've already received the object...
Here is some code that exemplifies my challenge:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
// code from a library. Can't take an influence in it.
async function booleanWhenGood(id) {
if (id in some Object) {
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
// Should return only elements with type 'ofInterest' and that the function booleanWhenGood is true
router.get('/', function(res,req) {
tryOne(testArray).then(tryOneResult =>{
console.log('tryOneResult', tryOneResult);
});
tryTwo(testArray).then(tryTwoResult => {
console.log("tryTwoResult ", tryTwoResult);
});
result = [];
for (const [idx, item] of testArray.entries() ) {
console.log(idx);
if (item.data.someDoc.type === "ofInterest") {
smt.find(item.id).then(element => {
if(element.found) {
result.push(item.id);
console.log("ID is true: ", item.id);
}
});
}
if (idx === testArray.length-1) {
// Always returns []
console.log(result);
res.send(result);
}
}
})
// A helper function I wrote that I use in the things I've tried
async function myComputeBoolean(inputId, inputBoolean) {
let result = await booleanWhenGood(inputId)
if (result.myBoolean) {
console.log("ID is true: ", inputId);
}
return (result.myBoolean && inputBoolean);
}
// A few things I've tried so far:
async function tryOne(myArray) {
let myTmpArray = []
Promise.all(myArray.filter(item => {
console.log("item ", item.id);
myComputeBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
tmpjsdlf.push(item.id);
return true;
}
})
})).then(returnOfPromise => {
// Always returns [];
console.log("returnOfPromise", myTmpArray);
});
// Always returns []
return(myTmpArray);
}
async function tryTwo(myArray) {
let myTmpArray = [];
myArray.forEach(item => {
console.log("item ", item.id);
myCompuBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
myTmpArray.push(item.did);
}
})
});
Promise.all(myTmpArray).then(promiseResult => {
return myTmpArray;
});
}
Asynchronous programming is really tough for me in this situation... Can you help me get it running?
I didn't inspect your attempts that closely, but I believe you are experiencing some race conditions (you print return and print the array before the promises resolve).
However you can alwayd use a regular for loop to filter iterables. Like this:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
async function booleanWhenGood(id) {
if (id in { 'stringId1': 1, 'stringId2': 1 }) { // mock object
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
async function main() {
let filtered = []
for (item of testArray)
if ((await booleanWhenGood(item.id)).myBoolean && item.data.someDoc.type === 'ofInterest')
filtered.push(item)
console.log('filtered :>> ', filtered);
}
main()
I have a series of asynchronous operations in my Backend code . I have doing backend coding in node js .
My code is something like this
db.users
.findAll({
where: condition,
duplicating: false,
attributes: userAttributes,
include: inclusion,
group: ['users.id', 'organizationEntries.id'],
order: [['organizationEntries', 'createdAt', 'DESC']]
})
.then(users => {
let result = users.map(c => {
let output = {
user_id: c.id,
name: c.name,
mobile: c.mobile,
mobile_alternate: c.mobileAlternate,
email: c.email,
gender: c.gender,
dob: c.dob,
image: Utils.getImageUrl(c.image),
entry_id: c.organizationEntries[0].id,
doj: c.organizationEntries[0].fromDate,
status: c.organizationEntries[0].status
};
if (isPlayer && c.dataValues.amount) {
output.due_amount = c.dataValues.amount.toFixed(2);
}
let arenaPromise;
if (!isPlayer) {
output.address_text = addressController.prepareAddressText(c.address);
output.address = addressController.prepareAddressJson(c.address);
if (c.organizationEntries[0].arenaIds)
db.arenas
.findAll({
attributes: ['name'],
where: {
id: {
[Op.in]: c.organizationEntries[0].arenaIds
},
organizationId: req.ORG_ID
}
})
.then(arenas => {
console.log(arenas);
output.arenas = arenas.map(a => {
let arena = {};
arenas.forEach(element => {
arena.name = element.name;
// console.log(arena.name);
});
console.log(arena);
return arena;
});
console.log(output);
// res.send(output);
});
}
return output;
});
res.send(result);
All the things are working fine but in the last then block where I am trying to add arenas in the output object .
.then(arenas => {
console.log(arenas);
output.arenas = arenas[0].name;
output.arenas = arenas.map(a => {
let arena = {};
arenas.forEach(element => {
arena.name = element.name;
// console.log(arena.name);
});
console.log(arena);
return arena;
});
Actually what is happening I am able to fetch arenas data from database
but after fetching it is not assigning the arenas in output object .
I am learning promise chain so anyone can please help me or give me some hint
I'm stuck at a crossroads with a component I am working on.
I have the following component "RecentUpdates"
Within it I am passing props down to a few other components, as you can see from the top of the file.
My problem is when adding a new post, I can not figure out how to get the correct update object array back and i also can not figure out the correct 'Vue way' to update the data prop that is being passed down to the "PostList" component.
<template>
<div>
<PostFilter v-on:selectedCategory="getSelectedPosts" v-on:showAllPosts="showAllPosts" :user="user" :categories="categories"/>
<PostList v-if="recent_posts[0]" :categories="categories" :posts="recent_posts[0]" :user="user"/>
<Pagination v-on:getPreviousPage="getPreviousPage" v-on:getNextPage="getNextPage"/>
</div>
</template>
<script>
import PostList from './PostList';
import PostFilter from './PostFilter';
import Pagination from './Pagination';
import EventBus from '../event-bus';
export default {
name: 'RecentUpdates',
data: () => ({
errors: [],
recent_posts: [],
}),
props: ['categories', 'user'],
components: {
PostList,
PostFilter,
Pagination
},
created() {
if (this.user.meta.selected_categories[0] == 0) {
this.showAllPosts();
}
// do not call here, not working as expected
// is switching selected category to an incorrect one
// this.updateList();
this.getSelectedCategory();
},
watch: {
recent_posts: function(newValue) {
EventBus.$on('addPost', function(newPost) {
console.log(newPost);
this.$forceUpdate();
//this.recent_posts.push(newPost);
//this.$set(this.recent_posts, newPost, newPost);
// this.$nextTick(function () {
// this.recent_posts.push(newPost);
// });
});
console.log(this.recent_posts[0]);
// this.$nextTick(function () {
// console.log(this.recent_posts[0]) // => 'updated'
// });
// if (this.user.meta.selected_categories[0] == 0) {
// EventBus.$on('addPost', this.showAllPosts);
// } else {
// EventBus.$on('addPost', this.getSelectedCategory);
// }
//this.updateList();
}
},
methods: {
// updateList() {
// if (this.user.meta.selected_categories[0] == 0) {
// EventBus.$on('addPost', this.showAllPosts);
// //EventBus.$emit('newPost');
// } else {
// EventBus.$on('addPost', this.getSelectedCategory);
// //EventBus.$emit('newPost');
// }
// },
getSelectedCategory() {
let categoryId = this.user.meta.selected_categories[0];
this.getSelectedPosts(categoryId);
},
showAllPosts() {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]',
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = [];
//this.recent_posts = response.data;
//console.log(response.data);
this.recent_posts.push(response.data);
console.log(this.recent_posts[0]);
})
.catch(e => {
this.errors.push(e);
});
},
getSelectedPosts(categoryId) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&categories=' + categoryId,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = [];
//console.log(response.data);
this.recent_posts.push(response.data);
console.log(this.recent_posts[0]);
})
.catch(e => {
this.errors.push(e);
});
},
/**
* Pagination methods
*
*/
getPreviousPage(page) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&page=' + page,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = response.data;
})
.catch(e => {
this.errors.push(e);
});
},
getNextPage(page) {
axios.get('/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&page=' + page,
{headers: {'X-WP-Nonce': portal.nonce}})
.then(response => {
this.recent_posts = response.data;
})
.catch(e => {
this.errors.push(e);
});
}
},
}
</script>
<style>
</style>
So there are a number of issues I see reading through your code.
You have a recent_posts data property, which is an array. When you make your ajax call to get the posts you push the response which is also an array into the recent_posts array. Why? Why not just set recent_posts = response.data? Then you won't have to be passing recent_posts[0] around.
You're setting up your EventBus handler inside a watcher. This is really unusual. Typically you would set up a handler inside created or mounted.
this inside the EventBus handler likely refers to the EventBus and not your Vue. Ideally, you would set the handler to be a method on the component, which is already bound to the Vue. Something like EventBus.$on("addPost", this.addPost).
Once you've done all that, adding a new post should be as simple as this.recent_posts.push(newPost).
Here is what I might recommend.
export default {
name: 'RecentUpdates',
data(){
return {
errors: [],
recent_posts: []
}
},
props: ['categories', 'user'],
components: {
PostList,
PostFilter,
Pagination
},
created() {
if (this.user.meta.selected_categories[0] == 0) {
this.showAllPosts();
}
this.getSelectedCategory();
EventBus.$on("addPost", this.addPost)
},
beforeDestroy(){
EventBus.$off("addPost", this.addPost)
},
methods: {
getPosts(url){
axios.get(url, {headers: {'X-WP-Nonce': portal.nonce}})
.then(response => this.recent_posts = response.data)
.catch(e => this.errors.push(e))
},
showAllPosts() {
const url = '/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]';
this.getPosts(url);
},
getSelectedPosts(categoryId) {
const url = '/wp-json/wp/v2/posts?_embed=true&status=[publish,resolved,unresolved]&categories=' + categoryId;
this.getPosts(url);
},
addPost(newPost){
this.recent_posts.push(newPost)
},
... //other methods
},
}
Try using kebab-case in your event listeners instead of camelCase:
Example: v-on:selectedCategory="getSelectedPosts" should be v-on:selected-category="getSelectedPosts".
Example: v-on:showAllPosts="showAllPosts" should be v-on:show-all-posts="showAllPosts" or even using the shortcut #show-all-posts="showAllPosts".
UPDATE: If you can provide the code of the other components so we can have a clearer vision of your problem, But you only want to track changes that happens on an object or an array in vue.js you need to deep watch them.
your watcher should be :
watch: {
recent_posts: {
deep: true,
handler: function( oldValue, newValue) {
console.log( "recent_posts has changed" );
// A post has been added, updated or even deleted
}
}
}