Next.js Handling User Logged In Status - express

I'm using next-iron-session in my next.js project with discord oauth2.
My Session Code:
import {
GetServerSidePropsContext,
NextApiRequest,
NextApiResponse,
} from "next";
import { withIronSession, Session } from "next-iron-session";
export type NextIronRequest = NextApiRequest & { session: Session };
export type NextIronHandler = (
req: NextIronRequest,
res: NextApiResponse
) => void | Promise<void>;
export type NextRoute = (
ctx: GetServerSidePropsContext & { req: { session: Session } },
redirect: string
) => any;
export function withSession(handler: NextIronHandler | NextRoute) {
return withIronSession(handler, {
password: process.env.COOKIE_SECRET as string,
cookieName: "session",
ttl: 15 * 24 * 3600,
cookieOptions: {
secure: process.env.NODE_ENV === "production",
sameSite: "strict",
httpOnly: true,
},
});
}
Auth Handler Code:
import next, { GetServerSidePropsContext } from 'next'
import { withSession } from './session'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/api/auth/login',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSession(withAuth(handler))
Dashboard Code:
import { GetServerSideProps, NextApiRequest } from "next";
import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { NextIronRequest } from "../util/session";
import { User, UserData } from "src/types";
import { developerRoute } from "src/util/redirects";
import { withSession } from "src/util/session";
import Router from "next/router";
import { redirect } from "next/dist/server/api-utils";
import { withAuthSsr } from '../util/AuthHandler'
interface Props {
user?: User;
}
export default function Dashboard(req: NextIronRequest) {
function Icon(icon: string) {
console.log(icon)
}
return (
<>
<Head>
<title>{req.user.user.username} | Dashboard</title>
<link rel="shortcut icon" href={req.user.avatar} />
</Head>
<body>
{
req.user.guilds.map((guild: any) => {
return (
<>
<div className="p-10 items-center justify-center flex flex-center grid grid-cols-2 grid-flow-row sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-3 gap-5">
<div className="rounded overflow-hidden shadow-lg">
<img className="w-full" src={Icon(guild.icon)} alt="Guild Icon" />
<div className="px-6 py-4">
<div className="font-bold text-center text-xl mb-2">{guild.name}</div>
</div>
<div className="px-6 pt-4 pb-2 items-center content-center justify-center">
<a href="#_" className="relative inline-flex items-center justify-center p-4 px-6 py-3 overflow-hidden font-medium text-indigo-600 transition duration-300 ease-out border-2 border-purple-500 rounded-full shadow-md group">
<span className="absolute inset-0 flex items-center justify-center w-full h-full text-white duration-300 -translate-x-full bg-purple-500 group-hover:translate-x-0 ease">
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path></svg>
</span>
<span className="absolute flex items-center justify-center w-full h-full text-purple-500 transition-all duration-300 transform group-hover:translate-x-full ease">Manage Settings</span>
<span className="relative invisible">Manage Settings</span>
</a>
</div>
</div>
</div>
</>
)
})
}
</body>
</>
)
}
export const getServerSideProps: GetServerSideProps = withSession(withAuthSsr(developerRoute))
What's happening now is, if the user is even loggedIn it will redirect for login again, How do i fix that?
What did i do wrong? If the user isn't logged he/she gets redirected.
Next.js Version: 12.0.7
Next-Iron-Session Version: 4.2.0

Related

Cloudinary Image Upload

