shentao / vue-multiselect

Universal select/multiselect/tagging component for Vue.js
https://vue-multiselect.js.org/
MIT License
6.73k stars 992 forks source link

multiple instances of vue-multiselect #333

Closed omdesignz closed 7 years ago

omdesignz commented 7 years ago

Hi there guys,

I'm using vue-multiselect within an invoice component and whenever I add a new row to the invoice it applies the same multiselect as the one above it. Whenever I change one selection, they all get updated to the same selected value.

How do I ensure that the selected values only apply to a given row?

shentao commented 7 years ago

Hey! Could you provide a reproduction link? You can use this fiddle: https://jsfiddle.net/shentao/c4L3gs91/ It would make much easier to solve if we could see your markup and code responsible for handling multiselect.

There shouldn’t be any problems in general if you’re using v-model. In case you’re using @input please make sure to use the second argument the event receives which is the id prop. That should make it easier to handle multiple multiselect components with the same event handler. See docs for the API description!

omdesignz commented 7 years ago

Unfortunately I'm using vue-multiselect within an invoice component.

document

My invoice.vue file looks as follows:

<template>
    <div class="block-header">
        <form class="" @submit.prevent="save">
            <center><h3 class="block-title">Facturação <small>Emite uma factura para cada Processo Iniciado</small></h3></center><br>
            <table class="table">
                <thead>
                <tr>
                    <th class="text-center" style="width: 50px;"></th>
                    <th>Descrição</th>
                    <th class="text-center" style="width: 100px;">Quantidade</th>
                    <th class="text-right" style="width: 120px;">Valor/Unidade</th>
                    <th class="text-right" style="width: 120px;">Valor</th>
                    <th style="text-align: center; width: 50px;"><input type="button" class="btn btn-primary" @click.stop="addLine" value="+"></th>
                </tr>
                </thead>
                <tbody class="body">
                <tr class="" v-for="(item, index) in form.items">
                    <td class="text-center no"></td>
                    <td>
                        <div class="col-sm-5">
                            <div class="form-material floating">
                                <multiselect v-model="selectedProducts" :options="productsArray" @update="updateSelected()" @search-change="" :limit="3"></multiselect>
                            </div>
                        </div>
                    </td>

                    <td class="text-center">
                        <div class="form-material floating">
                            <input type="text" name="qty[]" v-model="item.qty" class="form-control">
                        </div>
                    </td>

                    <td class="text-right">
                        <div class="form-material floating">
                            <input type="text" name="unit_price[]" v-model="item.unit_price" class="form-control">
                        </div>
                    </td>

                    <td class="text-right">00</td>

                    <td style="text-align: center; width: 50px;">
                        <div class="form-material floating">
                            <a href="#" style="text-align: center;" @click="removeLine(index)"><i style="color:red;" class="fa fa-trash"></i></a>
                        </div>
                    </td>
                </tr>

                <tr>
                    <td colspan="4" class="font-w600 text-right">Subtotal</td>
                    <td class="text-right">
                        {{subTotal}}
                    </td>
                </tr>

                <tr>
                    <td colspan="4" class="font-w600 text-right">Desconto</td>
                    <td class="text-right">
                        <div class="form-material floating">
                            <input name="discount" type="text" v-model="form.discount" class="form-control">

                        </div>
                    </td>
                </tr>

                <tr class="active">
                    <td colspan="4" class="font-w700 text-uppercase text-right">Valor Total</td>
                    <td class="font-w700 text-right">
                        {{total}}
                    </td>
                </tr>

                </tbody>
            </table>
        </form>
    </div>
    <!--<div>-->
    <!--<multiselect v-model="selectedProducts" :options="productsArray" @update="updateSelected()" @search-change="" :limit="3"></multiselect>-->
    <!--</div>-->
</template>

