如何从父级设置 Vue 子组件对象

问题描述 投票:0回答:1

我是 Vue 的新手,正在使用 Vue 3 组合 api。我也在使用 Vuetify,所以对话框是从他们的示例中复制的代码。希望我没有通过将其混合到我的学习中而使它变得更复杂。我有一个当前由 javascript 数组填充的主要用户列表。这个想法是一个相当常见的设置,它列出数组中的每个用户,用户的链接打开一个对话框来编辑用户信息。还有一个添加用户链接可以打开添加用户的对话框。

在我的代码中,到目前为止,我可以在单击名称时打开对话框。单击“添加用户”链接时我无法打开它。它们都调用相同的方法,但添加用户链接只是将 null 传递给用户值。单击用户的链接时,虽然对话框打开,但它不会设置用户的属性来填充表单。所以,我有几个问题。如何从添加用户链接打开对话框,以及如何从用户链接填充用户。基本代码如下。

商店.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()

用户列表.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>

用户窗体.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>
vuejs3 vue-composition-api
1个回答
0
投票

我不确定这行是什么意思:

userform.user = user

因为 userform 是一个 ref,所以 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;
}  

整个事情看起来像:

用户列表.vue:

<template>
  <v-app>
    <v-main>
      <table>
        <thead>
          <tr>
            <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>
      </table>
      <v-btn 
                rounded="lg"
        color="primary"
                @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 = {
      id: store.state.users.length + 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"                             
                  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"
                                    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>
© www.soinside.com 2019 - 2024. All rights reserved.