设置Vue子组件对象从父组件中

huangapple go评论125阅读模式
英文:

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


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&#39;m not sure what you mean by this line:

userform.user = user


since userform is a ref, and so userform.user doesn&#39;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) =&gt; {
  if (user) {
  	selectedUser.value = user;
  } else {
    selectedUser.value = {
      id: store.state.users.length + 1,
      firstName: &quot;&quot;,
      lastName: &quot;&quot;
     }
  }
  dialog.value = true;
}  

The whole thing could look like:

UserList.vue:

&lt;template&gt;
  &lt;v-app&gt;
    &lt;v-main&gt;
      &lt;v-card width=&quot;800&quot; class=&quot;ml-4 mt-4&quot;&gt;
        &lt;v-card-title&gt;
          Users
        &lt;/v-card-title&gt;
        &lt;v-card-text&gt;
          &lt;v-table class=&quot;w-auto&quot;&gt;
            &lt;thead&gt;
              &lt;tr class=&quot;bg-grey-lighten-3&quot;&gt;
                &lt;th&gt;First Name&lt;/th&gt;
                &lt;th&gt;Last Name&lt;/th&gt;
              &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
              &lt;tr v-for=&quot;user in store.state.users&quot; :key=user.id&gt;
                &lt;td&gt;&lt;a href=&quot;#&quot; @click.prevent=&quot;openDialog(user)&quot;&gt;{{ user.firstName }}&lt;/a&gt;&lt;/td&gt;
                &lt;td&gt;&lt;a href=&quot;#&quot; @click.prevent=&quot;openDialog(user)&quot;&gt;{{ user.lastName }}&lt;/a&gt;&lt;/td&gt;
              &lt;/tr&gt;
            &lt;/tbody&gt;
          &lt;/v-table&gt;          
        &lt;/v-card-text&gt;

      &lt;/v-card&gt;        
      &lt;v-btn 
				rounded=&quot;lg&quot;
      	color=&quot;primary&quot;
				class=&quot;mt-4 ml-4&quot;
				@click=&quot;openDialog(null)&quot;&gt;Add User&lt;/v-btn&gt;
      &lt;my-dialog v-model:dialog=&quot;dialog&quot; :user=&quot;selectedUser&quot; @save=&quot;saveUser&quot;/&gt;
    &lt;/v-main&gt;
  &lt;/v-app&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;;
import {store} from &#39;./store.js&#39;
import MyDialog from &#39;./MyDialog.vue&#39;;
  
const selectedUser = ref(null);
const dialog = ref(false);  
const openDialog = (user) =&gt; {
  if (user) {
  	selectedUser.value = user;
  } else {
    selectedUser.value = {
      // get max user id and add 1
      id: Math.max(...store.state.users.map(o =&gt; o.id)) + 1,
      firstName: &quot;&quot;,
      lastName: &quot;&quot;
    }
  }
  dialog.value = true;
}  
const saveUser = (user) =&gt; {
  let existingUser = store.state.users.find((u) =&gt; u.id === user.id);
  if (existingUser) {
    existingUser.lastName = user.lastName;
    existingUser.firstName = user.firstName;
  } else {
    store.state.users.push(user);    
  }
}
&lt;/script&gt;

MyDialog.vue:

&lt;template&gt;
  &lt;v-row justify=&quot;center&quot;&gt;
    &lt;v-dialog
      :model-value=&quot;dialog&quot;
      persistent
    &gt;
      &lt;v-card&gt;
        &lt;v-card-title&gt;
          &lt;span class=&quot;text-h5&quot;&gt;User Profile&lt;/span&gt;
        &lt;/v-card-title&gt;
        &lt;v-card-text&gt;
          &lt;v-container&gt;
            &lt;v-row&gt;
              &lt;v-col
								cols=&quot;4&quot;
              &gt;
                &lt;v-text-field
                  label=&quot;Legal first name*&quot;
									v-model=&quot;first&quot;  
									@keyup.enter=&quot;saveUser&quot;                              
                  required
                &gt;&lt;/v-text-field&gt;
              &lt;/v-col&gt;
              &lt;v-col
								cols=&quot;4&quot;
              &gt;
                &lt;v-text-field
                  label=&quot;Legal last name*&quot;
                  hint=&quot;example of persistent helper text&quot;
									v-model=&quot;last&quot;
									@keyup.enter=&quot;saveUser&quot;                              
									persistent-hint
                  required
                &gt;&lt;/v-text-field&gt;
              &lt;/v-col&gt;
            &lt;/v-row&gt;
          &lt;/v-container&gt;
          &lt;small&gt;*indicates required field&lt;/small&gt;
        &lt;/v-card-text&gt;
        &lt;v-card-actions&gt;
          &lt;v-spacer&gt;&lt;/v-spacer&gt;
          &lt;v-btn
            color=&quot;blue-darken-1&quot;
            variant=&quot;text&quot;
            @click=&quot;closeDialog&quot;
          &gt;
            Close
          &lt;/v-btn&gt;
          &lt;v-btn
            color=&quot;blue-darken-1&quot;
            variant=&quot;text&quot;
            @click=&quot;saveUser&quot;
          &gt;
            Save
          &lt;/v-btn&gt;
        &lt;/v-card-actions&gt;
      &lt;/v-card&gt;
    &lt;/v-dialog&gt;
  &lt;/v-row&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, watch, onMounted } from &#39;vue&#39;;
const props =  defineProps([&#39;user&#39;, &#39;dialog&#39;]);
const emit = defineEmits([&#39;update:dialog&#39;, &#39;save&#39;]);
const first = ref(&quot;&quot;);
const last = ref(&quot;&quot;);
  
const closeDialog = () =&gt; {
  emit(&quot;update:dialog&quot;, false);
}  

const saveUser = () =&gt; {
  let user = {
    id: props.user.id,
    firstName: first.value,
    lastName: last.value,
  }
  emit(&quot;save&quot;, user);
  emit(&quot;update:dialog&quot;, false);
}

watch(() =&gt; props.dialog, 
  (newValue) =&gt; {
  	if (newValue) {
      first.value = props.user.firstName;
      last.value = props.user.lastName;
    }
});
&lt;/script&gt;

store.js

import { reactive } from &#39;vue&#39;

class Store{
    constructor() {
        this.state = reactive({
            users: [
                { id: 1, firstName: &quot;John&quot;, lastName: &quot;Smithers&quot; },
                { id: 2, firstName: &quot;Mary&quot;, lastName: &quot;Smithers&quot;}
            ],
            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(() =&gt; props.dialog, 
  (newValue) =&gt; {
  	if (newValue) {
      if (props.user) {
        first.value = props.user.firstName;
        last.value = props.user.lastName;
      } else {
        first.value = &quot;&quot;;
        last.value = &quot;&quot;;
      }      
    }
});

Vuetify Playground for the "null" option

huangapple
  • 本文由 发表于 2023年4月17日 04:37:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76030213.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定