I have several component that I use in a component called QuickButton.vue:
<Tabs>
<TabPanels>
<TabPanel><EmployeesMainData/></TabPanel>
<TabPanel><ContactAddressData/></TabPanel>
<TabPanel><EmploymentData/></TabPanel>
<TabPanel><AdditionalData/></TabPanel>
</TabPanels>
<div>
<button type="submit" #click="signUp">Save</button>
</div>
</Tabs>
this is EmployeeMainData code:
<template>
<div class="col-span-6 sm:col-span-4">
<label for="email-address" class="block text-sm font-medium text-gray-700">Email address</label>
<input id="email-address" v-model="email" type="email" autocomplete="email" required
class="appearance-none rounded-md relative block w-full my-4 px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Email address">
<p class="mt-2 text-sm text-gray-500">*Required</p>
</div>
<div class="col-span-6 sm:col-span-4 mb-2">
<label for="password" class="block text-sm font-medium text-gray-700">Password</label>
<input id="password" v-model="password" type="text" required
class="appearance-none rounded-md relative block w-full my-4 px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Password">
<p class="mt-2 text-sm text-gray-500">*Required</p>
<p class="mt-2 text-sm text-gray-500">*Password is used for employee to login into their Android app. Save and share the password to the employee.</p>
</div>
</template>
How do I access email and password input using ref and script setup from QuickButton.vue? I already trying using defineExpose but didnt work
This chapter in the Vue docs contains information on v-model and using it with form input: https://vuejs.org/guide/essentials/forms.html. (Btw, the Vue docs are amazing! If you want to level up fast, review it a chapter or two at a time :).
I have a multi-step form. This is the first step in the form. Using Vee-Validate, I am trying to figure out how I would prevent the next button from being clickable before the form is valid. This is Fee-Validate 4 Vue 3
FORM:
<Form action="#" #submit="submit" method="POST" class="overflow-hidden space-y-6 pt-3">
<div v-if="formStep == 1">
<div>
<div class="relative border border-gray-500 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-blue-600 focus-within:border-blue-600 ">
<label for="name" value="Name" class="absolute -top-2 left-2 -mt-px inline-block px-1 bg-gray-900 text-sm font-medium text-gray-50">Username</label>
<Field
#keydown.space.prevent
type="text"
autocomplete="username"
name="name"
id="name"
v-model="form.name"
:rules="validateUsername"
required
autofocus
class="bg-gray-900 text-white block w-full border-0 p-0 placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder=""
/>
</div>
<ErrorMessage name="name" class="text-red-500 mt-2" />
</div>
<div class="mt-6">
<div class="relative border border-gray-500 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-blue-600 focus-within:border-blue-600">
<label for="email" value="Email" class="absolute -top-2 left-2 -mt-px inline-block px-1 bg-gray-900 text-sm font-medium text-gray-50">Email Address</label>
<Field
id="email"
name="email"
type="email"
autocomplete="email"
v-model="form.email"
:rules="validateEmail"
required
autofocus
class="bg-gray-900 text-white block w-full border-0 p-0 placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder=""
/>
</div>
<ErrorMessage name="email" class="text-red-500 mt-2" />
</div>
<div class="mt-6">
<div class="relative border border-gray-500 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-blue-600 focus-within:border-blue-600">
<label for="password" value="Password" class="absolute -top-2 left-2 -mt-px inline-block px-1 bg-gray-900 text-sm font-medium text-gray-50">Password</label>
<Field
id="password"
name="password"
type="password"
v-model="form.password"
required
autocomplete="new-password"
:rules="validatePassword"
validateOnInput
class="bg-gray-900 text-white block w-full border-0 p-0 placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder=""
/>
</div>
<ErrorMessage name="password" class="text-red-500 mt-2" />
</div>
<div class="mt-6">
<div class="relative border border-gray-500 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-blue-600 focus-within:border-blue-600">
<label for="password_confirmation" value="Confirm Password" class="absolute -top-2 left-2 -mt-px inline-block px-1 bg-gray-900 text-sm font-medium text-gray-50">Confirm Password</label>
<Field
id="password_confirmation"
name="password_confirmation"
type="password"
v-model="form.password_confirmation"
required
autocomplete="new-password"
class="bg-gray-900 text-white block w-full border-0 p-0 placeholder-gray-500 focus:ring-0 sm:text-sm"
placeholder=""
/>
</div>
<div v-if="form.password != form.password_confirmation">
<p class="text-sm text-red-500 mt-2">Passwords do not match</p>
</div>
</div>
</div>
<div>
<div v-if="formStep == 1">
<button type="button" #click="[nextStep(), locatorButtonPressed(), getStreetAddressFrom(this.form.user_latitude, this.form.user_longitude)] " class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">Next</button>
</div>
</Form>
Where I am not sure is do I create a new method for this, or does vee-validate have some function I am not understanding within their documentation. Is there a reason to use Yup to make this work or am I just off the ball on this one altogether?
Also, Is there a way to make the button gray until the form is valid. I am using Tailwind 3
I am trying to create a simple survey builder with Vue3 and Vue Draggable. All is going well until I try to create a multiple choice question. This type of questions has its own sortable list of possible answers. When I add another multiple choice question it then pulls from the same list which makes sense but I have tried to use a v-if to check the parents ID to match the choice ID..
Basically if I add a new choice it adds to the all multiple choice questions, which makes sense, but how to I keep it to the current item I am in?
Any ideas? I know the code is mess, it will be refactored once it works.
<template>
<div class="p-4">
<div class="container mx-auto rounded-md grid grid-cols-12 gap-4 h-full">
<div class="col-span-4 p-3 flex flex-col min-h-screen bg-gray-200 shadow-md rounded-md">
<div class="text-lg font-bold w-full bg-blue-600 text-white rounded-md p-2 mb-4">Builder your survey</div>
<div class="sticky top-4">
<div class="text-lg font-bold">Components</div>
<draggable
class="p-2 rounded-md"
:list="componentsList"
:group="{ name: 'questions', pull: 'clone', put: false, sort: false }"
:clone="cloneItem"
sort: false
#change="log"
item-key="id"
>
<template #item="{ element }">
<div
class="bg-gray-300 p-4 rounded-md mt-2 shadow-sm hover:shadow-md cursor-pointer border border-blue-800 border-dashed"
>
{{ element.name }}
</div>
</template>
</draggable>
</div>
</div>
<div class="col-span-8 flex p-3 flex-col bg-white shadow-md rounded-md">
<div class="text-lg font-bold pt-4">Survey</div>
<draggable
class="w-full h-full border border-blue-400 rounded-md p-2 flex flex-col flex-1"
:list="questionsList"
group="questions"
#change="log"
handle=".handle"
itemKey="name + index"
>
<template #item="{ element, index }">
<div>
<div v-if="element.name == 'Single Line of Text'" class="bg-gray-300 p-2 rounded-md mt-2 mb-2 shadow-lg hover:shadow-md">
<div class="w-full text-sm text-left">{{ element.name }} {{element.id}}</div>
<div class="flex justify-between items-center p-2 bg-gray-200 rounded-md mb-4">
<div class="w-10 font-bold hidden">Q{{ index + 1 }}</div>
<div class="w-full pr-4">
<input
type="text"
class="w-full p-2 bg-transparent flex-grow"
placeholder="Question title here..."
v-model="element.text"
/>
</div>
<div class="flex ">
<div class="cursor-pointer">
<i class="handle las la-arrows-alt la-2x mr-2"></i>
</div>
<div #click="remove(index)">
<i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
</div>
</div>
</div>
<div>
<input
type="text"
class="w-full p-2 rounded-md border border-gray-400"
placeholder="User response will go here"
/>
</div>
<div class="text-left flex justify-between items-center p-2 bg-gray-200 rounded-md mt-2">
<div class="flex items-center">
<div><input type="checkbox" class="mr-2" /></div>
<div>Required?</div>
</div>
</div>
</div>
<!-- START problem area -->
<div v-else-if="element.name == 'Multiple Choice'" class="bg-gray-300 p-2 rounded-md mt-2 mb-2 shadow-lg hover:shadow-md">
<div class="w-full text-sm text-left">{{ element.name }} {{element.id}}</div>
<div class="flex justify-between items-center p-2 bg-gray-200 rounded-md mb-4">
<div class="w-10 font-bold hidden">Q{{ index + 1 }}</div>
<div class="w-full pr-4">
<input
type="text"
class="w-full p-2 bg-transparent flex-grow"
placeholder="Question title here..."
v-model="element.text"
/>
</div>
<div class="flex ">
<div class="cursor-pointer">
<i class="handle las la-arrows-alt la-2x mr-2"></i>
</div>
<div #click="remove(index)">
<i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
</div>
</div>
</div>
<div class="flex items-center ">
<draggable
class="p-2 rounded-md w-full"
:list="multipleChoiceList"
:group="{ name: 'choice', pull: false, put: false, sort: true }"
sort: true
handle=".handle"
#change="log"
item-key="question"
>
<template #item="{ element }">
<div
class="bg-blue-100 p-4 flex items-center justify-start rounded-md mt-2 shadow-sm hover:shadow-md cursor-pointer w-full"
>
<div class="flex items-center flex-grow"
>
<input type="checkbox" class="w-6 h-6">
<input
type="text"
class="p-2 bg-transparent flex-grow"
placeholder="Add choice here"
v-model="element.text"
/>
</div>
<div class="flex ">
<div class="cursor-pointer">
<i class="handle las la-arrows-alt la-1x mr-2"></i>
</div>
<div #click="remove(index)">
<i class="las la-trash-alt text-red-800 la-1x cursor-pointer"></i>
</div>
</div>
</div>
</template>
<template #footer>
<div>
<button class="p-2 bg-blue-300 mt-2 rounded-md" #click="addChoice(element.id)">Add</button>
</div>
</template>
</draggable>
</div>
<div class="text-left flex justify-between items-center p-2 bg-gray-200 rounded-md mt-2">
<div class="flex items-center">
<div><input type="checkbox" class="mr-2" /></div>
<div>Required?</div>
</div>
</div>
</div>
<!-- END problem area -->
<div v-else-if="element.name == 'Open Ended'" class="bg-gray-300 p-2 rounded-md mt-2 mb-2 shadow-lg hover:shadow-md">
<div class="w-full text-sm text-left">{{ element.name }} {{element.id}}</div>
<div class="flex justify-between items-center p-2 bg-gray-200 rounded-md mb-4">
<div class="w-10 font-bold hidden">Q{{ index + 1 }}</div>
<div class="w-full pr-4">
<input
type="text"
class="w-full p-2 bg-transparent flex-grow"
placeholder="Question title here..."
v-model="element.text"
/>
</div>
<div class="flex ">
<div class="cursor-pointer">
<i class="handle las la-arrows-alt la-2x mr-2"></i>
</div>
<div #click="remove(index)">
<i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
</div>
</div>
</div>
<div>
<textarea
class="h-32 w-full w-full p-2 rounded-md border border-gray-400"
></textarea>
</div>
<div class="flex items-center">
<div>Max Length</div>
<div>
<input
type="number"
class="mr-2 w-20 border border-gray-400 p-2 rounded-md ml-2"
/>
</div>
</div>
<div class="text-left flex justify-between items-center p-2 bg-gray-200 rounded-md mt-2">
<div class="flex items-center">
<div><input type="checkbox" class="mr-2" /></div>
<div>Required?</div>
</div>
</div>
</div>
<div v-else-if="element.name == 'Divider'">
<div class="flex items-center">
<div class="flex-grow border-t border-black mx-4"> </div>
<div class="flex ">
<div class="cursor-pointer">
<i class="handle las la-arrows-alt la-2x mr-2"></i>
</div>
<div #click="remove(index)">
<i class="las la-trash-alt text-red-800 la-2x cursor-pointer"></i>
</div>
</div>
</div>
</div>
</div>
</template>
</draggable>
</div>
</div>
</div>
</template>
See Clone method which assigns a random number as the id
<script>
import draggable from "vuedraggable";
export default {
name: "Survey",
components: {
draggable,
},
data() {
return {
drag: false,
componentsList: [
{ name: "Single Line of Text", type: "question", text: "", id: 1 },
{ name: "Multiple Choice", type: "question", text: "", id: 2 },
{ name: "Matrix", type: "question", text: "", id: 3 },
{ name: "Open Ended", type: "question", text: "", id: 4 },
{ name: "Divider", type: "component", id: 9 },
],
questionsList: [],
multipleChoiceList: [
{text: "text A", type:"choice", question:"32"},
{text: "text B", type:"choice", question:"1"},
{text: "text A", type:"choice", question:"2"} ]
};
},
methods: {
onEnd: function(evt) {
console.log(evt);
},
log: function(evt) {
console.log(evt);
},
addChoice(id) {
this.multipleChoiceList.push({ text: "Choice " + id, type: "choice", question:id });
console.log(this.multipleChoiceList);
},
remove(index) {
this.questionsList.splice(index, 1);
},
cloneItem({ id, name, type }) {
return {
name: name,
id: Math.ceil(Math.random()*100),
text: "",
type: type,
};
}
},
mounted() {
// console.log("mounted");
},
};
</script>
https://github.com/SortableJS/Vue.Draggable/issues/687#issuecomment-1153083717
I just answered to a similar question on github.
Not sure would this help as Vue.Draggable and vue.draggable.next is slightly different.
I am using the following code as a Vue component in order to register it in the root component.
const RegisterForm = {
data() {
return {
test: 'bonjour',
user: {
email: '',
firstName: 'test',
lastName: '',
password: '',
passwordConfirm: '',
terms: false,
receiveUpdates: false
}
};
},
methods: {
handleSubmit() {
const data = this.user;
if (!isFormValid()) {
return;
}
},
template: `
<div>
<form class="flex flex-col mt-2" #submit.prevent="handleSubmit()">
<label class="text-xs block mt-6 mb-3" for="firstName">
First Name
</label>
<input v-model="user.firstName" required type="text" class="text-register border-b focus:border-pink font-MuseoSans-medium outline-none pb-4 text-lg focus:outline-none w-full" name="firstName" id="firstName" value="">
<label class="text-xs block mt-6 mb-3" for="lastName">
Last Name
</label>
<input v-model="user.lastName" required type="text" class="text-register border-b focus:border-pink font-MuseoSans-medium outline-none pb-4 text-lg focus:outline-none w-full" name="lastName" id="lastName" value="">
<label class="text-xs block mt-6 mb-3" for="emailAdress">
Email Address
</label>
<input v-model="user.email"required type="text" class="text-register border-b focus:border-pink font-MuseoSans-medium outline-none pb-4 text-lg focus:outline-none w-full" name="emailAdress" id="emailAdress" value="">
<label class="text-xs block mt-6 mb-3" for="password">
Password
</label>
<input v-model="user.password" placeholder="Set password for your account" required type="password" class="text-register border-b focus:border-pink font-MuseoSans-medium outline-none pb-4 text-lg focus:outline-none w-full" name="password" id="password" value="">
<label for="confirmTerms" class="cursor-pointer select-none flex items-start mt-8 mb-3 text-xs leading-none">
<input v-model="user.terms" type="checkbox" name="confirmTerms" id="confirmTerms" checked class="mr-3 inline-block">
<span class="flex-auto leading-normal -mt-1">Check this box to agree to our <a class="texspt-pink no-underline" href="#">Terms of Use</a>, <a class="text-pink no-underline" href="#">Privacy Policy</a> and consent to us storing your name and email address as highlighted in our <a class="text-pink no-underline" href="#">GDPR compliance</a>.</span>
</label>
<label for="receiveUpdates" class="cursor-pointer select-none flex items-start mt-2 mb-8 text-xs leading-none">
<input v-model="user.receiveUpdates" type="checkbox" name="receiveUpdates" id="receiveUpdates" class="mr-3 inline-block">
<span class="flex-auto leading-normal -mt-1">We would like to send you emails with tips to help you get started and details on new features.</span>
</label>
<button type="submit" class="bg-pink hover:bg-pink-dark flex-none text-white px-4 py-6 rounded text-lg font-MuseoSans-medium" name="button">Sign up</button>
</form>
</div>
`
};
This renders fine and works as a form, however I have been trying to add an error section to the top. When ever I add any HTML to the top of this template code (i.e. between the <div> and the <form>) it breaks the whole thing and it doesn't render.
This is because you have an end label tag which has no start tag.
<div>
<form class="flex flex-col mt-2" #submit.prevent="handleSubmit()">
<label class="text-xs block mt-6 mb-3" for="firstName">First Name</label>
<input v-model="user.firstName" required type="text" class="[...]" name="firstName" id="firstName" value="">
<label class="text-xs block mt-6 mb-3" for="firstName">Last Name</label>
<input v-model="user.lastName" required type="text" class="[...]" name="lastName" id="lastName" value="">
<label class="text-xs block mt-6 mb-3" for="emailAdress">Email Adress</label>
<input v-model="user.email"required type="text" class="[...]" name="emailAdress" id="emailAdress" value="">
<label class="text-xs block mt-6 mb-3" for="password">Password</label>
<input v-model="user.password" placeholder="[...]" required type="password" class="[...]" name="password" id="password" value="">
<input v-model="user.receiveUpdates" type="checkbox" name="receiveUpdates" id="receiveUpdates" class="mr-3 inline-block">
<span class="flex-auto leading-normal -mt-1">[...]</span>
</label <!-- Just here -->
<button type="submit" class="[...]" name="button">Sign up</button>
</form>
</div>
so I'm attempting to use the Twitch API to enable users to sign in using the Twitch API, however, I'm running into some issues
Here is the code I have so far
<template>
<section>
<form class="pt-6 pb-8 animated fadeIn" style="outline:none" #submit.prevent="signIn">
<section class="mb-4">
<label class="block text-grey-darker text-sm font-bold mb-2" for="email">
Email
</label>
<input class="shadow hover:shadow-md bg-transparent appearance-none border-2 border-grey rounded w-full py-2 px-3 text-grey-darker"
style="outline:none" type="email" v-model="email" placeholder="demo#example.co.uk">
</section>
<section class="mb-6">
<label class="block text-grey-darker text-sm font-bold mb-2" for="password">
Password
</label>
<input class="shadow hover:shadow-md bg-transparent appearance-none border-2 border-grey rounded w-full py-2 px-3 text-grey-darker"
style="outline:none" type="password" v-model="password" placeholder="********">
</section>
<section class="flex items-center justify-between">
<button class="bg-grey-darker hover:bg-grey-darkest w-full text-white font-semibold py-3 px-4 border rounded-sm shadow" style="outline:none">
Sign In
</button>
</section>
<a class="flex justify-center pt-6 pb-px font-semibold text-sm text-grey hover:text-green-darker" href="#sign-up">
Create an account
</a>
<a class="flex justify-center font-semibold text-sm text-grey hover:text-green-darker" href="#forgot-password">
Forgot Password?
</a>
</form>
<button class="twitch flex justify-center absolute pin-l items-center p-8 uppercase text-white font-semibold tracking-wide w-full" #click="twitch">
<i class="fab fa-twitch pr-2 text-xl"></i> Sign in with Twitch
</button>
</section>
</template>
<script>
import firebase from '#/middleware/firebase'
import axios from 'axios'
export default {
data: function () {
return {
email: '',
password: ''
}
},
methods: {
signIn () {
firebase.auth().signInWithEmailAndPassword(this.email, this.password),
console.log('Signed in with ' + this.email)
},
twitch: function () {
return axios.get(
'https://id.twitch.tv/oauth2/authorize?response_type=code&client_id=<KEY>&redirect_uri=http://localhost:3000&scope=chat_login'
).then(res => {
console.log(res)
})
}
}
}
</script>
I'm not sure what I'm doing wrong here, at the moment I just want to console log the response then I'll go from there.
Any help is appreciated!
Your problem is about CORS (see https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
You are try to reach a resource from a domain to another domain, which is only possible if expicitly allowed.