I am using Cloudinary with NextJS. When I am trying to specify the folder name in sign.js, which will create a signature for my request, I am getting an "Unauthorized" error as a response. But without a folder name, it works fine.
sign.js - This is the file responsible for creation of signature.
const cloudinary = require("cloudinary").v2;
const sign = async (req, res) => {
const timestamp = Math.round((new Date()).getTime() / 1000);
const signature = cloudinary.utils.api_sign_request({
timestamp: timestamp,
folder: 'product'
}, process.env.NEXT_PUBLIC_CLOUDINARY_SECRET);
res.statusCode = 200;
res.json({ signature, timestamp });
};
export default sign;
index.js - This is the main file.
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import { useState } from 'react';
export default function Home() {
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const [file, setFile] = useState();
const changeHandler = (event) => {
console.log(event.target.files[0]);
setSelectedFile(event.target.files[0]);
setFile(URL.createObjectURL(event.target.files[0]))
setIsFilePicked(true);
};
const handleSubmission = async () => {
const { signature, timestamp } = await getSignature();
const formData = new FormData();
const url = `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload`
formData.append('file', selectedFile)
formData.append("signature", signature);
formData.append("timestamp", timestamp);
formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_KEY);
fetch(url, {
method: 'POST',
body: formData
}).then((response) => response.json())
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error('Error:', error);
});
}
return (
<div className={styles.container}>
<Head>
<title>Tailwind-Next</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className={styles.main}>
<div className="flex items-center justify-center w-[50%] mb-5">
{selectedFile == null ?
<label htmlFor="dropzone-file" className={`flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 hover:bg-gray-100 ${styles.dropzone}`}>
<div className="flex flex-col items-center justify-center pt-5 pb-6">
<svg aria-hidden="true" className="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400"><span className="font-semibold">Click to upload</span> or drag and drop</p>
<p className="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p>
</div>
<input id="dropzone-file" type="file" className="hidden" accept="image/*" onChange={changeHandler} />
</label> :
<div className='flex flex-col items-center justify-center w-full h-64 relative'>
<div className={styles.image_wrapper}>
<img src={file} className={styles.img_preview} alt="" />
</div>
</div>}
</div>
<button className="w-[50%] bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-600 rounded" onClick={handleSubmission}>
Upload
</button>
</div>
</div>
)
}
async function getSignature() {
const response = await fetch("/api/sign");
const data = await response.json();
const { signature, timestamp } = data;
return { signature, timestamp };
}
I tried creating a signature without a folder name, and it worked fine. However, when I attempted to create a signature using the folder name, it returned unauthorized. 
After 13 bad requests and 31 unauthorized requests, I was finally able to resolve the issue.
sign.js - This is the file responsible for creation of signature.
const cloudinary = require("cloudinary").v2;
const sign = async (req, res) => {
const timestamp = Math.round((new Date()).getTime() / 1000);
const signature = cloudinary.utils.api_sign_request({
timestamp: timestamp,
folder: 'product'
}, process.env.NEXT_PUBLIC_CLOUDINARY_SECRET);
res.statusCode = 200;
res.json({ signature, timestamp });
};
export default sign;
index.js - This is the main file.
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import { useState } from 'react';
export default function Home() {
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const [file, setFile] = useState();
const changeHandler = (event) => {
console.log(event.target.files[0]);
setSelectedFile(event.target.files[0]);
setFile(URL.createObjectURL(event.target.files[0]))
setIsFilePicked(true);
};
const handleSubmission = async () => {
const { signature, timestamp } = await getSignature();
const formData = new FormData();
const url = `https://api.cloudinary.com/v1_1/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/upload`
formData.append('file', selectedFile)
formData.append("signature", signature);
formData.append("timestamp", timestamp);
formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_KEY);
formData.append("folder", "product")
fetch(url, {
method: 'POST',
body: formData
}).then((response) => response.json())
.then((result) => {
console.log(result);
})
.catch((error) => {
console.error('Error:', error);
});
}
return (
<div className={styles.container}>
<Head>
<title>Tailwind-Next</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className={styles.main}>
<div className="flex items-center justify-center w-[50%] mb-5">
{selectedFile == null ?
<label htmlFor="dropzone-file" className={`flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:hover:bg-bray-800 hover:bg-gray-100 ${styles.dropzone}`}>
<div className="flex flex-col items-center justify-center pt-5 pb-6">
<svg aria-hidden="true" className="w-10 h-10 mb-3 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400"><span className="font-semibold">Click to upload</span> or drag and drop</p>
<p className="text-xs text-gray-500 dark:text-gray-400">SVG, PNG, JPG or GIF (MAX. 800x400px)</p>
</div>
<input id="dropzone-file" type="file" className="hidden" accept="image/*" onChange={changeHandler} />
</label> :
<div className='flex flex-col items-center justify-center w-full h-64 relative'>
<div className={styles.image_wrapper}>
<img src={file} className={styles.img_preview} alt="" />
</div>
</div>}
</div>
<button className="w-[50%] bg-blue-600 hover:bg-blue-500 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-600 rounded" onClick={handleSubmission}>
Upload
</button>
</div>
</div>
)
}
async function getSignature() {
const response = await fetch("/api/sign");
const data = await response.json();
const { signature, timestamp } = data;
return { signature, timestamp };
}

