英文:
How to set Vue child component object from parent
问题
以下是您要翻译的内容:
"I'm fairly new to Vue and am working with the Vue 3 composition api. I'm also using Vuetify, so the dialog is code copied from their example. Hopefully I haven't made it more complicated by mixing that into my learning. I have a main list of users populated currently by a javascript array. The idea is a fairly common set up where it lists each user in the array and the user's link opens a dialog to edit the user info. There's also an Add User link to open the dialog to add a user.
In my code, so far, I can get the dialog to open when I click a name. I can't get it to open when clicking the Add User link. They both call the same method, but the Add User link simply passes null for the value of user. When clicking the link for a user, although the dialog opens, it doesn't set the property of the user to populate the form. So, I've got a couple of issues. How to open the dialog from the Add User link, and how to populate the user from the user link. The basic code is below.
store.js
import { reactive } from 'vue'
class Store{
constructor() {
this.state = reactive({
users: [
{ id: 1, firstName: "John", lastName: "Smithers" },
{ id: 2, firstName: "Mary", lastName: "Smithers"}
],
currentUserId: null
})
}
addUser(){
this.state.currentUserId = null;
}
}
export const store = new Store()
UserList.vue
Users | |
---|---|
{{ user.firstName }} {{ user.lastName }} | |
Add User |
UserForm.vue
"
英文:
I'm fairly new to Vue and am working with the Vue 3 composition api. I'm also using Vuetify, so the dialog is code copied from their example. Hopefully I haven't made it more complicated by mixing that into my learning. I have a main list of users populated currently by a javascript array. The idea is a fairly common set up where it lists each user in the array and the user's link opens a dialog to edit the user info. There's also an Add User link to open the dialog to add a user.
In my code, so far, I can get the dialog to open when I click a name. I can't get it to open when clicking the Add User link. They both call the same method, but the Add User link simply passes null for the value of user. When clicking the link for a user, although the dialog opens, it doesn't set the property of the user to populate the form. So, I've got a couple of issues. How to open the dialog from the Add User link, and how to populate the user from the user link. The basic code is below.
store.js
import { reactive } from 'vue'
class Store{
constructor() {
this.state = reactive({
users: [
{ id: 1, firstName: "John", lastName: "Smithers" },
{ id: 2, firstName: "Mary", lastName: "Smithers"}
],
currentUserId: null
})
}
addUser(){
this.state.currentUserId = null;
}
}
export const store = new Store()
UserList.vue
<script setup>
import { ref } from 'vue'
import {store} from '../services/store.js'
import UserForm from './UserForm.vue'
const userform = ref(null)
const openDialog = (user) => {
userform.user = user
userform.value.openDialog()
}
</script>
<template>
<table>
<thead>
<tr>
<th>Users</th>
</tr>
</thead>
<tbody>
<tr v-for="user in store.state.users" :key="user.id">
<td><a href="javascript:;" @click="openDialog(user)">{{ user.firstName }} {{ user.lastName }}</a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2" class="text-center" style="padding:20px 0">
<a href="javascript:;" @onclick="openDialog(null)">Add User</a>
<UserForm user="null" ref="userform"></UserForm>
</td>
</tr>
</tfoot>
</table>
</template>
UserForm.vue
<script setup>
import {ref} from 'vue'
const dialog = ref(false)
const save = () => {
console.log('saving')
}
const openDialog =() => {
dialog.value = true;
}
const props = defineProps({
user: {
type: Object,
required: true,
firstName: String,
lastName: String
},
openDialog: {
type: Boolean
}
})
const emits = defineEmits(['save'])
defineExpose({
openDialog
});
</script>
<template>
<form>
<v-row justify="center">
<v-dialog
v-model="dialog"
persistent
width="1024"
>
<template v-slot:activator="{ props }">
</template>
<v-card>
<v-card-title>
<span class="text-h5">Add a User</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col cols="12" lg="4" sm="4" xs="12">
<v-text-field
label="First name*"
required
v-model="props.user.firstName"
></v-text-field>
</v-col>
<v-col cols="12" lg="4" sm="4" xs="12">
<v-text-field
label="Middle name"
hint="example of helper text only on focus"
></v-text-field>
</v-col>
<v-col cols="12" lg="4" sm="4" xs="12">
<v-text-field
label="Last name"
hint="example of helper text only on focus"
v-model="props.user.lastName"
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" lg="6" sm="6" xs="12">
<v-text-field
label="Email*"
required
></v-text-field>
</v-col>
<v-col cols="12" lg="6" sm="6" xs="12">
<v-text-field
label="Password*"
type="password"
required
></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col
cols="12"
sm="6"
>
<v-select
:items="['0-17', '18-29', '30-54', '54+']"
label="Age*"
required
></v-select>
</v-col>
<v-col
cols="12"
sm="6"
>
<v-autocomplete
:items="['Skiing', 'Ice hockey', 'Soccer', 'Basketball', 'Hockey', 'Reading', 'Writing', 'Coding', 'Basejump']"
label="Interests"
multiple
></v-autocomplete>
</v-col>
</v-row>
</v-container>
<small>*indicates required field</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="blue-darken-1"
variant="text"
@click="dialog = false"
>
Close
</v-btn>
<v-btn
class="ma-2"
color="primary"
@click="save"
>
<v-icon
start
icon="mdi-checkbox-marked-circle"
></v-icon>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</form>
</template>
答案1
得分: 0
I can help you translate the code part you provided. Here's the translation:
我不确定你对这行的意思是什么:
```userform.user = user
因为userform是一个引用,所以对我来说userform.user没有解析。
一个可能的解决方案是将一个空的虚拟用户传递给对话框组件,例如:
const selectedUser = ref(null);
const dialog = ref(false);
const openDialog = (user) => {
if (user) {
selectedUser.value = user;
} else {
selectedUser.value = {
id: store.state.users.length + 1,
firstName: "",
lastName: ""
}
}
dialog.value = true;
}
整个过程可能如下:
UserList.vue:
<template>
<v-app>
<v-main>
<v-card width="800" class="ml-4 mt-4">
<v-card-title>
Users
</v-card-title>
<v-card-text>
<v-table class="w-auto">
<thead>
<tr class="bg-grey-lighten-3">
<th>名字</th>
<th>姓氏</th>
</tr>
</thead>
<tbody>
<tr v-for="user in store.state.users" :key=user.id>
<td><a href="#" @click.prevent="openDialog(user)">{{ user.firstName }}</a></td>
<td><a href="#" @click.prevent="openDialog(user)">{{ user.lastName }}</a></td>
</tr>
</tbody>
</v-table>
</v-card-text>
</v-card>
<v-btn
rounded="lg"
color="primary"
class="mt-4 ml-4"
@click="openDialog(null)">添加用户</v-btn>
<my-dialog v-model:dialog="dialog" :user="selectedUser" @save="saveUser"/>
</v-main>
</v-app>
</template>
<script setup>
import { ref } from 'vue';
import { store } from './store.js';
import MyDialog from './MyDialog.vue';
const selectedUser = ref(null);
const dialog = ref(false);
const openDialog = (user) => {
if (user) {
selectedUser.value = user;
} else {
selectedUser.value = {
id: Math.max(...store.state.users.map(o => o.id)) + 1,
firstName: "",
lastName: ""
}
}
dialog.value = true;
}
const saveUser = (user) => {
let existingUser = store.state.users.find((u) => u.id === user.id);
if (existingUser) {
existingUser.lastName = user.lastName;
existingUser.firstName = user.firstName;
} else {
store.state.users.push(user);
}
}
</script>
MyDialog.vue:
<template>
<v-row justify="center">
<v-dialog
:model-value="dialog"
persistent
>
<v-card>
<v-card-title>
<span class="text-h5">用户资料</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col
cols="4"
>
<v-text-field
label="名字*"
v-model="first"
@keyup.enter="saveUser"
required
></v-text-field>
</v-col>
<v-col
cols="4"
>
<v-text-field
label="姓氏*"
hint="持久性帮助文本示例"
v-model="last"
@keyup.enter="saveUser"
persistent-hint
required
></v-text-field>
</v-col>
</v-row>
</v-container>
<small>*表示必填字段</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="blue-darken-1"
variant="text"
@click="closeDialog"
>
关闭
</v-btn>
<v-btn
color="blue-darken-1"
variant="text"
@click="saveUser"
>
保存
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
const props = defineProps(['user', 'dialog']);
const emit = defineEmits(['update:dialog', 'save']);
const first = ref("");
const last = ref("");
const closeDialog = () => {
emit("update:dialog", false);
}
const saveUser = () => {
let user = {
id: props.user.id,
firstName: first.value,
lastName: last.value,
}
emit("save", user);
emit("update:dialog", false);
}
watch(() => props.dialog,
(newValue) => {
if (newValue) {
if (props.user) {
first.value = props.user.firstName;
last.value = props.user.lastName;
} else {
first.value = "";
last.value = "";
}
}
});
</script>
store.js
import { reactive } from 'vue';
class Store{
constructor() {
this.state = reactive({
users: [
{ id: 1, firstName: "John", lastName: "Smithers" },
{ id: 2, firstName: "Mary", lastName: "Smithers"}
],
currentUserId: null
})
}
addUser(){
this.state.currentUserId = null;
}
}
export const store = new Store
<details>
<summary>英文:</summary>
I'm not sure what you mean by this line:
userform.user = user
since userform is a ref, and so userform.user doesn't parse for me.
One possible solution is to pass an empty dummy user into the dialog component, e.g.,
```lang-js
const selectedUser = ref(null);
const dialog = ref(false);
const openDialog = (user) => {
if (user) {
selectedUser.value = user;
} else {
selectedUser.value = {
id: store.state.users.length + 1,
firstName: "",
lastName: ""
}
}
dialog.value = true;
}
The whole thing could look like:
UserList.vue:
<template>
<v-app>
<v-main>
<v-card width="800" class="ml-4 mt-4">
<v-card-title>
Users
</v-card-title>
<v-card-text>
<v-table class="w-auto">
<thead>
<tr class="bg-grey-lighten-3">
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
<tr v-for="user in store.state.users" :key=user.id>
<td><a href="#" @click.prevent="openDialog(user)">{{ user.firstName }}</a></td>
<td><a href="#" @click.prevent="openDialog(user)">{{ user.lastName }}</a></td>
</tr>
</tbody>
</v-table>
</v-card-text>
</v-card>
<v-btn
rounded="lg"
color="primary"
class="mt-4 ml-4"
@click="openDialog(null)">Add User</v-btn>
<my-dialog v-model:dialog="dialog" :user="selectedUser" @save="saveUser"/>
</v-main>
</v-app>
</template>
<script setup>
import { ref } from 'vue';
import {store} from './store.js'
import MyDialog from './MyDialog.vue';
const selectedUser = ref(null);
const dialog = ref(false);
const openDialog = (user) => {
if (user) {
selectedUser.value = user;
} else {
selectedUser.value = {
// get max user id and add 1
id: Math.max(...store.state.users.map(o => o.id)) + 1,
firstName: "",
lastName: ""
}
}
dialog.value = true;
}
const saveUser = (user) => {
let existingUser = store.state.users.find((u) => u.id === user.id);
if (existingUser) {
existingUser.lastName = user.lastName;
existingUser.firstName = user.firstName;
} else {
store.state.users.push(user);
}
}
</script>
MyDialog.vue:
<template>
<v-row justify="center">
<v-dialog
:model-value="dialog"
persistent
>
<v-card>
<v-card-title>
<span class="text-h5">User Profile</span>
</v-card-title>
<v-card-text>
<v-container>
<v-row>
<v-col
cols="4"
>
<v-text-field
label="Legal first name*"
v-model="first"
@keyup.enter="saveUser"
required
></v-text-field>
</v-col>
<v-col
cols="4"
>
<v-text-field
label="Legal last name*"
hint="example of persistent helper text"
v-model="last"
@keyup.enter="saveUser"
persistent-hint
required
></v-text-field>
</v-col>
</v-row>
</v-container>
<small>*indicates required field</small>
</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn
color="blue-darken-1"
variant="text"
@click="closeDialog"
>
Close
</v-btn>
<v-btn
color="blue-darken-1"
variant="text"
@click="saveUser"
>
Save
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-row>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
const props = defineProps(['user', 'dialog']);
const emit = defineEmits(['update:dialog', 'save']);
const first = ref("");
const last = ref("");
const closeDialog = () => {
emit("update:dialog", false);
}
const saveUser = () => {
let user = {
id: props.user.id,
firstName: first.value,
lastName: last.value,
}
emit("save", user);
emit("update:dialog", false);
}
watch(() => props.dialog,
(newValue) => {
if (newValue) {
first.value = props.user.firstName;
last.value = props.user.lastName;
}
});
</script>
store.js
import { reactive } from 'vue'
class Store{
constructor() {
this.state = reactive({
users: [
{ id: 1, firstName: "John", lastName: "Smithers" },
{ id: 2, firstName: "Mary", lastName: "Smithers"}
],
currentUserId: null
})
}
addUser(){
this.state.currentUserId = null;
}
}
export const store = new Store()
Code may be found on the Vuetify Playground
Option is to allow the dialog to accept a null user and deal with it.
To allow the above code to work with this, I test for the truthiness of props.user
when the dialog is opened, when props.dialog
turns true:
watch(() => props.dialog,
(newValue) => {
if (newValue) {
if (props.user) {
first.value = props.user.firstName;
last.value = props.user.lastName;
} else {
first.value = "";
last.value = "";
}
}
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论