How to put array in multiple select in Vue.JS - vue.js

Just few hours im tryin to put array of object in multiple select
I got this array from axios request and next im need display whole array with all title objects in this array
If someone have info how to solv this probled it will be nice and im gratefull
this is my array
products: [{id: 6, category_id: 2, title: "Тест", brand: "Тест", serial_number: "2165412315864",…},…]
0: {id: 6, category_id: 2, title: "Test", brand: "Test", serial_number: "2165412315864",…}
1: {id: 7, category_id: 3, title: "Test2", brand: "Test2", serial_number: "2165412315864",…}
2: {id: 8, category_id: 5, title: "New", brand: "New", serial_number: "2165412315864",…}
this is my code from Vuex Store
async getOrders(ctx, data)
{
return new Promise((resolve, reject) => {
axios({
url: '/orders',
data: data,
method: 'GET'
})
.then((resp) => {
ctx.commit('setOrders', resp.data.orders)
ctx.commit('setUsers', resp.data.users)
ctx.commit('setProducts', resp.data.products)
ctx.commit('updatePagination', resp.data.pagination)
resolve(resp)
})
.catch((error) => {
console.log(error)
reject(error)
})
})
},

ASYNC/AWAIT FIX:
async getOrders(ctx, data) {
try {
const { data } = await axios({
url: '/orders',
data: data,
method: 'GET'
})
ctx.commit('setOrders', data.orders)
ctx.commit('setUsers', data.users)
ctx.commit('setProducts', data.products)
ctx.commit('updatePagination', data.pagination)
} catch (e) {
console.error(e)
}
}
IN COMPONENT:
<select v-model="product">
<option v-for="product in products" :key="product.code" :value="product">
{{ product.title }}
</option>
</select>
When you do this way, the whole selected product would be saved in the product data property.

Related

Vuejs v-for not working with my data, is my data not formatted correctly?

