Skip to content

Instantly share code, notes, and snippets.

@dantrevino
Forked from fanvyr/README.md
Created April 4, 2019 17:35
Show Gist options
  • Save dantrevino/4d8766fbcb4bc4c87cee8de79d593ac4 to your computer and use it in GitHub Desktop.
Save dantrevino/4d8766fbcb4bc4c87cee8de79d593ac4 to your computer and use it in GitHub Desktop.
A smooth registration form

Registration Form

Demo registration-demo

Notes

API check
The snippet tries to verify if the mail is okay or bad in general by reaching out to my/a backend.
Therefor the statuscode is important: return a 2xx for okay, and a 4xx with a message for not okay.

Error Display
The error will be handled via the store. So make sure to mount computed props / getters into the alert.

Validation
Validation is done in computed props too.

Google Autocomplete
The vuetify-google-autocomplete is included and ready to go.
I disabled it because of the insane increased pricing. (thanks to google btw...)

Language
Currently everything visible is in GERMAN.
var / method names are in english.

<template>
<v-container grid-list-md fluid>
<v-layout column>
<v-flex xs12 class="text-xs-center">
<h1>Registrieren</h1>
</v-flex>
<v-layout row wrap>
<v-flex xs10 sm6 md4 lg4 offset-xs1 offset-sm3 offset-md4 offset-lg4 mt-3>
<v-alert type="error" v-model="alert" dismissible transition="scale-transition">{{ error }}</v-alert>
</v-flex>
<v-slide-y-transition>
<v-flex
xs10
sm6
md4
lg4
offset-xs1
offset-sm3
offset-md4
offset-lg4
mt-4
v-show="animationShow"
>
<v-flex v-show="currentStep === 'mail'">
<v-text-field
ref="mailField"
name="mail"
label="Mail Adresse"
id="mail"
type="email"
:rules="[mailLength]"
v-model="newUser.mail"
:error="error !== null"
@keyup.enter="checkMail()"
required
></v-text-field>
<v-btn
@click="checkMail()"
block
flat
:loading="loading"
:disabled="loading || mailLength !== true"
color="primary"
>Weiter</v-btn>
</v-flex>
<v-flex v-show="currentStep === 'name'">
<v-text-field
ref="nameField"
name="surname"
label="Vorname"
id="surname"
type="text"
v-model="newUser.surname"
:rules="[surnameLength]"
></v-text-field>
<v-text-field
name="name"
label="Nachname"
id="name"
type="text"
v-model="newUser.name"
:rules="[nameLength]"
@keyup.enter="nextStep('address')"
></v-text-field>
<v-btn color="primary" block flat @click="nextStep('address')">Weiter</v-btn>
</v-flex>
<v-flex v-show="currentStep === 'address'">
<v-form @submit.prevent>
<!-- The google autocomplete was removed because of the crazy increased price... -->
<!-- <vuetify-google-autocomplete
ref="addressFocus"
id="map"
append-icon="location_on"
placeholder="Adresse"
@placechanged="addressChanged"
@keyup.enter="finishLocationStep"
></vuetify-google-autocomplete>-->
<v-layout row wrap>
<v-flex xs10>
<v-text-field
ref="addressField"
label="Straße"
v-model="newUser.address.street"
></v-text-field>
</v-flex>
<v-flex xs2>
<v-text-field label="Nr." type="number" v-model="newUser.address.street_number"></v-text-field>
</v-flex>
<v-flex xs4>
<v-text-field label="PLZ" type="number" v-model="newUser.address.plz"></v-text-field>
</v-flex>
<v-flex xs8>
<v-text-field label="Stadt" v-model="newUser.address.city"></v-text-field>
</v-flex>
<v-flex xs12>
<v-text-field label="Land" v-model="newUser.address.country"></v-text-field>
</v-flex>
</v-layout>
<v-btn
type="submit"
flat
block
color="primary"
:disabled="newUser.address.country === ''"
@click="finishLocationStep"
>Weiter</v-btn>
</v-form>
</v-flex>
<v-flex v-show="currentStep === 'password'">
<v-form @submit.prevent="sendRegistration">
<v-text-field
ref="passwordField"
name="password"
label="Passwort"
id="password"
type="password"
v-model="newUser.password"
:rules="[pwLength]"
loading
>
<template v-slot:progress>
<v-progress-linear :value="progress" :color="color" height="7"></v-progress-linear>
</template>
</v-text-field>
<v-text-field
name="conPassword"
type="password"
label="Passwort bestätigen"
id="conPassword"
@keyup.enter="sendRegistration"
v-model="confirmPassword"
:rules="[comparePasswords]"
></v-text-field>
<v-checkbox
color="primary"
v-model="agbs">
<template v-slot:label>
<p style="margin-bottom: 0px;">Ich habe die <a href="">AGB's</a> gelesen und stimme ausdrücklich zu.</p>
</template>
</v-checkbox>
<v-btn
@click.prevent="sendRegistration"
block
:disabled="loading || pwLength !== true || comparePasswords !== true || !agbs"
:loading="loading"
color="primary"
>Registrieren</v-btn>
</v-form>
</v-flex>
</v-flex>
</v-slide-y-transition>
</v-layout>
</v-layout>
</v-container>
</template>
<script>
import API from "../helper/API";
export default {
components: {
},
data() {
return {
alert: false,
animationShow: true,
currentStep: "",
newUser: {
mail: "",
surname: "",
name: "",
address: {
street: "",
street_number: "",
city: "",
plz: "",
country: "",
},
password: ""
},
agbs: false,
confirmPassword: ""
};
},
methods: {
sendRegistration() {
if (this.comparePasswords !== true || this.pwLength !== true || !this.agbs) return;
console.log("sending registration", this.newUser);
this.$store.dispatch("auth/register", this.newUser);
},
// JUST FOR USE WITH GOOGLE AUTOCOMPLETE API
addressChanged(e) {
if (e) {
this.newUser.address = {
street: e.route,
street_number: parseInt(e.street_number),
city: e.locality,
plz: parseInt(e.postal_code),
country: e.country
};
}
},
finishLocationStep() {
if (this.newUser.country === "") return;
this.nextStep("password");
},
// check if the entered mail is already in use with an api call
async checkMail() {
this.$store.commit("auth/setLoading", true);
// Write here your own api call
API.global.user
.checkIfMailIsInUse(this.newUser.mail)
.then(res => {
this.alert = false;
this.nextStep("name");
})
.catch(err => {
this.$store.commit("auth/setError", err.response.data);
})
.finally(() => {
this.$store.commit("auth/setLoading", false);
});
},
nextStep(nextStep) {
this.animationShow = false;
setTimeout(() => {
this.animationShow = true;
}, 500);
setTimeout(() => {
this.currentStep = nextStep;
}, 240);
}
},
computed: {
progress() {
// thoughts about a more complex pw validation progress bar:
// (currently just checks the .length )
// is a number in there?
// is a special char in there?
return Math.min(100, this.newUser.password.length * 15);
},
// color for the pw progress bar
color() {
return ["error", "warning", "success"][Math.floor(this.progress / 40)];
},
comparePasswords() {
return this.confirmPassword === this.newUser.password
? true
: "Passwörter stimmen nicht überein...";
},
pwLength() {
return this.newUser.password.length >= 6
? true
: "Mindestens 6 Zeichen erforderlich";
},
nameLength() {
return this.newUser.name.length >= 2 ? true : "Bitte ausfüllen";
},
surnameLength() {
return this.newUser.surname.length >= 2 ? true : "Bitte ausfüllen";
},
mailLength() {
return this.newUser.mail.length >= 5 ? true : "Mindestens 5 Zeichen";
},
// the loading state will be handled globally via the store
loading() {
return this.$store.getters["auth/getLoading"];
},
error() {
return this.$store.getters["auth/getError"];
}
},
watch: {
error(value) {
if (value) this.alert = true;
},
alert(value) {
if (!value) this.$store.commit("auth/setError", null);
},
currentStep(value) {
setTimeout(() => {
this.$refs[`${value}Field`].focus();
}, 350);
}
},
created() {
this.nextStep("mail");
},
mounted() {}
};
</script>
<style scoped>
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment