phpMick's avatar
Level 15

Making Vue modal reusable.

Hi,

I have created a Vue component for the edit and new function of a Datatable. (Inspired by this: https://adamwathan.me/2016/01/04/composing-reusable-modal-dialogs-with-vuejs/)

What I would like to do now, is to make this reusable for my other Datatables. This means that it would need to have different fields on and slightly different functionality depending on the field type.

Can anyone suggest where to start with this? Has anyone done anything similar?

I am guessing that maybe I could extract the fields HTML and use a slot for this.

I will need the functionality to have a file uploads, selects, dates, text and textarea fields.


<template>

    <div class="modal-mask">
        <div class="modal-wrapper">
            <div class="modal-container">

                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>

                    <h4 class="modal-title"> {{ modalTitle }} </h4>

                </div>

                <form class="form-horizontal" role="form">
                    <input type="hidden" name="_method" value="PATCH"><!--needed to get to store-->


                    <div class="modal-body">

                        <div class="row">
                            <div class="form-group">
                            <label for="report_type_id" class="col-sm-1 control-label">Type</label>
                            <div class="col-sm-4">

                                <select v-model="report_type_id" id = "report_type_id" class="form-control">
                                    <option disabled value="">Please select one</option>
                                    <option v-for="report_type in typeOptions" v-bind:value="report_type.id">
                                        {{ report_type.name }}
                                    </option>
                                </select>

                            </div>

                                <label for="area_id" class="col-sm-1 control-label">Area</label>
                                <div class="col-sm-4">
                                    <select v-model="area_id" id = "area_id" class="form-control">
                                        <option disabled value="">Please select one</option>
                                        <option v-for="area in areaOptions" v-bind:value="area.id">
                                            {{ area.name }}
                                        </option>
                                    </select>
                                </div>
                            </div>
                        </div>


                        <div class="row">
                            <div class="form-group">
                            <div class="col-sm-12">
                                <div class="form-group">
                                    <label for="name" class="col-sm-1 control-label">Name</label>
                                    <div class="col-sm-4">
                                        <input type="text" class="form-control" id="name" v-model="name"  required>
                                    </div>
                                </div>
                            </div>
                            </div>
                        </div>

                        <div v-if="report_type_id == 1">

                        <div class="row">
                                <div class="form-group">
                                    <label for="sql" class="col-sm-1 control-label">SQL</label>
                                    <div class="col-sm-11">

                                        <textarea  class="form-control" rows="5" columns="200" id="sql" v-model="SQL"  required></textarea>

                                    </div>
                                </div>

                        </div>

                        </div>
                        <div v-else>
                            File Upload
                        </div>
                    </div>

                </form>

                <div class="modal-footer">
                    <button type="button" @click="closeForm" class="btn btn-default">Cancel</button>
                    <button type="button" @click="saveData" class="btn btn-primary">Save</button>
                </div>
            </div>
        </div>
    </div>
</template>

<script>

      export default {

         data() {
          return {
            //need one of these for each element
            title:"",
            name:"",
            SQL:"",
            area_id:"",
            report_type_id:"",
            hiddenId:"",
            areaOptions:[],
            typeOptions:[]

            }
        },
        props: ['table','rowId','modalTitle','mode'],

        created: function () {

            this.loadSelects();



        },

  methods: {


        loadSelects: function (event) {

            let self = this;


            axios.get('/reports/selects')
                .then(function (response) {

                    //populate the selects
                    self.areaOptions = response.data.areas;
                    self.typeOptions = response.data.reportTypes;

                    if(self.mode=="edit"){
                        self.loadData();

                    }else{
                        self.clearData();
                    }

                })
                .catch(function (error) {
                    console.log(error);
                });
            },

        clearData: function (event) {

            self.name = "";
            self.SQL = "";
            self.area_id = "";
            self.report_type_id = "";

        },


        loadData: function (event) {

            let self = this;

            axios.get('/' + self.table + '/' +  self.rowId + '/edit')
                .then(function (response) {

                    self.name = response.data.record.name;
                    self.SQL = response.data.record.sql;
                    self.area_id = response.data.record.area_id;
                    self.report_type_id = response.data.record.report_type_id;

                })
                .catch(function (error) {
                    console.log(error);
                });

    },

    closeForm: function (event) {
        this.$emit('close');
    },

    saveData: function (event) {

        if(this.mode=="edit"){
            this.update();
        }else{
            this.store();
        }

      },//saveData


    update: function (event) {
        let self = this;

    //update -         | PUT|PATCH | widgets/{widget}
    axios.patch('/' + self.table + '/' + self.rowId,{

                name: self.name,
                sql:self.SQL,
                area_id:self.area_id,
                report_type_id:self.report_type_id

            })
            .then(function (response) {
                self.closeForm();
                sweetSuccess('Record updated.');

            })
            .catch(function (error) {

                displayFieldErrors(JSON.parse(error.response.request.responseText));

            });

    },//update


        store: function (event) {
        let self = this;

    //POST      /photos                 store   photos.store
    axios.post('/' + self.table,{

                name: self.name,
                sql:self.SQL,
                area_id:self.area_id,
                report_type_id:self.report_type_id

            })
            .then(function (response) {
                self.closeForm();
                sweetSuccess('Record created.');


            })
            .catch(function (error) {
                displayFieldErrors(JSON.parse(error.response.request.responseText));
            });

    }//store

  }//methods

 }//export

</script>


Please note that I am also allowing the users to insert SQL into my database. This may seem slightly insane, but this application will be used by the IT department, to create reports for users. This is a key requirement of this system and it will only be used internally. (I may add some validation, to only allow SELECTS!).

Can anyone think of a better way to do this?:

<div v-if="report_type_id == 1">

The 1 is the id in the table, different report types require different fields.

Thanks,

Mick

0 likes
3 replies
Borisu's avatar

The first thing that pops into mind is to use a prop which says which fields are enabled... This won't be pretty tho, as you will have to make an even larger modal than this. I'll keep thinking about it tho, maybe some other idea comes up.

:fields="{field1: {name: blah, type: select}, field2: {name: blah2, type: input}}"

// on your modal
props: ['fields']
phpMick's avatar
Level 15

Yeah, I was thinking along the same lines.

I'm also aware that I am well on the way to creating a monster. Ideally I would like to split this code somehow.

Borisu's avatar

You can create separate vue components for each input and dropdown etc. This way it won't be that huge and ugly...

Please or to participate in this conversation.