Vue 3 Vuelidate not working with component

I am using vue 3 / typescript . I have 2 forms one is for patient registration and other is for patient visits both forms required validation on some fields. I am using vuelidate to validate both forms. I am have applied validation on both forms patient and visit . patient form is a component and i injected it inside the visit page .The problem is when i add patient component inside visit page vailidation on visit page get fails. but then i comment out the patient compoment on visit page it works fine. both forms are independent form each other.
Here is the patient registration component
<template>
<Dialog
id="previewReceiptDailog"
v-model:visible="productDialog"
:style="{ width: '60vw' }"
position="top"
class="p-fluid"
:modal="true"
:closable="true"
#hide="closeDialog"
>
<template #header>
<h6 class="p-dialog-titlebar p-dialog-titlebar-icon">
<i class="pi pi-plus-circle"></i> {{ dialogTitle }}
</h6>
</template>
<div class="p-grid">
<div class="p-col-6">
<div class="p-field">
<label
for="firstName"
:class="{
'p-error': v$.firstName.$invalid && submitted
}"
>First Name</label
>
<InputText
id="firstName"
v-model="v$.firstName.$model"
:class="{
'p-invalid': v$.firstName.$invalid && submitted
}"
/>
<small
v-if="
(v$.firstName.$invalid && submitted) ||
v$.firstName.$pending.$response
"
class="p-error"
>{{
v$.firstName.required.$message.replace(
"Value",
"First Name"
)
}}</small
>
</div>
</div>
</div>
<template #footer>
<Button
type="button"
label="Save"
icon="pi pi-check-circle"
class="p-button-primary pull-left"
#click.prevent="saveItem(!v$.$invalid)"
/>
</template>
</Dialog>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import { reactive } from "vue";
import { required } from "#vuelidate/validators";
import useVuelidate from "#vuelidate/core";
#Options({
props: {
PatientRegistration: Object
},
watch: {
PatientRegistration(obj) {
this.openDialog();
this.dialogTitle = obj.dialogTitle;
this.productDialog = obj.status;
if (obj.patientID != 0) {
this.loadPatient(obj.patientID);
}
}
},
emits: ["updatePatientStatus"]
})
export default class PatientDialog extends Vue {
private submitted = false;
private state = reactive({
firstName: "",
});
private validationRules = {
firstName: {
required
},
};
private v$ = useVuelidate(this.validationRules, this.state);
//DEFAULT METHOD OF TYPE SCRIPT
//CALLING WHENEVER COMPONENT LOADS
created() {
this.toast = new Toaster();
this.patientService = new PatientService();
}
saveItem(isFormValid) {
this.submitted = true;
if (isFormValid) {
//DO SOMETHING
}
}
}
</script>
Here is the visit page where i injeceted the component of patient
<template>
<section>
<div class="app-container">
<Dialog
v-model:visible="productDialog"
:style="{ width: '50vw' }"
:maximizable="true"
position="top"
class="p-fluid"
>
<template #header>
<h5 class="p-dialog-titlebar p-dialog-titlebar-icon">
{{ dialogTitle }}
</h5>
</template>
<form #submit.prevent="saveVisitItem(!v1$.$invalid)">
<div class="p-field">
<label for="notes">Notes</label>
<InputText
id="notes"
v-model="v1$.notes.$model"
:class="{
'p-invalid': v1$.notes.$invalid && submitted
}"
/>
<small
v-if="
(v1$.notes.$invalid && submitted) ||
v1$.notes.$pending.$response
"
class="p-error"
>{{
v1$.notes.required.$message.replace(
"Value",
"Notes"
)
}}</small
>
</div>
<Button
type="submit"
label="Save"
icon="pi pi-check"
class="p-button-primary"
/>
</form>
</Dialog>
</div>
</section>
<PatientDialog
:PatientRegistration="{
patientID: this.patientID,
status: this.patientDialogStatus,
dialogTitle: this.dialogTitle
}"
v-on:updatePatientStatus="updatePatientStatus"
/>
</template>
<script lang="ts">
import { Options, Vue } from "vue-class-component";
import VisitService from "../../service/VisitService.js";
import { reactive } from "vue";
import useVuelidate from "#vuelidate/core";
import { required } from "#vuelidate/validators";
import PatientDialog from "../../components/PatientDialog.vue";
import AutoComplete from "primevue/autocomplete";
#Options({
components: {
PatientDialog,
AutoComplete
}
})
export default class EncounterVisit extends Vue {
private validationRules = {
notes: {
required
}
};
private visitFormState = reactive({
notes: "",
});
private v1$ = useVuelidate(this.validationRules, this.visitFormState);
//ADD OR UPDATE THE ITEM VIA HTTP
saveVisitItem(isFormValid) {
this.submitted = true;
console.log(this.v1$);
console.log(isFormValid);
//this.submitted = false;
}
}
</script>
The response i get from the console log is
$anyDirty: true
$clearExternalResults: ƒ $clearExternalResults()
$dirty: false
$error: false
$errors: []
$getResultsForChild: ƒ $getResultsForChild(key)
$invalid: true
$model: null
$path: "__root"
$pending: false
$reset: () => {…}
$silentErrors: (5) [Proxy, Proxy, Proxy, Proxy, Proxy]
$touch: () => {…}
$validate: ƒ ()
notes: Proxy {$dirty: ComputedRefImpl, $path: 'notes', required: {…}, $touch: ƒ, $reset: ƒ, …}
_vuelidate_72: Proxy {$dirty: ComputedRefImpl, $path: '__root', $model: null, $touch: ƒ, $reset: ƒ, …}
[[Prototype]]: Object
false

