Как установить объект дочернего компонента Vue из родительского

Я новичок в Vue и работаю с API композиции Vue 3. Я также использую Vuetify, поэтому код диалога скопирован из их примера. Надеюсь, я не усложнил это, смешав это со своим обучением. У меня есть основной список пользователей, в настоящее время заполненный массивом javascript. Идея представляет собой довольно распространенную настройку, в которой перечислены все пользователи в массиве, а ссылка пользователя открывает диалоговое окно для редактирования информации о пользователе. Также есть ссылка «Добавить пользователя», чтобы открыть диалоговое окно для добавления пользователя.

Пока что в моем коде я могу открыть диалоговое окно, когда я щелкаю имя. Я не могу заставить его открыться при нажатии на ссылку «Добавить пользователя». Оба они вызывают один и тот же метод, но ссылка «Добавить пользователя» просто передает значение null вместо значения пользователя. При нажатии на ссылку для пользователя, хотя диалоговое окно открывается, оно не устанавливает свойство пользователя для заполнения формы. Итак, у меня есть пара вопросов. Как открыть диалоговое окно по ссылке «Добавить пользователя» и как ввести пользователя по ссылке пользователя. Основной код ниже.

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.info('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>
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я не уверен, что вы имеете в виду под этой строкой:

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;
}  

Все это может выглядеть так:

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()

Код можно найти на Vuetify Playground


Опция - разрешить диалогу принимать нулевого пользователя и работать с ним.

Чтобы приведенный выше код работал с этим, я проверяю истинность props.user при открытии диалогового окна, когда props.dialog становится истинным:

watch(() => props.dialog, 
  (newValue) => {
    if (newValue) {
      if (props.user) {
        first.value = props.user.firstName;
        last.value = props.user.lastName;
      } else {
        first.value = "";
        last.value = "";
      }      
    }
});

Vuetify Playground для «нулевого» варианта

Спасибо. Теперь мне просто нужно проработать это и убедиться, что я понимаю, как это работает.

geoff swartz 18.04.2023 00:46

@geoffswartz: я уверен, что есть другие способы решить эту проблему, но это то, что я придумал. Обратите внимание на только что сделанные изменения.

Hovercraft Full Of Eels 18.04.2023 01:09

@geoffswartz: Кроме того, приведенный выше код можно найти на игровой площадке Vuetify. Это то, что мне пришлось использовать, чтобы попытаться что-то придумать, так как я не использую Vuetify (до сих пор).

Hovercraft Full Of Eels 18.04.2023 01:19

Другие вопросы по теме