如何创建一个将用户输入发送到另一个组件的 vue 模态

How to create a vue modal that sends user input to another component

我正在尝试创建一个接收用户输入的模态组件,并在保存该信息后显示在另一个组件中。例如,提示用户在模态组件 (Modal.vue) 中分别输入他们的名字和姓氏。一旦用户保存了该数据(模式上的提交方法),数据就会显示在另一个组件上 (InputItem.vue)。

目前,我有一个包含输入元素的 CreateEvent.vue 组件,一个 modal.vue 模态组件,一个 EventItem.vue 组件,它将显示一次输入的内容CreateEvent 被执行,一个 EventsList.vue 组件显示用户创建的所有事件,最后 app.vue 包含模态和事件组件。

我已经能够在没有模态组件的情况下成功地使这个 CRUD 功能正常工作,但是一旦我添加了模态,我就感到困惑了。

如果你能帮助我指引正确的方向,我将不胜感激!

Modal.vue

<template>
  <transition name="modal-fade">
    <div class="modal-backdrop">
      <div
        class="modal"
        role="dialog"
        aria-labelledby="modalTitle"
        aria-describedby="modalDescription"
      >
        <header class="modal-header" id="modalTitle">
          <slot name="header">
            Create an Event
            <button
              type="button"
              class="btn-close"
              @click="close"
              aria-label="Close modal"
            >
              x
            </button>
          </slot>
        </header>
        <section class="modal-body" id="modalDescription">
          <slot name="body">
            <div @keyup.enter="addTodo">
              <input
                type="text"
                class="todo-input"
                placeholder="What needs to be done"
                v-model="newTodo"
              />
              <input
                type="text"
                placeholder="add an emoji?"
                v-model="newEmoji"
              />
            </div>
            <button @click="doneEdit">Create Event</button>
            <button @click="cancelEdit">Cancel</button>
          </slot>
        </section>
        <footer class="modal-footer">
          <slot name="footer">
            I'm the default footer!

            <button
              type="button"
              class="btn-green"
              @click="close"
              aria-label="Close modal"
            >
              Close me!
            </button>
          </slot>
        </footer>
      </div>
    </div>
  </transition>
</template>

<script>
export default {
  name: 'modal',
  data() {
    return {
      newTodo: '',
      newEmoji: '',
      idForTodo: this.todos.length + 1
    }
  },
  methods: {
    close() {
      this.$emit('close')
    },
    addTodo() {
      if (this.newTodo.trim().length == 0) return

      this.todos.push({
        id: this.idForTodo,
        title: this.newTodo,
        emoji: this.newEmoji
      })
      this.newTodo = ''
      this.newEmoji = ''
      this.idForTodo++
    }
  }
}
</script>

CreateEvent.vue

<template>
  <div @keyup.enter="addTodo">
    <input
      type="text"
      class="todo-input"
      placeholder="What needs to be done"
      v-model="newTodo"
    />
    <input type="text" placeholder="add an emoji?" v-model="newEmoji" />
  </div>
</template>

<script>
export default {
  props: {
    todos: {
      type: Array
    }
  },
  data() {
    return {
      newTodo: '',
      newEmoji: '',
      idForTodo: this.todos.length + 1
    }
  },
  methods: {
    addTodo() {
      if (this.newTodo.trim().length == 0) return

      this.todos.push({
        id: this.idForTodo,
        title: this.newTodo,
        emoji: this.newEmoji
      })
      this.newTodo = ''
      this.newEmoji = ''
      this.idForTodo++
    }
  }
}
</script>

EventItem.vue

<template>
  <div class="todo-item">
    <h3 class="todo-item--left">
      <!-- <span v-if="!editing" @click="editTodo" class="todo-item--label">
        {{ title }}
        {{ emoji }}
      </span> -->
      <input
        class="todo-item--edit"
        type="text"
        v-model="title"
        @click="editTitle"
        @blur="doneEdit"
      />
      <input
        class="todo-item--edit"
        type="text"
        v-model="emoji"
        @click="editEmoji"
        @blur="doneEdit"
      />

      <!-- <button @click="doneEdit">Update</button>
      <button @click="cancelEdit">Cancel</button> -->
    </h3>
    <button class="remove-item" @click="removeTodo(todo.id)">✘</button>
  </div>
</template>