VueJs Internal Server Error 500 when direct access url

I have created one login page using VuejS and when I'm accessing directly into the browser in server it gives me 500 error. but when i click from my website it works.
My Login.vue :
<template>
<div>
<div v-if="progress" class="progress-bg">
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
</div>
<section class="container login">
<h1 class="login-title text-center">Login With Email</h1>
<form>
<p v-if="error">{{error}}</p>
<div class="form-group">
<input type="text" v-on:keyup="validate()" v-model="email" :disabled="disabled" class="edt-text" name="email" placeholder="Email">
<p v-if="emailError">{{ emailError}}</p>
</div>
<div class="form-group">
<input type="password" v-on:keyup="validate()" v-model="password" :disabled="disabled" class="edt-text" name="password" placeholder="Password">
<p v-if="passwordError">{{ passwordError}}</p>
</div>
<p class="label-forgot-password">Forgot Password?</p>
<div class="form-group">
<button type="button" :disabled="disabled" v-on:click="login" class="btn-login">Login</button>
</div>
</form>
<p class="text-center">Or Connect With</p>
<div class="row social-login-buttons text-center">
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
<button type="button" class="btn-google"><i class="fa fa-google"></i>Google</button>
</div>
<div class="col-lg-6 col-md-6 col-sm-6 col-xs-6">
<button type="button" class="btn-facebook"><i class="fa fa-facebook"></i>Facebook</button>
</div>
</div>
<div class="row">
<p class="text-center yellow-text margin-20">Don't you have an account?</p>
</div>
</section>
</div>
</template>
<script>
import axios from 'axios'
export default {
name:"Login",
data() {
return {
email:'',
password:'',
progress:false,
disabled:false,
emailError:null,
passwordError:null,
error:null,
}
},
methods: {
async login() {
this.error = ''
if (this.email && this.password && this.validEmail(this.email)) {
this.progress = true
this.disabled = true
let result = await axios.get(
`https://example.com/login/direct?email=${this.email}&password=${this.password}`
)
this.progress=false
this.disabled=false
if(result.status == "200"){
if(result.data['status'] == true) {
localStorage.setItem("user-token", JSON.stringify(result.data['token']))
this.$router.push({name:'Home'})
}
else {
this.error = "Invalid email address / password"
this.email=''
this.password=''
}
}
}
else {
this.validate()
}
},
validate() {
if (!this.email) {
this.emailError = 'Email address required.';
}
else if (!this.validEmail(this.email)) {
this.emailError = 'Enter valid email address.';
}
else{
this.emailError = null;
}
if (!this.password) {
this.passwordError = 'Password is required';
}
else{
this.passwordError = null;
}
},
validEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
},
}
};
</script>
My router/index.js :
import Vue from 'vue'
import VueRouter from 'vue-router'';
import ChatList from '../views/Chat/ChatList.vue'
import Chat from '../views/Chat/Chat.vue'
import Login from '../views/Login/Login.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/inbox',
name: 'ChatList',
component:ChatList
},
{
path: '/chat',
name: 'Chat',
component:Chat
},
{
path: '/login',
name: 'Login',
component:Login
},
]
const router = new VueRouter({
mode:'history',
routes
})
export default router
I'm able to access my login page when i click from my site which is linked via hyper link but when i directly hit the url into browser address bar it gives me Internal server error.