<script>
    import Vue from 'vue'
    import Multiselect from 'vue-multiselect'
    import axios from 'axios'

    export default {
        name: 'InvoiceForm',
        components: { Multiselect },
        data () {
            return {
                counter: 0,
                form: {
                    items: [],
                    discount: 0,
                },
                option: {
                    customers: []
                },
                errors: {},
                products: [],
                selectedProducts: [],
                isLoading: false
            }
        },
        methods: {
            fetchProducts () {
                this.isLoading = true;
                axios.get('http://adv.dev/api/users').then(response => {
                    this.products = response.data;
                    this.isLoading = false
                })
            },

            updateSelected(newSelected) {
                this.selectedProducts = newSelected;
            },

            addLine() {
                this.form.items.push({
                    description: '',
                    unit_price: 0,
                    qty: 1,
                });
            },
            removeLine(index) {
                this.form.items.splice(index, 1);
            },

            save() {
                let vm = this;
                axios.post('http://adv.dev/api/users', this.form)
                    .then(function(response) {
                        if(response.data.saved) {
                            console.log('saved')
                        }
                    })
                    .catch(function(error) {
                        Vue.set(vm.$data, 'errors', error.response.data)
                    })
            }

        },
        created(){
            this.fetchProducts();
        },
        computed: {
            productsArray(){
                const _ = require ('lodash');
                return _.map(this.products, function(num, key){ return num.name} )
            },
            subTotal() {
                return this.form.items.reduce(function(carry, item) {
                    return carry + parseFloat(item.qty) * parseFloat(item.unit_price)
                }, 0)
            },
            total() {
                return this.subTotal - parseFloat(this.form.discount)
            }
        }
    }
</script>

my app.js file looks as follows:

require ('./bootstrap.js');
import invoice from './components/invoice.vue'

const app = new Vue({
    el: '#app',
    components: { invoice },
    data: function() {
        return {

        }
    },
    methods: {
        done: function(data) {
            console.log(data);
        }
    }
});

and finally my html file looks as follows:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <title>Document</title>
</head>
<body>
<div id="app">
        <invoice></invoice>
    </div>

</div>
<script src={!! asset('js/app.js') !!}></script>
</body>
</html>

I also couldn't get it to show results after typing into a select input field.

fernandoherlo commented 7 years ago

Link: https://jsfiddle.net/c4L3gs91/123/

OliverGrimsley commented 7 years ago

In the data property, try using either mustache brackets (not sure if that works), or an array of objects (how I do it) like this:

  data () {
   return {
      ...
      selectedProducts: {},
      ...

or

selectedProducts: [
   {
     id: ''
   }
],

So in the backend, I then have 'selectedProducts..id' where the is each row.

Don't have time to try it with just brackets, but I know my way works (I make extensive use of this plugin on forms that have multiple rows of inputs that are generated dynamically using v-model with the value defined in the loop, just as you are doing, but all of my data objects use dot notation and so each row has the data like 'addresses' and then I have id, city, state etc as subparts, so in the v-for loop it iterates over each one).

Example: HTML:

<div class="row" v-for="(newaddress,index) in addresses">
    <div class="two wide column">
        <div class="ui mini form">
            <div class="field">
                <label>Address 1</label>
                    <input type="text" v-model="newaddress.address" @keydown.enter.prevent="">
            </div>
        </div>
    </div>
... [other data fields]
    <div class="three wide column">
        <div class="field">
            <label>Type</label>
                <multiselect
                    v-model="newaddress.addresstype_id"
                    :options="atypes"
                    :taggable="true"
                    tag-placeholder="Add type"
                    placeholder="Type"
                    label="label"
                    track-by="label"
                ></multiselect>
        </div>
    </div>

and my data looks like this:

addresses: [
           {
            addresstype_id: '',
            address: '',
        address2: '',
        city: '',
        state: '',
        zip: ''
           }
],
shentao commented 7 years ago

Closing due to no activity.

Klodovsky commented 3 years ago

``