I'm fairly new to Vue and I'm trying to pass data from a component to a view. I'm not sure if I'm using props right. I have a dialog and when I save, I want to insert the data to the database. I also want to reuse the addCustomer() function that's why I didn't place the function in the component.
pages/customers.vue
<template>
<div>
<div class="items-center justify-between md:flex">
<Heading
title="Customers"
desc="The list of customers or companies you work with."
/>
<button #click="openModal" class="btn-primary">Add New Customer</button>
</div>
<CustomerList class="mt-4" :customers="customers" />
</div>
<CustomerDialog
:is-open="isOpen"
:close-modal="closeModal"
:open-modal="openModal"
:name="name"
:address="address"
:email="email"
:add-customer="addCustomer"
/>
</template>
<script setup>
const client = useSupabaseClient();
const name = ref("");
const address = ref("");
const email = ref("");
const isOpen = ref(false);
function closeModal() {
isOpen.value = false;
}
function openModal() {
isOpen.value = true;
}
const { data: customers } = await useAsyncData("customers", async () => {
const { data } = await client.from("customers").select("*");
return data;
});
async function addCustomer() {
if (name.value == "" || address.value == "" || email.value == "") return;
const { data } = await client.from("customers").upsert({
name: name.value,
address: address.value,
email: email.value,
});
customers.value.push(data[0]);
name.value = "";
address.value = "";
email.value = "";
closeModal();
}
</script>
components/customer/Dialog.vue
<template>
<Dialog as="div" #close="closeModal" class="relative z-10">
<input type="text" id="name" v-model="name" />
<input type="text" id="address" v-model="address" />
<input type="email" id="email" v-model="email" />
<button type="button" #click="addCustomer">Save</button>
<button type="button" #click="closeModal">Cancel</button>
</Dialog>
</template>
<script setup>
defineProps([
"name",
"address",
"email",
"addCustomer",
"isOpen",
"closeModal",
"openModal",
]);
</script>
EDIT: The Cancel button in the Dialog works while Save button doesn't.
You cannot bind props directly to the v-model directive, in your case you've to use Multiple v-model bindings
<template>
<Dialog as="div" #close="closeModal" class="relative z-10">
<input type="text" id="name" :value="name" #input="$emit('update:name', $event.target.value)"/>
<input type="text" id="address" :value="adress" #input="$emit('update:address', $event.target.value)" />
<input type="email" id="email" :value="email" #input="$emit('update:email', $event.target.value)" />
<button type="button" #click="$emit('add-customer')">Save</button>
<button type="button" #click="closeModal">Cancel</button>
</Dialog>
</template>
<script setup>
defineProps([
"name",
"address",
"email",
"addCustomer",
"isOpen",
"closeModal",
"openModal",
]);
defineEmits(['update:name', 'update:email','update:address','add-customer'])
</script>
in parent component :
<CustomerDialog
:is-open="isOpen"
:close-modal="closeModal"
:open-modal="openModal"
v-model:name="name"
v-model:address="address"
v-model:email="email"
#add-customer="addCustomer"
/>
As addCustomer method is available in parent. What you can do is that emit an event on Save button click from the dialog component and then capture the event in parent and invoke addCustomer method.
I just created a below code snippet with Vue 2.* just for your understanding purpose.
Vue.component('customerdialog', {
data() {
return {
customerDetailObj: {}
}
},
props: ['name', 'address', 'email'],
mounted() {
this.customerDetailObj = {
name: this.name,
address: this.address,
email: this.email
}
},
template: `<div><input type="text" id="name" v-model="customerDetailObj.name" />
<input type="text" id="address" v-model="customerDetailObj.address" />
<input type="email" id="email" v-model="customerDetailObj.email" />
<button type="button" #click="$emit('add-customer', customerDetailObj)">Save</button></div>`
});
var app = new Vue({
el: '#app',
data: {
name: 'Alpha',
address: 'Street 1',
email: 'alpha#testmail.com',
customerList: []
},
methods: {
addCustomer(customerObj) {
// Created user details
this.customerList.push(customerObj);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<CustomerDialog :name="name"
:address="address"
:email="email" #add-customer="addCustomer($event)"></CustomerDialog>
<pre>{{ customerList }}</pre>
</div>
You can defineEmits
<script setup>
import {
TransitionRoot,
TransitionChild,
Dialog,
DialogPanel,
DialogTitle,
} from "#headlessui/vue";
defineProps([
"name",
"address",
"email",
"addCustomer",
"isOpen",
"closeModal",
"openModal",
]);
let emit = defineEmits(["add"])
</script>
Or better with typescript:
<script lang="ts" setup>
import {
TransitionRoot,
TransitionChild,
Dialog,
DialogPanel,
DialogTitle,
} from "#headlessui/vue";
defineProps([
"name",
"address",
"email",
"addCustomer",
"isOpen",
"closeModal",
"openModal",
]);
let emit = defineEmits<{ (name: "add"): void }>()
</script>
Now you can use it in your button:
<button
type="button"
class="inline-flex justify-center px-4 py-2 text-sm font-medium text-red-900 bg-red-100 border border-transparent rounded-md hover:bg-red-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2"
#click="emit('add')"
>
Save
</button>
Now you can listen to this add event and trigger your addCustomer function
<CustomerDialog
:is-open="isOpen"
:close-modal="closeModal"
:open-modal="openModal"
:name="name"
:address="address"
:email="email"
#add="addCustomer"
/>
You'll want to use event emitters:
In the dialog:
<button #click="$emit('addCustomer')">save</button>
In customers.vue:
<CustomerDialog
:is-open="isOpen"
:close-modal="closeModal"
:open-modal="openModal"
:name="name"
:address="address"
:email="email"
#add-customer="addCustomer"
/>
Related
I had a form which contain several radio input type, when I try to submit the form data to mongoDB, it failed. Something wrong in onSubmit? Thanks!
The form:
<form #submit="onSubmit">
<fieldset>
<legend>What is your favourite Color? </legend>
<div>
<div><input type="radio" name="color" value="Red" checked><label>
Red</label>
</div>
<div><input type="radio" name="color" value="Blue"><label>
Blue</label>
</div>
</fieldset>
<button type="submit" class="btn btn-primary">Submit Survey</button>
</form>
Vue:
<script>
import { ref, onMounted } from "vue";
export default {
name: 'SurveyView',
setup() {
const onSubmit = (event) => { fetch("/survey", {
method: "post",
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(event.target.value)
})};
onMounted();
return {
onSubmit
}
}
You aren't binding any data property to your template. You can use v-model to bind your radio button then use the data to submit to server/db.
<script type="importmap">
{
"imports": {
"vue": "https://unpkg.com/vue#3/dist/vue.esm-browser.js"
}
}
</script>
<div id="app">
<form #submit.prevent="onSubmit">
<fieldset>
<legend>What is your favourite Color?</legend>
<div>
<div>
<input type="radio" name="color" value="Red" v-model="color" /><label>
Red</label
>
</div>
<div>
<input
type="radio"
name="color"
value="Blue"
v-model="color"
/><label> Blue</label>
</div>
</div>
</fieldset>
<button type="submit" class="btn btn-primary">Submit Survey</button>
</form>
</div>
<script type="module">
import { createApp, ref } from 'vue'
createApp({
name: "App",
setup() {
const color = ref(null);
const onSubmit = () => {
console.log(color.value);
// make server call with appropriate data
};
return { color, onSubmit };
},
}).mount('#app')
</script>
Not sure why I'm getting unexpected results here. When I select a product I'd like to have one more dynamic select and input instead I get like +100 of them
<template v-for="product in form.selectedProducts">
<div class="col-span-3">
<Label value="Products" />
<Select :options="products" v-model="product.id" label="trade_name" class="mt-1" />
</div>
<div class="col-span-3">
<Label value="Quantity" />
<Input type="number" v-model="product.quantity" class="block w-full mt-1" />
</div>
</template>
<script setup>
import { useForm } from '#inertiajs/inertia-vue3';
import { watch } from "vue";
const form = useForm({
selectedProducts: [{
id: -1,
quantity: ''
}]
});
watch(form.selectedProducts, (value) => {
form.selectedProducts.push({
id: -1,
quantity: ''
})
})
</script>
I developed one page which is responsible for Registering the users previously i submitted the form without radio buttons, now i need to add radio buttons based on checked type button that value should be sent to the backend ,i am unable to figure out where did i mistaken please help me to fix this issue
Register.vue
<template>
<div class="main">
<div v-if="flag==true" class="container">
<img id="side-img" src="../assets/sideImg.png" alt="notFound" />
<p id="side-content">Online Book Shopping</p>
<div class="box">
<div class="headings">
<h5 class="signin" v-on:click="flip();" id="login" :class="{ active: isLogin }" #click="isLogin = true">Login</h5>
<h5 class="signup" id="signup" :class="{ active: !isLogin }" #click="isLogin = false">signup</h5>
</div>
<form ref="myForm" #submit.prevent="handlesubmit">
<div class="fullname">
<p>FullName</p>
<input type="name" id="name-input" class="namebox" required v-model="fullName" autocomplete="off" pattern="[A-Za-z]{3,12}">
</div>
<div class="username">
<p>EmailID</p>
<input type="email" id="Email-input" class="emailbox" autocomplete="off" required v-model="email" pattern="^[a-z0-9._%+-]+#[a-z0-9.-]+\.[a-z]{2,4}$">
</div>
<div class="password-section">
<p>Password</p>
<input :type="password_type" class="password" :class="{'password-visible': isPasswordVisible }" id="passField" v-model="password" pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{6,}$" required>
<i class="bi bi-eye-slash" id="togglePassword" #click="togglePassword();"></i>
</div>
<div class="mobile">
<p>MobileNumber</p>
<input type="tel" class="telephone" autocomplete="off" v-model="mobile" id="tel" pattern="^\d{10}$" required>
</div>
<div class="role-btns">
<input type="radio" id="user" value="user" name="role" v-model="roleUser" >
<label for="user" class="radio-label">user</label>
<input type="radio" id="admin" value="admin" name="role" v-model="roleUser">
<label for="admin">admin</label>
</div>
<button class="btn-section" id="btn" type="submit">Signup</button>
</form>
</div>
</div>
<Login v-if="flag==false" />
</div>
</template>
<script>
import service from '../service/User'
export default {
name: 'Register',
components: {
Login: () => import('./Login.vue')
},
data() {
return {
fullName: '',
email: '',
password: '',
mobile: '',
roleUser:'',
password_type: "password",
isLogin: false,
isPasswordVisible: false,
flag: true,
title: 'Online Book Shopping'
}
},
methods: {
flip() {
this.flag = !this.flag;
},
togglePassword() {
this.password_type = this.password_type === 'password' ? 'text' : 'password'
this.isPasswordVisible = !this.isPasswordVisible
},
handlesubmit() {
let userData = {
fullName: this.fullName,
email: this.email,
password: this.password,
mobile: this.mobile
}
service.userRegister(userData).then(response => {
if (response.status == 201) {
alert("user registered successfully");
this.$refs.myForm.reset();
this.$router.push('/login');
}
return response;
}).catch(error => {
alert("invalid credentials");
return error;
})
}
}
}
</script>
<style lang="scss" scoped>
#import "#/styles/Register.scss";
</style>
let userData = {
fullName: this.fullName,
email: this.email,
password: this.password,
mobile: this.mobile,
roleUser:this.roleUser
}
I want to access the error message returned from my api if error exists for its corresponding input field. With what I have tried, all errors returned from the api shows below all input fields as in the image below.
Below are the parent and child vue components.
InputField.vue
<template>
<div class="form-group basic">
<div class="input-wrapper">
<label class="label" :for="name">{{label}}</label>
<input :type="type"
class="form-control"
:class="errorClassObject()"
:id="name" :placeholder="placeholder"
:v-model="value" #input="updateField">
<i class="clear-input">
<ion-icon name="close-circle" role="img" class="md hydrated" aria-label="close circle"></ion-icon>
</i>
</div>
<div v-text="errorMessage(name)" class="input-info text-danger">Error here</div>
</div>
</template>
<script>
export default {
name: "InputField",
props: [
'name', 'value', 'type', 'label', 'placeholder', 'errors'
],
data: function() {
return {
}
},
computed: {
hasError: function(){
return this.errors
//return this.errors.name throws an error name is undefined
}
},
methods: {
updateField (){
this.$emit('input', event.target.value)
},
errorMessage(name){
if(this.hasError){
return this.errors;
//return this.errors.name throws undefined name
}
},
errorClassObject (){
return{
'error-field': this.hasError
}
}
}
}
</script>
<style scoped>
</style>
ChangePassword.vue
<template>
<form #submit.prevent="onSubmit">
<form-input
required
name="current_password"
type="password"
label="Current Password"
:errors="errors"
placeholder="Enter current password"
v-model="passwordForm.current_password"
/>
<form-input .../>
<form-input.../>
<div class="form-button-group transparent">
<button type="submit" class="btn btn-primary btn-block btn-lg">Update Password</button>
</div>
</form>
</template>
Presently, if there are any errors from the request, the response is returned in the format below
You could get access to it using the square brackets like :
errorMessage(name){
if(this.hasError){
return this.errors[name];
}
},
this is the template I want to make a password input and checkbox while box is checked I want to type inside input to turn into the text instead of password
<!--template-->
<template>
<div>
<div id="login">
<h1 style="font-size:30px; color:black;">Login</h1>
<input type="text" placeholder="Username...">
<input :type="inputtype" placeholder="Password...">
<input type="checkbox" name="" id="" :checked="totext">
<button class="button is-link">Submit</button>
Register
Forgot the password..?!
</div>
</div>
</template>
17:5 error Unexpected labeled statement no-labels
17:16 error Expected an assignment or function call and instead saw an expression
<script>
export default {
data () {
inputtype: 'password'
},
methods: {
totext () {
this.inputtype = this.inputtype === 'password' ? 'text' : 'password'
}
}
}
</script>
Please try this code instead:
:type="totext ? 'password': 'text' "
<template>
<div>
<div id="login">
<h1 style="font-size:30px; color:black;">Login</h1>
<input type="text" placeholder="Username...">
<input :type="totext ? 'password': 'text' " placeholder="Password...">
<input type="checkbox" name="" id="" :checked="totext">
<button class="button is-link">Submit</button>
Register
Forgot the password..?!
</div>
</div>
</template>
And this script:
<script>
export default {
data () {
return {
totext : false
}
}
}
</script>
Or this one (the better one)
<template>
<div>
<div id="login">
<h1 style="font-size:30px; color:black;">Login</h1>
<input type="text" placeholder="Username...">
<input :type="isText" placeholder="Password...">
<input type="checkbox" name="" id="" :checked="totext">
<button class="button is-link">Submit</button>
Register
Forgot the password..?!
</div>
</div>
</template>
And
<script>
export default {
data () {
return {
totext : false
}
},
computed: {
isText(){
return totext ? 'password': 'text'
}
}
}
</script>
Please try this code :
data: () => ({
inputtype: 'password'
)}
You've merged the two sets of braces.
This:
data () {
inputtype: 'password'
},
should be this:
data () {
return {
inputtype: 'password'
}
},