如何创建一个将用户输入发送到另一个组件的 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>
我正在尝试创建一个接收用户输入的模态组件,并在保存该信息后显示在另一个组件中。例如,提示用户在模态组件 (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>