<script>
export default {
  name: 'todo-item',
  props: {
    todo: {
      type: Object,
      required: true
    }
  },
  data() {
    return {
      id: this.todo.id,
      title: this.todo.title,
      emoji: this.todo.emoji,
      editing: this.todo.editing,
      beforeEditCacheTitle: this.todo.title,
      beforeEditCacheEmoji: this.todo.emoji
    }
  },
  methods: {
    editTitle() {
      this.beforeEditCacheTitle = this.title
      this.editing = true
    },
    editEmoji() {
      this.beforeEditCacheEmoji = this.emoji
      this.editing = true
    },
    doneEdit() {
      if (this.title.trim() == '') {
        this.title = this.beforeEditCacheTitle
      }
      if (this.emoji.trim() == '') {
        this.emoji = this.beforeEditCacheEmoji
      }

      this.editing = false
      this.$emit('finishedEdit', {
        id: this.id,
        title: this.title,
        emoji: this.emoji,
        editing: this.editing
      })
    },
    cancelEdit() {
      this.title = this.beforeEditCacheTitle
      this.emoji = this.beforeEditCacheEmoji
      this.editing = false
    },
    removeTodo(id) {
      this.$emit('removedTodo', id)
    }
  }
}
</script>

Events.vue

<template>
  <div>
    <transition-group
      name="fade"
      enter-active-class="animated fadeInUp"
      leave-active-class="animated fadeOutDown"
    >
      <EventItem
        v-for="todo in todosFiltered"
        :key="todo.id"
        :todo="todo"
        @removedTodo="removeTodo"
        @finishedEdit="finishedEdit"
      />
    </transition-group>
  </div>
</template>

<script>
import EventItem from '@/components/EventItem'

export default {
  components: {
    EventItem
  },
  data() {
    return {
      filter: 'all',
      todos: [
        {
          id: 1,
          title: 'Eat sushi',
          emoji: '',
          editing: false
        },
        {
          id: 2,
          title: 'Take over world',
          emoji: '‍',
          editing: false
        }
      ]
    }
  },
  computed: {
    todosFiltered() {
      if (this.filter == 'all') {
        return this.todos
      }
    }
  },
  methods: {
    removeTodo(id) {
      const index = this.todos.findIndex(item => item.id == id)
      this.todos.splice(index, 1)
    },
    finishedEdit(data) {
      const index = this.todos.findIndex(item => item.id == data.id)
      this.todos.splice(index, 1, data)
    }
  }
}
</script>

app.vue

<template>
  <div id="app" class="container">
    <button type="button" class="btn" @click="showModal">
      Create Event
    </button>

    <Modal v-show="isModalVisible" @close="closeModal" />
    <Events />
  </div>
</template>

<script>
import Events from './components/Events'
import Modal from './components/Modal'

export default {
  name: 'App',
  components: {
    Events,
    Modal
  },
  data() {
    return {
      isModalVisible: false
    }
  },
  methods: {
    showModal() {
      this.isModalVisible = true
    },
    closeModal() {
      this.isModalVisible = false
    }
  }
}
</script>

模态组件应该发出值而不是将其推入 todos 数组。当它发射它时,父组件 (App.vue) 监听发射的项目。

我会做这样的事情

Modal.vue

<template>
   ...
// header
<section class="modal-body" id="modalDescription">
  <slot name="body">
    <div @keyup.enter="addTodo">
      ...
    </div>
    <button @click="handleModalSubmit">Create Event</button>
  ...
  //footer
  ...
</template>

<script>
export default {
  ...
  data() {
    ...
  },
  methods: {
    ...,
    handleModalSubmit() {
      this.$emit('todos-have-been-submitted', this.todos);
    },
    addTodo() {
      ...
      this.todos.push({
        id: this.idForTodo,
        title: this.newTodo,
        emoji: this.newEmoji
      })
      ...
    }
  }
}
</script>

App.vue

<template>
  ...
  <Modal
    @todos-have-been-submitted="handleTodoSubmission" //watch the 'todos-have-been-submitted" emission and trigger handleTodoSubmission method when the emission is detected
  />
  <Events 
    :todos="todos" // pass todos as a prop to the Events component
  />
  ...
</template>

<script>
import Events from './components/Events'
import Modal from './components/Modal'

export default {
  name: 'App',
  components: {
    Events,
    Modal
  },
  data() {
    return {
    ...,
      todos: []
    }
  },
  methods: {
    ...,
    handleTodoSubmission(todos) {
      this.todos = [...todos];
    }
  }
}
</script>