<template>
<form
id="basic-signup-form"
@submit.prevent="submitHandler"
>
<h2>Sign up for FormulateConf 2020 in Charlottesville, VA</h2>
<div class="form-input">
<label for="username">
Username
</label>
<input
id="username"
ref="username"
@keyup="checkUsername"
type="text"
name="username"
pattern="[a-z]{4,8}"
required
/>
<p class="help-text">
Usernames must be lowercase and between 4-8 characters in length.
</p>
<p
v-if="usernameTaken"
class="error"
>
Sorry, that username is already taken.
</p>
</div>
<div class="form-input">
<label for="email">
Email
</label>
<input
id="email"
type="email"
name="email"
required
/>
</div>
<div class="form-input">
<label for="bio">
Bio
</label>
<textarea
id="bio"
name="bio"
required
/>
<p class="help-text">
Help others get to know you a bit.
</p>
</div>
<div class="form-input">
<label for="password">
Password
</label>
<input
id="password"
ref="password"
@keyup="passwordsMatch = true"
@blur="comparePasswords"
type="text"
name="password"
minlength="6"
autocomplete="off"
required
/>
<p class="help-text">
Your password must be at least 6 characters long.
</p>
</div>
<div class="form-input">
<label for="password-confirm">
Confirm Password
</label>
<input
id="password-confirm"
ref="passwordConfirm"
@keyup="passwordsMatch = true"
@blur="comparePasswords"
type="text"
name="password-confirm"
minlength="6"
autocomplete="off"
required
/>
<p
v-if="!passwordsMatch"
class="error"
>
Oops! Your passwords do not match.
</p>
</div>
<input type="submit" value="Register!" />
</form>
</template>
<script>
export default {
data () {
return {
takenNamePartials: [
'dawg',
'js',
'boss',
'oy'
],
usernameTaken: false,
passwordsMatch: true
}
},
computed: {
hasErrors () {
return this.usernameTaken || !this.passwordsMatch
}
},
methods: {
submitHandler () {
if (!this.hasErrors) {
alert('Thank you for signing up!')
} else {
alert('There are errors with your form submission.')
}
},
async checkUsername () {
// reset the usernameTaken variable to false
// when the user starts typing
this.usernameTaken = false
// check to see if the username contains any of
// our partial matches to simulate taken usernames.
// wrap it in a promise and timeout to simulate an xhr request.
let match = false
await new Promise((resolve) => {
setTimeout(() => {
// use .find() to check for matches and bail on first success
match = this.takenNamePartials.find(partial => this.$refs.username.value.includes(partial))
resolve()
}, 2500)
})
// set usernameTaken to the cooerced Boolean value of match
this.usernameTaken = !!match
},
comparePasswords () {
// if both inputs have been filled out then compare their values and store
// the comparison result in this.passwordsMatch
if (this.$refs.password.value && this.$refs.passwordConfirm.value) {
this.passwordsMatch = this.$refs.password.value === this.$refs.passwordConfirm.value
}
}
}
}
</script>
<style lang="scss">
body {
min-height: 100vh;
padding: 2em;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(to bottom right, cyan, magenta);
}
form {
display: flex;
flex-direction: column;
background-color: #fff;
border: 1px solid #999;
width: 90%;
max-width: 320px;
padding: 3em 2em;
border-radius: 0.3em;
box-shadow: 0.25em 0.15em 12em 0 rgba(#000, 0.25);
* {
box-sizing: border-box;
}
h2 {
margin-top: 0;
color: #555;
}
}
.form-input {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
margin-bottom: 1em;
input, textarea {
width: 100%;
margin-bottom: 0;
border: 1px solid #cacaca;
padding: 0.5em;
border-radius: 0.25em;
}
label {
font-weight: bold;
font-size: 0.85em;
margin-bottom: 0.5em;
}
.help-text {
margin-top: 0.5em;
margin-bottom: 0;
color: #999;
font-size: 0.75em;
}
.error {
color: red;
font-size: 0.8em;
}
}
input[type="submit"] {
appearance: none;
border: 1px solid deeppink;
background-color: magenta;
padding: 1em 1.5em;
font-weight: bold;
color: #fff;
text-transform: uppercase;
border-radius: 0.5em;
margin: 1em auto 0 auto;
}
</style>
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.