Parse XML from an API in Nuxt using XML2JS

I'm trying to Loop through the API in nuxt using XML2JS and then looping through the data to display it within an html file.
It's working when console logged but it's not looping through within the HTML. There arent any errors its, just blank and not displaying anything in the html.
<template>
<article>
<TheHeader />
<h1>Destinations</h1>
<main class="mt-8 mx-auto max-w-screen-xl px-4 sm:mt-12 sm:px-6 md:mt-20 xl:mt-24">
<section v-for="(mountain, index) in mountains" :key="index">
<div v-for="category in mountain.property" :key="category.id">
<div class="lg:grid lg:grid-cols-12 lg:gap-8 mt-12">
<div class="sm:text-center md:max-w-2xl md:mx-auto lg:col-span-6 lg:text-left">
<h2 class="mt-1 text-4xl tracking-tight leading-10 font-extrabold text-gray-900 sm:leading-none sm:text-6xl lg:text-5xl xl:text-6xl">
{{ category.propertyname }}
</h2>
<p class="mt-3 text-base text-gray-500 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl">
{{ category.propertyaddress }}
</p>
</div>
<div class="mt-12 relative sm:max-w-lg sm:mx-auto lg:mt-0 lg:max-w-none lg:mx-0 lg:col-span-6 lg:flex lg:items-center">
<div class="relative mx-auto w-full rounded-lg shadow-lg">
<button type="button" class="relative block w-full rounded-lg overflow-hidden focus:outline-none focus:shadow-outline">
<img class="w-full" :src="category.photos.img.main._url" :alt="category.photos.img.caption">
<div class="absolute inset-0 w-full h-full flex items-center justify-center">
</div>
</button>
</div>
</div>
</div>
</div>
</section>
</main>
</article>
</template>
<script>
const xml2js = require('xml2js');
export default {
data() {
return {
mountains: []
}
},
async fetch() {
this.mountains = await fetch('http://localhost:3000/api/')
.then( res => res.text() )
.then( data=> {
const xml = data;
xml2js.parseString(xml, (err, result) => {
if(err) {
throw err;
}
const output = JSON.stringify(result, null, 4);
console.log(output);
});
})
}
}
</script>
JSON:
{
"scdata": {
"property": [
{
"propertycode": "123456"
Any help would be much appreciated, please let me know if you need me to provide any more information.
Thanks.
I guess you are not returning any data to the mountains. Might be possible that's why you didn't get any display at visual while you got data in const output
So, Please try with the replace the fetch function with below
<template>
<div class="container">
<h1>Test demo of xml parse</h1>
<div v-for="(category, index) in mountains" :key="index">
<div class="property-name"> <h3>Property Code:</h3><span>category.propertycode</span>
</div>
</div>
</div>
</template>
<script>
const xml2js = require('xml2js');
export default {
data() {
return {
mountains: []
}
},
methods: {
xmlToJSON: (str, options) => {
return new Promise((resolve, reject) => {
xml2js.parseString(str, options, (err, jsonObj) => {
if (err) {
return reject(err);
}
resolve(jsonObj);
});
});
}
},
async fetch() {
const xmlData = await fetch('<YOUR-API-URL>')
.then(res => res.text())
.then(data => {
console.log('<<==');
const xml = data;
return data;
});
const jsonData = await this.xmlToJSON(xmlData);
if (jsonData && jsonData.scdata && jsonData.scdata.property) this.mountains = jsonData.scdata.property
}
}
</script>

I got Vue errors rendering component

I have a "vue-cli webpack" like the following :
src/components/Signin.vue:
<template>
...
<form v-on:submit.prevent="userSignIn">
...
<div class="field">
<p class="control has-icons-left has-icons-right">
<input
v-validate="'required|email'"
v-bind:class="{'is-danger': errors.has('name')}"
name="email"
v-model="form.email"
class="input"
id="email"
type="email"
placeholder="Email"
>
<span class="icon is-small is-left">
<i class="fa fa-envelope"></i>
</span>
<span class="icon is-small is-right">
<i class="fa fa-check"></i>
</span>
<span class="help is-danger" v-show="errors.has('email')">{{ errors.first('email') }}</span>
</p>
</div>
<div class="field">
<p class="control has-icons-left">
<input
v-validate="'required|min:5'"
v-bind:class="{'is-danger': errors.has('name')}"
name="password"
v-model="form.password"
class="input"
id="password"
type="password"
placeholder="Password"
>
<span class="icon is-small is-left">
<i class="fa fa-lock"></i>
</span>
<span class="help is-danger" v-show="errors.has('password')">{{ errors.first('password') }}</span>
</p>
</div>
<div class="field is-grouped">
<div class="control">
<button v-bind:disabled="errors.any()" class="button is-primary" type="submit" :disabled="loading">
Submit
</button>
</div>
</div>
</form>
...
</template>
<script>
...
export default {
data () {
return {
form: {
email: '',
password: '',
alert: false
}
}
},
computed: {
error () {
return this.$store.getters.getError
},
loading () {
return this.$store.getters.getLoading
}
},
watch: {
error (value) {
if (value) {
this.alert = true
}
},
alert (value) {
if (!value) {
this.$store.dispatch('setError', false)
}
},
methods: {
userSignIn () {
this.$store.dispatch('userSignIn', {email: this.email, password: this.password})
}
}
},
...
}
</script>
src/App.vue:
<template>
<main>
<router-view></router-view>
</main>
</template>
<style lang="sass">
#import "~bulma"
/* Your css for this file... */
</style>
src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import firebase from 'firebase'
import { store } from './store'
import VeeValidate from 'vee-validate'
import { firebaseConfig } from './config'
Vue.use(VeeValidate)
Vue.config.productionTip = false
firebase.initializeApp(firebaseConfig)
/* eslint-disable no-new */
const unsubscribe = firebase.auth()
.onAuthStateChanged((firebaseUser) => {
new Vue({
el: '#app',
router,
store,
render: h => h(App),
created () {
store.dispatch('autoSignIn', firebaseUser)
}
})
unsubscribe()
})
and I get two errors when I click the button :
Property or method "userSignIn" is not defined on the instance but
referenced during render. Make sure to declare reactive data
properties in the data option.
Signin.vue?d58e:24 Uncaught TypeError: _vm.userSignIn is not a
function
You've defined your methods inside your watch. Move them outside.
watch: {
error (value) {
if (value) {
this.alert = true
}
},
alert (value) {
if (!value) {
this.$store.dispatch('setError', false)
}
},
},
methods: {
userSignIn () {
this.$store.dispatch('userSignIn', {email: this.email, password: this.password})
}
}