Saiyan Prince Saiyan Prince - 1 month ago 19
PHP Question

Bind database table values inside a modal vue component form

In my Laravel 5.3 application, I am having a modal component which gets displayed when a user clicks on a button to edit the data.

Loading of modal window is done with the following code:

<a
href="#"
class="btn btn-sm btn-info"
data-toggle="modal"
data-target="#editCountryModal"
data-country-id="{{ countries['id'] }}"
data-country-code="{{ countries['code'] }}"
data-country-name="{{ countries['name'] }}"
data-country-currency-code="{{ countries['currency_code'] }}"
data-country-currency-name="{{ countries['currency_name'] }}"
data-country-display="{{ countries['display'] }}"
>
<i class="fa fa-lg fa-pencil"></i>
</a>


The above code loads the following form which is inside a modal component.

<template>
<div class="errors"></div>

<form method="POST" @submit.prevent="updateCountry">
<div class="form-group">
<label for="name">Id:</label>
<input
type="text"
name="id"
id="countryId"
class="inputText"
autofocus="autofocus"
placeholder="1"
readonly="readonly"
v-model="formData.countryId"
/>
</div>

<div class="form-group">
<label for="name">Name:</label>
<input
type="text"
name="name"
id="name"
class="inputText"
placeholder="Eg. India"
required="required"
v-model="formData.name"
/>
<div class="error">
<span v-if="formErrors['name']"
class="text-danger"
>
{{ formErrors['name'] }}
</span>
</div>
</div>

<div class="form-group">
<label for="code">Code:</label>
<input
type="text"
name="code"
id="code"
class="inputText"
placeholder="Eg. IND"
required="required"
v-model="formData.code"
/>
<div class="error">
<span v-if="formErrors['code']"
class="text-danger"
>
{{ formErrors['code'] }}
</span>
</div>
</div>

<div class="form-group">
<label for="currency_name">Currency Name:</label>
<input
type="text"
name="currency_name"
id="currency_name"
class="inputText"
placeholder="Eg. Indian National Rupee"
required="required"
v-model="formData.currency_name"
/>
<div class="error">
<span v-if="formErrors['currency_name']"
class="text-danger"
>
{{ formErrors['currency_name'] }}
</span>
</div>
</div>

<div class="form-group">
<label for="currency_code">Currency Code:</label>
<input
type="text"
name="currency_code"
id="currency_code"
class="inputText"
placeholder="Eg. INR"
required="required"
v-model="formData.currency_code"
/>
<div class="error">
<span v-if="formErrors['currency_code']"
class="text-danger"
>
{{ formErrors['currency_code'] }}
</span>
</div>
</div>

<div class="form-group">
<label for="display">Display:</label>
<select name="display"
id="display"
class="selectText"
required="required"
v-model="formData.display"
>
<option value="Disabled" selected="selected">Disabled</option>
<option value="Enabled">Enabled</option>
</select>
<div class="error">
<span v-if="formErrors['display']"
class="text-danger"
>
{{ formErrors['display'] }}
</span>
</div>
</div>

<button type="submit" class="button button--teal">Edit</button>
</form>
</template>
<script>
export default {
data() {
return {
formData: {
countryId: '',
name: '',
code: '',
currency_name: '',
currency_code: '',
display: ''
},
formErrors: {},
allCountries: []
}
},

ready: function() {
this.fetchAllCountries();
},

methods: {
notify: function(mType, mTitle, mMessage, nDelay) {
$.iGrowl({
type: mType == 'success' ? 'success' : 'error',
title: mTitle,
message: mMessage,
icon: mType == 'success' ? 'steadysets-checkmark !' : 'feather-cross',
delay: nDelay,
animShow: 'bounceInRight',
animHide: 'bounceOutRight'
});
},

fetchAllCountries: function() {
$.get('/api/all-countries', function(countries) {
this.allCountries = countries;
}.bind(this), 'json');
},

updateCountry: function(e) {
$('.button').addClass('button--disabled')
.html('<i class="fa fa-spinner fa-spin"></i> Editing...');
$(document).find('i.fa.fa-spinner.fa-spin').show();

this.$http
.post('/admin/settings/update-country/' + this.formData.countryId, this.formData)
.then((result) => {
var res = result.data;

$('.button').removeClass('button--disabled').html('Edit');
$(document).find('i.fa.fa-spinner.fa-spin').hide();

// 3000 is the number of seconds before disappearing
this.notify(res.status, res.title, res.message, 3000);
},
(err) => {
$('.button').removeClass('button--disabled').html('Edit');
$(document).find('i.fa.fa-spinner.fa-spin').hide();

if ( err.status === 422 ) {
var errors = err.data;
this.formErrors = errors;

}
});

return false;
}
}
}
</script>


What is happening is, whenever I submit the above form, it gets submitted with no values at all. Meaning I get an empty string for all the fields above. But, if I go to each field manually, it gets submitted with proper field values.

I want that whenever a user just clicks on the submit button, the values should get updated. Meaning the user should not be forced to click on every field, so that vue can recognize it.

EDIT 1:

This is how I call the modal:

@section('pageScripts')
<script>
$('#editCountryModal').on('show.bs.modal', function(e) {
var link = $(e.relatedTarget);

var id = link.data('country-id');
var name = link.data('country-name');
var code = link.data('country-code');
var currency_name = link.data('country-currency-name');
var currency_code = link.data('country-currency-code');
var display = link.data('country-display');

var modal = $(this);
modal.find('.modal-title').html('Edit Country: ' + name);
modal.find('.modal-body #countryId').val(id);
modal.find('.modal-body #name').val(name);
modal.find('.modal-body #code').val(code);
modal.find('.modal-body #currency_name').val(currency_name);
modal.find('.modal-body #currency_code').val(currency_code);
modal.find('.modal-body #display').val(display);
});

</script>
@endsection


Bottom line question: How do I submit the default values (loaded in the modal window) of the form if the user just clicks on the submit button ?

Any help is highly appreciated. Thanks.

Answer

You're using a combination of Jquery and Vue which can cause issues of compatibility as Jquery doesn't have access to the Vue internal object structure. as Sam stated VueStrap would be a good place to look for a refactoring.

The other option would be to store the Vue Root and use $broadcast to send an event to your form which propagates the data to the model instead of setting the dom objects values.

@section('pageScripts')
    <script>
        $('#editCountryModal').on('show.bs.modal', function(e) {
            var object = {
                countryId:      link.data('country-id'),
                name:           link.data('country-name'),
                code:           link.data('country-code'),
                currency_name:  link.data('country-currency-name'),
                currency_code:  link.data('country-currency-code'),
                display:        link.data('country-display')
            };

            window.Vm.$broadcast('fillModal', object);
        });
    </script>
@endsection

where window.Vm is your globally stored root node so your vue initialization would go window.Vm = new Vue({...})

and your component structure would look like this:

<script>
    export default {
        data() {
            return {
                formData: {
                    countryId: '',
                    name: '',
                    code: '',
                    currency_name: '',
                    currency_code: '',
                    display: ''
                },...
            }
        },
        ready: function() {...},
        methods: {...},
        events: {
            "fillModal": function(object){
                this.formData = object;
            }
        }
    }
</script>
Comments