admin管理员组文章数量:1398840
I have written an app in Vue 3 that allows a user to book a charging point in the office car park, however I am having some issues writing the data back to my GraphQL API.
My main.js looks like this;
import { createApp, h, provide } from 'vue'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import { registerPlugins } from '@/plugins'
import App from './App.vue'
// HTTP connection to the API
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql', // Your GraphQL endpoint
})
// Cache implementation
const cache = new InMemoryCache()
// Apollo Client instance
const apolloClient = new ApolloClient({
link: httpLink,
cache,
})
const app = createApp({
setup() {
provide(DefaultApolloClient, apolloClient)
},
render: () => h(App),
})
registerPlugins(app)
app.mount('#app')
The App.vue is looking something like this
<template>
<v-app>
<Header />
<v-main>
<v-container>
<Chargers />
</v-container>
</v-main>
</v-app>
</template>
<script>
import Chargers from '@/components/Chargers.vue';
import Header from '@/components/Header.vue'; // Import Header.vue
export default {
name: 'App',
components: {
Chargers, // Add the Chargers component
Header, // Add the Header component here
}
};
</script>
<style>
body {
font-family: 'Roboto', sans-serif;
}
</style>
The Chargers.vue looks like this
<template>
<v-container>
<v-row v-for="station in stations" :key="station.id" class="mb-4">
<v-col cols="12">
<h2 class="text-h5 font-weight-bold">{{ station.name }}</h2>
</v-col>
<v-col
v-for="child in station.children"
:key="child.id"
cols="12"
sm="6"
md="4"
>
<v-card class="pa-4 left-align-card clickable" @click="openBookingDialog(child)">
<h3 class="text-h5 font-weight-bold">{{ child.name }}</h3>
<p class="status">
⚡ <strong>{{ child.state.toUpperCase() }}</strong>
</p>
<p class="check-in">Check-in</p>
</v-card>
</v-col>
</v-row>
<v-dialog v-model="dialogVisible" max-width="500px">
<v-card>
<v-card-title>
<span class="text-h6">Booking for {{ selectedChargerName }}</span>
</v-card-title>
<v-card-text>
<BookingForm
:charger="selectedChargerName"
:charger-id="selectedChargerId"
:stations="stations"
:refetchStations="refetchStations"
@check-in="updateChildState"
@closeDialog="dialogVisible = false"
/>
</v-card-text>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
import { ref, watchEffect } from "vue";
import { useQuery } from "@vue/apollo-composable";
import { GET_STATIONS } from "@/graphql/queries";
import BookingForm from "./BookingForm.vue";
export default {
components: {
BookingForm,
},
setup() {
const { result, refetch } = useQuery(GET_STATIONS);
const stations = ref([]);
const dialogVisible = ref(false);
const selectedChargerName = ref("");
const selectedChargerId = ref("");
// Open the booking dialog for the selected charger
const openBookingDialog = (child) => {
selectedChargerName.value = child.name;
selectedChargerId.value = child.id; // Ensure child.id is stored here
console.log("Opening booking dialog for charger:", child.id); // Log the child.id for verification
dialogVisible.value = true;
};
// Watch and update stations when the data is fetched
watchEffect(() => {
if (result.value) {
stations.value = result.value.stations;
console.log("Stations data fetched:", stations.value);
}
});
// Refetch the stations
const refetchStations = async () => {
console.log("Refetching stations...");
await refetch({ fetchPolicy: "network-only" });
if (result.value) {
console.log("Updated stations:", result.value.stations);
} else {
console.warn("Stations data not updated.");
}
};
// Update the updateChildState method
const updateChildState = (bookingData) => {
console.log("Booking data received:", bookingData);
// Find the station containing the booked charger
const stationIndex = stations.value.findIndex((station) =>
station.children.some((child) => child.name === bookingData.chargerName)
);
if (stationIndex === -1) {
console.error("Station not found for charger name:", bookingData.chargerName);
return;
}
const station = stations.value[stationIndex];
// Find the charger index within the station
const childIndex = station.children.findIndex((child) => child.name === bookingData.chargerName);
if (childIndex === -1) {
console.error("Charger not found in the station list for charger name:", bookingData.chargerName);
return;
}
console.log("Found charger:", station.children[childIndex].name);
const updatedStation = {
...station,
children: station.children.map((child, index) =>
index === childIndex
? { ...child, state: "Charging", userEmail: bookingData.userEmail, registrationNumber: bookingData.registrationNumber, vehicleType: bookingData.vehicleType }
: child
)
};
stations.value = [
...stations.value.slice(0, stationIndex),
updatedStation,
...stations.value.slice(stationIndex + 1),
];
};
return {
stations,
refetchStations,
dialogVisible,
selectedChargerName,
selectedChargerId,
openBookingDialog,
updateChildState,
};
},
};
</script>
<style>
.status strong {
color: rgb(91, 181, 19);
}
.check-in {
max-height: 60px;
max-width: 100px;
display: flex;
align-items: center;
overflow: hidden;
overflow-wrap: break-word;
text-transform: uppercase;
font-size: 12px;
cursor: pointer;
color: rgb(0, 62, 122);
border: 5px;
padding: 15px 0 0;
transition: background 0.8s;
}
</style>
And the form I use to add the details to the API is called BookingForm.vue
<template>
<v-form @submit.prevent="handleSubmit">
<v-row>
<!-- Registration Number -->
<v-col cols="12">
<v-text-field
v-model="registrationNumber"
label="Vehicle Registration Number"
required
></v-text-field>
</v-col>
<!-- Vehicle Type -->
<v-col cols="12">
<v-select
v-model="vehicleType"
:items="vehicleTypes"
label="Vehicle Type"
required
></v-select>
</v-col>
<!-- Email -->
<v-col cols="12">
<v-text-field
v-model="userEmail"
label="Email Address"
type="email"
required
></v-text-field>
</v-col>
<!-- Error Message -->
<v-col cols="12" v-if="error">
<v-alert type="error">
{{ error.message }}
</v-alert>
</v-col>
<v-col cols="12">
<v-btn
type="submit"
color="primary"
:disabled="!isValid || isCheckedIn || loading"
>
{{ loading ? "Checking in..." : "Check-In" }}
</v-btn>
</v-col>
</v-row>
</v-form>
</template>
<script>
import { ref, computed, watchEffect } from "vue";
import { useMutation } from "@vue/apollo-composable";
import { BOOK_CHARGER } from "@/graphql/mutations";
export default {
props: {
charger: String, // Charger Name passed from parent
dialogModel: Boolean, // Controls the visibility of the dialog
stations: Array, // Accepting stations as a prop
refetchStations: Function, // Function to refetch stations passed from the parent
},
setup(props) {
const registrationNumber = ref("");
const vehicleType = ref("");
const userEmail = ref("");
const isCheckedIn = ref(false);
const vehicleTypes = ["Plug-in Hybrid", "Full Electric"];
// Ensure all fields are filled before enabling submit
const isValid = computed(() =>
props.charger &&
registrationNumber.value &&
vehicleType.value &&
userEmail.value
);
// Debugging: Watch for changes in charger prop
watchEffect(() => {
console.log("Updated charger prop:", props.charger);
});
// Mutation for booking the charger
const { mutate: bookCharger, loading, error, data } = useMutation(BOOK_CHARGER);
const resetForm = () => {
registrationNumber.value = "";
vehicleType.value = "";
userEmail.value = "";
};
// Handle Check-In and mutation call
const handleSubmit = async () => {
try {
const response = await bookCharger({
variables: {
chargerName: props.charger,
registrationNumber: registrationNumber.value,
vehicleType: vehicleType.value,
userEmail: userEmail.value,
},
});
console.log("Booking response:", response?.data);
} catch (err) {
console.error("Error during booking:", err);
}
};
return {
registrationNumber,
vehicleType,
userEmail,
isCheckedIn,
vehicleTypes,
handleSubmit,
loading,
error,
data,
isValid,
};
},
};
</script>
and finally my mutations.js
import { gql } from "graphql-tag";
export const BOOK_CHARGER = gql`
mutation BookCharger(
$chargerName: String!,
$registrationNumber: String!,
$vehicleType: String!,
$userEmail: String!
) {
bookCharger(
chargerName: $chargerName,
registrationNumber: $registrationNumber,
vehicleType: $vehicleType,
userEmail: $userEmail
) {
success
message
}
}
`;
I am currently powering all this via a locally created GraphQL API with Apollo.
I can see that the form is capturing the data, however I can't see what is happening to that data, and why it isn't being sent to the GraphQL API
Can anyone help?
I have tried a lot of googling and even asked ChatGPT, however that resulted in the form generating this error in the console
Opening booking dialog for charger: 1A BookingForm.vue:82 Updated charger prop: 1A BookingForm.vue:109 Error during booking: ApolloError: Variable "$chargerName" of required type "String!" was not provided. Variable "$registrationNumber" of required type "String!" was not provided. Variable "$vehicleType" of required type "String!" was not provided. Variable "$userEmail" of required type "String!" was not provided.
the GraphQL API itself isn't actually showing an error, but here is my Schema for reference.
export const typeDefs = `#graphql
type Station {
id: ID!
name: String!
children: [Child!]!
}
type Child {
id: ID!
name: String!
state: String!
userEmail: String
registrationNumber: String
vehicleType: String
countdown: String
}
# Define the response for booking a charger
type BookChargerResponse {
success: Boolean
message: String
}
type Mutation {
# Define the mutation to book a charger
bookCharger(
registrationNumber: String!,
vehicleType: String!,
userEmail: String!,
chargerName: String!
): BookChargerResponse
}
type Query {
stations: [Station!]!
}
`;
I have written an app in Vue 3 that allows a user to book a charging point in the office car park, however I am having some issues writing the data back to my GraphQL API.
My main.js looks like this;
import { createApp, h, provide } from 'vue'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import { registerPlugins } from '@/plugins'
import App from './App.vue'
// HTTP connection to the API
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql', // Your GraphQL endpoint
})
// Cache implementation
const cache = new InMemoryCache()
// Apollo Client instance
const apolloClient = new ApolloClient({
link: httpLink,
cache,
})
const app = createApp({
setup() {
provide(DefaultApolloClient, apolloClient)
},
render: () => h(App),
})
registerPlugins(app)
app.mount('#app')
The App.vue is looking something like this
<template>
<v-app>
<Header />
<v-main>
<v-container>
<Chargers />
</v-container>
</v-main>
</v-app>
</template>
<script>
import Chargers from '@/components/Chargers.vue';
import Header from '@/components/Header.vue'; // Import Header.vue
export default {
name: 'App',
components: {
Chargers, // Add the Chargers component
Header, // Add the Header component here
}
};
</script>
<style>
body {
font-family: 'Roboto', sans-serif;
}
</style>
The Chargers.vue looks like this
<template>
<v-container>
<v-row v-for="station in stations" :key="station.id" class="mb-4">
<v-col cols="12">
<h2 class="text-h5 font-weight-bold">{{ station.name }}</h2>
</v-col>
<v-col
v-for="child in station.children"
:key="child.id"
cols="12"
sm="6"
md="4"
>
<v-card class="pa-4 left-align-card clickable" @click="openBookingDialog(child)">
<h3 class="text-h5 font-weight-bold">{{ child.name }}</h3>
<p class="status">
⚡ <strong>{{ child.state.toUpperCase() }}</strong>
</p>
<p class="check-in">Check-in</p>
</v-card>
</v-col>
</v-row>
<v-dialog v-model="dialogVisible" max-width="500px">
<v-card>
<v-card-title>
<span class="text-h6">Booking for {{ selectedChargerName }}</span>
</v-card-title>
<v-card-text>
<BookingForm
:charger="selectedChargerName"
:charger-id="selectedChargerId"
:stations="stations"
:refetchStations="refetchStations"
@check-in="updateChildState"
@closeDialog="dialogVisible = false"
/>
</v-card-text>
</v-card>
</v-dialog>
</v-container>
</template>
<script>
import { ref, watchEffect } from "vue";
import { useQuery } from "@vue/apollo-composable";
import { GET_STATIONS } from "@/graphql/queries";
import BookingForm from "./BookingForm.vue";
export default {
components: {
BookingForm,
},
setup() {
const { result, refetch } = useQuery(GET_STATIONS);
const stations = ref([]);
const dialogVisible = ref(false);
const selectedChargerName = ref("");
const selectedChargerId = ref("");
// Open the booking dialog for the selected charger
const openBookingDialog = (child) => {
selectedChargerName.value = child.name;
selectedChargerId.value = child.id; // Ensure child.id is stored here
console.log("Opening booking dialog for charger:", child.id); // Log the child.id for verification
dialogVisible.value = true;
};
// Watch and update stations when the data is fetched
watchEffect(() => {
if (result.value) {
stations.value = result.value.stations;
console.log("Stations data fetched:", stations.value);
}
});
// Refetch the stations
const refetchStations = async () => {
console.log("Refetching stations...");
await refetch({ fetchPolicy: "network-only" });
if (result.value) {
console.log("Updated stations:", result.value.stations);
} else {
console.warn("Stations data not updated.");
}
};
// Update the updateChildState method
const updateChildState = (bookingData) => {
console.log("Booking data received:", bookingData);
// Find the station containing the booked charger
const stationIndex = stations.value.findIndex((station) =>
station.children.some((child) => child.name === bookingData.chargerName)
);
if (stationIndex === -1) {
console.error("Station not found for charger name:", bookingData.chargerName);
return;
}
const station = stations.value[stationIndex];
// Find the charger index within the station
const childIndex = station.children.findIndex((child) => child.name === bookingData.chargerName);
if (childIndex === -1) {
console.error("Charger not found in the station list for charger name:", bookingData.chargerName);
return;
}
console.log("Found charger:", station.children[childIndex].name);
const updatedStation = {
...station,
children: station.children.map((child, index) =>
index === childIndex
? { ...child, state: "Charging", userEmail: bookingData.userEmail, registrationNumber: bookingData.registrationNumber, vehicleType: bookingData.vehicleType }
: child
)
};
stations.value = [
...stations.value.slice(0, stationIndex),
updatedStation,
...stations.value.slice(stationIndex + 1),
];
};
return {
stations,
refetchStations,
dialogVisible,
selectedChargerName,
selectedChargerId,
openBookingDialog,
updateChildState,
};
},
};
</script>
<style>
.status strong {
color: rgb(91, 181, 19);
}
.check-in {
max-height: 60px;
max-width: 100px;
display: flex;
align-items: center;
overflow: hidden;
overflow-wrap: break-word;
text-transform: uppercase;
font-size: 12px;
cursor: pointer;
color: rgb(0, 62, 122);
border: 5px;
padding: 15px 0 0;
transition: background 0.8s;
}
</style>
And the form I use to add the details to the API is called BookingForm.vue
<template>
<v-form @submit.prevent="handleSubmit">
<v-row>
<!-- Registration Number -->
<v-col cols="12">
<v-text-field
v-model="registrationNumber"
label="Vehicle Registration Number"
required
></v-text-field>
</v-col>
<!-- Vehicle Type -->
<v-col cols="12">
<v-select
v-model="vehicleType"
:items="vehicleTypes"
label="Vehicle Type"
required
></v-select>
</v-col>
<!-- Email -->
<v-col cols="12">
<v-text-field
v-model="userEmail"
label="Email Address"
type="email"
required
></v-text-field>
</v-col>
<!-- Error Message -->
<v-col cols="12" v-if="error">
<v-alert type="error">
{{ error.message }}
</v-alert>
</v-col>
<v-col cols="12">
<v-btn
type="submit"
color="primary"
:disabled="!isValid || isCheckedIn || loading"
>
{{ loading ? "Checking in..." : "Check-In" }}
</v-btn>
</v-col>
</v-row>
</v-form>
</template>
<script>
import { ref, computed, watchEffect } from "vue";
import { useMutation } from "@vue/apollo-composable";
import { BOOK_CHARGER } from "@/graphql/mutations";
export default {
props: {
charger: String, // Charger Name passed from parent
dialogModel: Boolean, // Controls the visibility of the dialog
stations: Array, // Accepting stations as a prop
refetchStations: Function, // Function to refetch stations passed from the parent
},
setup(props) {
const registrationNumber = ref("");
const vehicleType = ref("");
const userEmail = ref("");
const isCheckedIn = ref(false);
const vehicleTypes = ["Plug-in Hybrid", "Full Electric"];
// Ensure all fields are filled before enabling submit
const isValid = computed(() =>
props.charger &&
registrationNumber.value &&
vehicleType.value &&
userEmail.value
);
// Debugging: Watch for changes in charger prop
watchEffect(() => {
console.log("Updated charger prop:", props.charger);
});
// Mutation for booking the charger
const { mutate: bookCharger, loading, error, data } = useMutation(BOOK_CHARGER);
const resetForm = () => {
registrationNumber.value = "";
vehicleType.value = "";
userEmail.value = "";
};
// Handle Check-In and mutation call
const handleSubmit = async () => {
try {
const response = await bookCharger({
variables: {
chargerName: props.charger,
registrationNumber: registrationNumber.value,
vehicleType: vehicleType.value,
userEmail: userEmail.value,
},
});
console.log("Booking response:", response?.data);
} catch (err) {
console.error("Error during booking:", err);
}
};
return {
registrationNumber,
vehicleType,
userEmail,
isCheckedIn,
vehicleTypes,
handleSubmit,
loading,
error,
data,
isValid,
};
},
};
</script>
and finally my mutations.js
import { gql } from "graphql-tag";
export const BOOK_CHARGER = gql`
mutation BookCharger(
$chargerName: String!,
$registrationNumber: String!,
$vehicleType: String!,
$userEmail: String!
) {
bookCharger(
chargerName: $chargerName,
registrationNumber: $registrationNumber,
vehicleType: $vehicleType,
userEmail: $userEmail
) {
success
message
}
}
`;
I am currently powering all this via a locally created GraphQL API with Apollo.
I can see that the form is capturing the data, however I can't see what is happening to that data, and why it isn't being sent to the GraphQL API
Can anyone help?
I have tried a lot of googling and even asked ChatGPT, however that resulted in the form generating this error in the console
Opening booking dialog for charger: 1A BookingForm.vue:82 Updated charger prop: 1A BookingForm.vue:109 Error during booking: ApolloError: Variable "$chargerName" of required type "String!" was not provided. Variable "$registrationNumber" of required type "String!" was not provided. Variable "$vehicleType" of required type "String!" was not provided. Variable "$userEmail" of required type "String!" was not provided.
the GraphQL API itself isn't actually showing an error, but here is my Schema for reference.
export const typeDefs = `#graphql
type Station {
id: ID!
name: String!
children: [Child!]!
}
type Child {
id: ID!
name: String!
state: String!
userEmail: String
registrationNumber: String
vehicleType: String
countdown: String
}
# Define the response for booking a charger
type BookChargerResponse {
success: Boolean
message: String
}
type Mutation {
# Define the mutation to book a charger
bookCharger(
registrationNumber: String!,
vehicleType: String!,
userEmail: String!,
chargerName: String!
): BookChargerResponse
}
type Query {
stations: [Station!]!
}
`;
Share
Improve this question
asked Mar 26 at 22:13
TakuhiiTakuhii
9672 gold badges9 silver badges29 bronze badges
1
- That helps with the weird errors thank you, but I am struggling to get it to write back to the GraphQL API still :/ – Takuhii Commented Mar 27 at 8:08
1 Answer
Reset to default 0Your issue seems to be here:
const response = await bookCharger({
variables: {
chargerName: props.charger,
registrationNumber: registrationNumber.value,
vehicleType: vehicleType.value,
userEmail: userEmail.value,
},
});
The mutate
function returned by useMutation
expects your variables as the first parameter, but you seem to nest your variables into an object with a superfluous variables
key. Change your code to:
const response = await bookCharger({
chargerName: props.charger,
registrationNumber: registrationNumber.value,
vehicleType: vehicleType.value,
userEmail: userEmail.value,
});
本文标签: vuejs3Writing Back to GraphQL APIStack Overflow
版权声明:本文标题:vuejs3 - Writing Back to GraphQL API - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744123383a2591844.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论