Not sure why the data is showing up as undefined. Is the data not formatted correctly? I've tried several ways to adjust it and nest it differently but nothing is working out so far.
<template>
<main>
<AboutMeComponent v-for="i in about" :key="i.posts.id" :title="i.posts.title" :body="i.posts.body" :skills="i.posts.skills"
:img="i.images.img" />
</main>
</template>
<script>
import AboutMeComponent from '../components/AboutMeComponent.vue';
export default {
data() {
return {
about: {
posts: [
{ id: 1, title: "About Me", body: `Body 1` },
{ id: 2, title: "Skills" },
{ id: 3, title: "Hobbies", body: "Body 3" },
],
images: [
{ img: 'figma.png'},
{ img: 'figma.png'},
{ img: 'figma.png'},
]
}
}
},
components: {
AboutMeComponent
}
}
</script>
I think you are need to set the v-for loop on the about.posts, not on the about. Also, if the img is used for the post, I would suggest you keep them with the post, instead of separated.
So your data will be:
data() {
return {
about: {
posts: [
{ id: 1, title: 'About Me', body: 'Body 1', img: 'figma.png' },
{ id: 2, title: 'Skills', body: 'Body 2', img: 'figma.png' },
{ id: 3, title: 'Hobbies', body: 'Body 3', img: 'figma.png' },
],
}
}
},
And now you can loop over about.posts:
<AboutMeComponent v-for="post in about.posts" :key="post.id" :title="post.title" :body="post.body" :img="post.img" />
I left skills out, as they are not in your data.
Just a small suggestion: using variable names as i makes your code harder to understand. If you had used about in about, you would have felt that this was not what you wanted, because you wanted something to do with a post in posts.
Hope this helps.
Few Observations :
As each img in the images array corresponds to each object in the posts array. Merge that into a single one and then use v-for loop for about.posts.
As few properties are missing. Use Optional chaining (?.) operator so that you can read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.
Live Demo :
Vue.component('aboutmecomponent', {
props: ['key', 'title', 'body', 'skills', 'img'],
template: '<h3>{{ title }}</h3>'
});
var app = new Vue({
el: '#app',
data: {
about: {
posts: [
{ id: 1, title: "About Me", body: "Body 1" },
{ id: 2, title: "Skills" },
{ id: 3, title: "Hobbies", body: "Body 3" },
],
images: [
{ img: 'figma.png'},
{ img: 'figma.png'},
{ img: 'figma.png'},
]
}
},
mounted() {
this.about.posts = this.about.posts.map((obj, index) => {
const newObj = {
...obj,
...this.about.images[index] }
return newObj;
});
delete this.about.images;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<AboutMeComponent
v-for="item in about.posts"
:key="item?.id"
:title="item?.title"
:body="item?.body"
:skills="item?.skills"
:img="item?.img" >
</AboutMeComponent>
</div>

Using a method as data inside the app parent

So I'm still learning Vue.js and I got my list working well and I have one question. I will explain what I'm trying to do below as best as possible and I wanted to see if someone could help me with my issue.
So here is the component that I have on the HTML side:
<favorites-edit-component
v-for="(favorite, index) in favorites"
v-bind:index="index"
v-bind:name="favorite.name"
v-bind:key="favorite.id"
v-on:remove="favorites.splice(index, 1)"
></favorites-edit-component>
Here is the vue.js portion that I have:
Vue.component('favorites-edit-component', {
template: `
<div class="column is-half">
<button class="button is-fullwidth is-danger is-outlined mb-0">
<span>{{ name }}</span>
<span class="icon is-small favorite-delete" v-on:click="$emit('remove')">
<i class="fas fa-times"></i>
</span>
</button>
</div>
`,
props: ['name'],
});
new Vue({
el: '#favorites-modal-edit',
data: {
new_favorite_input: '',
favorites: [
{
id: 1,
name: 'Horse',
url: 'www.example.com',
},
{
id: 2,
name: 'Sheep',
url: 'www.example2.com',
},
{
id: 3,
name: 'Octopus',
url: 'www.example2.com',
},
{
id: 4,
name: 'Deer',
url: 'www.example2.com',
},
{
id: 5,
name: 'Hamster',
url: 'www.example2.com',
},
],
next_favorite_id: 6,
},
methods: {
add_new_favorite: function() {
this.favorites.push({
id: this.next_favorite_id++,
name: this.new_favorite_input
})
this.new_favorite_input = ''
},
get_favorite_menu_items: function() {
wp.api.loadPromise.done(function () {
const menus = wp.api.collections.Posts.extend({
url: wpApiSettings.root + 'menus/v1/locations/favorites_launcher',
})
const Menus = new menus();
Menus.fetch().then(posts => {
console.log(posts.items);
return posts.items;
});
})
}
}
});
So as you can see, I have the data: { favorites: [{}] } called inside the vue app and I get this console.log:
Now I built a method called get_favorite_menu_item and this is the return posts.items output inside console.log:
Problem: I don't want to have a manual array of items, I want to be able to pull in the method output and structure that - How would I take a approach on pulling the items?
Could I call something like this:
favorites: this.get_favorite_menu_items?
Here is a JFiddle with all the items: https://jsfiddle.net/5opygkxw/
All help will be appreciated on how to pull in the data.
First I will init favorites to empty array.
then on get_favorite_menu_items() after I will init data from post.item to favorites.
on created() hooks i will call get_favorite_menu_items() to fetch the data when the view is created.
new Vue({
el: '#favorites-modal-edit',
data: {
new_favorite_input: '',
favorites: [],
next_favorite_id: 6,
},
methods: {
add_new_favorite: function() {
this.favorites.push({
id: this.next_favorite_id++,
name: this.new_favorite_input
})
this.new_favorite_input = ''
},
get_favorite_menu_items: function() {
wp.api.loadPromise.done(function () {
const menus = wp.api.collections.Posts.extend({
url: wpApiSettings.root + 'menus/v1/locations/favorites_launcher',
})
const Menus = new menus();
Menus.fetch().then(posts => {
console.log(posts.items);
// need map key also
this.favorites = posts.items;
});
})
}
},
created () {
// fetch the data when the view is created
this.get_favorite_menu_items();
},
});

How to proccessing Vuex array in Vue.js

I'm a new web developer. I need help getting all of the titles from all of the objects in an array. I get an array of products from an axios request. In the next step I need to process this array in a Vue component and record a single value to data. Next I need to display this data value in a multiselect's options.
Here is the axios code:
async getOrders(ctx, data)
{
return new Promise((resolve, reject) => {
axios({
url: '/orders',
data: data,
method: 'GET'
})
.then((resp) => {
ctx.commit('setOrders', resp.data.orders)
ctx.commit('setUsers', resp.data.users)
ctx.commit('setProducts', resp.data.products)
ctx.commit('updatePagination', resp.data.pagination)
resolve(resp)
})
.catch((error) => {
console.log(error)
reject(error)
})
})
},
This is my array of products recorded in Vuex store
0: {id: 6, category_id: 2, title: "Test", brand: "Тест", serial_number: "2165412315864",…}
1: {id: 7, category_id: 3, title: "Климат", brand: "Климат", serial_number: "2165412315864",…}
2: {id: 8, category_id: 5, title: "New", brand: "New", serial_number: "2165412315864",…}
This is my code to proccessing this array
computed:{
...mapGetters('order', ['users', 'products', 'orders']),
},
methods:{
getProducts(products)
{
const arr = products.map(c => c.title)
console.log('titles: ', arr); //Debug
this.options = arr
}
},
And here is the code for the multiselect
<multiselect v-model="formData.product" :options="options" :value="values" :multiple="true" :close-on-select="false" :clear-on-select="false" :preserve-search="true" placeholder="Pick some" label="name" track-by="name" :preselect-first="true">
<template slot="selection" slot-scope="{ values, search, isOpen }"><span class="multiselect__single" v-if="values.length && !isOpen">{{ values.length }} options selected</span></template>
</multiselect>
mounted() {
this.getProducts();
},
It looks like you're not sending your args by calling just this.getProducts(). You don't need to, just write this. in front of the computed value inside the method.
methods: {
getProducts () {
const arr = this.products.map(c => c.title)
this.options = arr
}
}

Vue - Deep watch change of array of objects, either if a object is added of modified

I'm trying to create an element in vue.js, so that when I update my cart it will show a warning with the item added/updated to cart. So if I add a new car, it would show that last car added.
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
to
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3},
{ name: 'Mustang', quantity: 1}
]
will show
<div>
You have 1 x Mustang in Cart
</div>
But if I update the quantity of a car that was already in the cart, it will show that last car updated.
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
to
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 4}
]
will show
<div>
You have 4 x Toyota in Cart
</div>
So far I made it work based in this answer
new Vue({
el: '#app',
data: {
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
}
});
Vue.component('car-component', {
props: ["car"],
data: function() {
return {
lastAdded:''
}
},
template: `
<div>
You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
</div>`,
watch: {
car: {
handler: function(newValue) {
this.lastAdded = newValue;
},
deep: true
}
}
});
html
<script src="https://unpkg.com/vue#2.5.17/dist/vue.js"></script>
<body>
<div id="app">
<p>Added to Cart:</p>
<car-component :car="car" v-for="car in cars"></car-component>
</div>
</body>
The point is that now it just detects when a object is already in the cart and changes quantity, but not when there is a new car added. I tried to play with another watcher, but it didn't work. Thanks in advance!
hmm how would I do this?
seems to me we have an array of objects and we are tracking the most recently added or modified object. Sure.
So, I think I'd want to only track the recently modified object and render that.
first the html:
<div id="app">
<p>Added to Cart:</p>
<car-component :car="latestCar"></car-component>
</div>
and the vue instance:
new Vue({
el: '#app',
data: {
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
],
latestCar: {}
},
methods: {
updateLatestCar(car) {
this.latestCar = car;
//call this method from any other method where updates take place
//so if would be called from your addCar method and your updateCar method
//(which I assume exist even though they are not shown in your code)
}
}
});
Vue.component('car-component', {
props: ["car"],
data: function() {
return {
lastAdded:''
}
},
template: `
<div>
You have {{lastAdded.quantity}} x {{lastAdded.name}} in Cart
</div>`,
watch: {
car: {
handler: function(newValue) {
this.lastAdded = newValue;
},
deep: true
}
}
});
If you are modifying your array of objects via some method that is external to the Vue instance then that will require some additional thought.
But it seems like for this you'd have some methods in the Vue instance methods block like this:
addCar(car) {
this.cars.push(car);
this.updateLatestCar(car);
},
updateCar(index, car) {
this.cars[index] = car;
this.updateLatestCar(car);
}
You could pass the entire cars[] array to <car-component>, and allow the component to determine which element of cars[] to display a message about:
In car-component, add a prop (typed for safety) to hold the passed-in cars[]:
Vue.component('car-component', {
// ...
props: {
cars: Array
},
}
Add two data properties:
* `car` - the current car.
* `copyOfCars` - the last known copy of `cars[]`, used to determine which array element has changed. *Note: While watchers are provided both the old and new values of the watched property, the old value does not actually indicate the previous value for arrays of objects.*
Vue.component('car-component', {
//...
data() {
return {
car: {},
copyOfCars: undefined, // `undefined` because we don't need it to be reactive
};
},
}
Define a method (e.g., named findActiveCar) that determines which element in a given cars[] is most recently "active" (newly added or modified).
Vue.component('car-component', {
// ...
methods: {
/**
* Gets the newest/modified car from the given cars
*/
findActiveCar(newCars) {
if (!newCars || newCars.length === 0) return {};
let oldCars = this.copyOfCars;
// Assume the last item of `newCars` is the most recently active
let car = newCars[newCars.length - 1];
// Search `newCars` for a car that doesn't match its last copy in `oldCars`
if (oldCars) {
for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
if (newCars[i].name !== oldCars[i].name
|| newCars[i].quantity !== oldCars[i].quantity) {
car = newCars[i];
break;
}
}
}
this.copyOfCars = JSON.parse(JSON.stringify(newCars));
return car;
}
}
}
Define a watcher on the cars property that sets car to the new/modified item from findActiveCar().
Vue.component('car-component', {
// ...
watch: {
cars: {
handler(newCars) {
this.car = this.findActiveCar(newCars);
},
deep: true, // watch subproperties of array elements
immediate: true, // run watcher immediately on this.cars[]
}
},
}
Vue.component('car-component', {
props: {
cars: Array,
},
data() {
return {
car: {},
copyOfCars: undefined,
}
},
template: `<div>You have {{car.quantity}} x {{car.name}} in Cart</div>`,
watch: {
cars: {
handler(newCars) {
this.car = this.findActiveCar(newCars);
},
deep: true,
immediate: true,
}
},
methods: {
findActiveCar(newCars) {
if (!newCars || newCars.length === 0) return {};
let oldCars = this.copyOfCars;
let car = newCars[newCars.length - 1];
if (oldCars) {
for (let i = 0; i < Math.min(newCars.length, oldCars.length); i++) {
if (newCars[i].name !== oldCars[i].name
|| newCars[i].quantity !== oldCars[i].quantity) {
car = newCars[i];
break;
}
}
}
this.copyOfCars = JSON.parse(JSON.stringify(newCars));
return car;
}
}
});
new Vue({
el: '#app',
data: () => ({
cars: [
{ name: 'Porsche', quantity: 2},
{ name: 'Ferrari', quantity: 1},
{ name: 'Toyota', quantity: 3}
]
}),
methods: {
addCar() {
this.cars.push({
name: 'Mustang', quantity: 1
})
}
}
})
<script src="https://unpkg.com/vue#2.5.17"></script>
<div id="app">
<h1>Added to Cart</h1>
<button #click="addCar">Add car</button>
<ul>
<li v-for="(car, index) in cars" :key="car.name + index">
<span>{{car.name}} ({{car.quantity}})</span>
<button #click="car.quantity++">+</button>
</li>
</ul>
<car-component :cars="cars" />
</div>

populate mongoose key as part of object

EDIT
minimal reproduction repo
It's easier to explain in code than English.
The following code works, but it feels like there's gotta be an easier, more MongoDBy/mongoosy way ...
// recipeModel.js, relevant part of the schema
equipments: [{
_id: {
type: Schema.Types.ObjectId,
ref: 'equipments',
},
quantity: {
type: Number,
required: true,
},
}],
// recipeController.js
const equipmentsWorkaround = recipe => Object.assign({}, recipe.toObject(), {
equipments: recipe.toObject().equipments.map(equip => ({
quantity: equip.quantity,
name: equip._id.name,
_id: equip._id._id,
})),
})
const getItem = (req, res, next) => {
Recipes.findById(req.params.id)
.populate('equipments._id')
.then(equipmentsWorkaround) // <--- ugh ...
.then(recipe => res.json(recipe))
.catch(next)
}
I know how to do a "conventional" ref in mongoose, but is what I'm after here even possible in mongo?
desired outcome:
equipments: [
{
quantity: 1,
name: "Pan",
_id: 'some mongo object here...'
},
{
quantity: 3,
name: "Big Knife",
_id: 'some mongo object here...'
}
]