How to create a simple modal for create & edit functions in vue.js?

Viewed 5137

I'm trying to create a model to be able to edit existing data? How would I go about doing that?

I'm using a <el-form> to create a new entry in this case I'm creating questions, I want to reuse this for the edit and have the data from the entry added into the form.

This is my form right now

<el-dialog title="Nuevo" :loading="loading" :visible="dialogFormVisible" :visible.sync="dialogFormVisible">

    <el-form label-position="top" ref="form" :model="form" :rules="rules">
        <el-row>
            <el-form-item label="Pregunta" prop="question">
                <el-input v-model="form.question"></el-input>
            </el-form-item>
        </el-row>
        <el-row>
            <el-col :span="8">
                <el-form-item label="Seccion" prop="survey_section_id">
                    <el-select v-model="form.survey_section_id" placeholder="Selecionar">
                        <el-option v-for="section in survey_section" :key="section.id" :label="section.title"
                            :value="section.id">
                        </el-option>
                    </el-select>
                </el-form-item>
            </el-col>
            <el-col :span="8">
                <el-form-item label="Tipo de Respuesta" prop="response_type">
                    <el-select v-model="form.response_type_id" placeholder="Selecionar">
                        <el-option v-for="type in response_type" :key="type.id" :label="type.type" :value="type.id">
                        </el-option>
                    </el-select>
                </el-form-item>
            </el-col>
            <el-col :span="4">
                <el-form-item label="Opcional" prop="optional">
                    <el-switch v-model="form.optional"></el-switch>
                </el-form-item>
            </el-col>
        </el-row>
    </el-form>
    <span slot="footer">
        <el-button type="info" @click="dialogFormVisible = false">Cancelar
        </el-button>
        <el-button type="primary" :loading="loading" @click="submit('form')">Guardar
        </el-button>
    </span>
</el-dialog>

What do I need to do to turn this into a modal and use it for my editing as well?

Ask me anything, I can provide any code that I have that is needed. Thanks

1 Answers

This is a common problem. Of course, a lot of these things come down to personal preference or opinion, but here are a few things to consider:

  1. Usually when solving this problem, you display all of your existing models in some way (ie. a list), with an "edit" button. When I am doing this, I use an object that maps model IDs to their corresponding model objects as the underlying data model for this list. This makes it easy to replace a model with an updated version (models[model.id] = model).

  2. Binding the model to the form directly (eg. :model="the model you want to edit") is usually a bad implementation. This is because any changes that are made during editing would be immediately written to the model. The problem with that is, if your save() function fails, then you have to change everything back. A better implementation is to clone the model, and bind that object to the form.

  3. You can use a computed property for the modal's title/header. I usually have a data property called mode, which will be either "Create" or "Edit", and then I have a computed property that returns mode + ' Model', where 'Model' is the name of the model – in your case, "Pregunta".

  4. Since the create and update functions are usually use different API endpoints (and HTTP methods), your "Save / Update" button needs to call the right one. Again, I use the mode property to handle this (eg. <button @click="mode == 'edit' ? update : save">)


The following code should give you a good starting point for having a modal you can use for both creating & editing models, as well as the basic structure for most CRUD components.

<template>
  <div>
    <template v-for="model in modelsArray">
      ... <!-- Display your existing models however you want -->
      <a href="" @click.prevent="edit(model)">Edit</a>
    </template>
    <button @click="create">Create New Model</button>
    <el-dialog
      :title="modalTitle"
      :loading="loading"
      :visible.sync="dialogFormVisible"
    >
      <el-form :model="formModel">
        ...
      </el-form>
      <span slot="footer">
        <el-button type="info" @click="cancel">Cancelar</el-button>
        <el-button
          type="primary"
          :loading="loading"
          @click="mode == 'edit' ? update : save"
        >
          {{ mode }}
        </el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  data() {
    mode: '',
    dialogFormVisible: false,
    loading: false,
    formModel: {},
    models: {            // an object mapping your existing model IDs
      1: { id: 1, ...},  // to their corresponding model objects
      2: { id: 2, ...},
      3: { id: 3, ...},
      ...
    }
  },
  ...,
  computed: {
    modelsArray() {
      return Object.values(this.models);
    },
    modalTitle() {
      return `${ this.mode } Model`;
    }
  },
  methods: {
    create() {
      this.mode = 'Create';
      this.dialogFormVisible = true;
    },
    edit(model) {
      this.mode = 'Edit';     
      this.formModel = _.cloneDeep(model); // See note below
      this.dialogFormVisible = true;
    },
    save() {
      // POST the new model (this.formModel) to your server using your ajax library of choice     
      // after the ajax call returns, do the following:
      this.models.push(the newly created model); // usually returned from the server as part of this ajax call
      this.reset();
    },
    update() {
      // PUT the new model (this.formModel) to your server using your ajax library of choice
      // After the ajax call returns, replace the updated model in the `models` object, and reset the form:
      this.models[this.formModel.id] = _.cloneDeep(this.formModel);
      this.reset();
    },
    reset() {
      this.dialogFormVisible = false;
      this.loading = false;
      this.formModel = {
        ... // put default form values here
      };
    }
  },
  mounted() {
    this.reset();
  }
}
</script

NOTE: _.cloneDeep is from lodash. If you're not using lodash, you can use one of these methods to clone the model.

Related