Can not get the latest data after updated the user profile(vue.js 2, vuex, laravel 8) - vue.js

I'm a newbie in VueX. I face a problem which is after I updated the profile information and save it successfully, the database has shown the latest updated info but the user interfaces cannot retrieve the latest data. The state seems no updated.
My profile UI
https://i.stack.imgur.com/8QtI9.png
but after updated, all the info disappear and didn't show the latest info
https://i.stack.imgur.com/ul1ob.png
UserProfile.vue
<template>
<div class="container" style="padding-top:25px">
<div class="main-font">My Profile</div>
<div class="d-flex row">
<div class="col-6">
<ValidationObserver v-slot="{ handleSubmit }">
<form #submit.prevent="handleSubmit(updateProfile)">
<div class="d-flex py-4">
<div>
<img class="profile" src="/img/default.png" alt=""
</div>
<div class="my-auto ml-5">
<button type="submit" class="btn upload text"><i class="fas fa-upload fa-sm pr-2"></i>Upload new picture</button>
</div>
</div>
<div class="form-group col-10 p-0 m-0">
<ValidationProvider name="Name" rules="required|alpha" v-slot="{ errors }">
<label class="text">Name</label>
<input type="text" id="name" class="form-control form-text" placeholder="Enter your username" v-model="userForm.name">
<span class="error-messsage">{{ errors[0] }}</span>
</ValidationProvider>
<!-- <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small> -->
</div>
<div class="form-group col-10 p-0 m-0 mt-4">
<ValidationProvider name="E-mail" rules="required|email" v-slot="{ errors }">
<label class="text">Email</label>
<input type="email" id="email" class="form-control form-text" placeholder="Enter email" v-model="userForm.email">
<span class="error-messsage">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<button type="submit" class="btn col-10 p-o save-but">SAVE CHANGES</button>
</form>
</ValidationObserver>
</div>
<div class="w-50">
<img class="bg-img" src="/img/profile-bg.png" alt="">
</div>
</div>
</div>
script
<script>
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate/dist/vee-validate.full';
export default {
components: {
ValidationProvider,
ValidationObserver,
},
data() {
return {
userForm: {
name: '',
email: '',
},
error: null,
}
},
created () {
this.userForm = JSON.parse(JSON.stringify(this.$store.getters.currentUser));
},
computed: {
currentUser(){
return this.$store.getters.currentUser;
},
},
methods: {
getUser (){
const token = this.$store.getters.currentUser.token
axios.get('/api/auth/userprofile',{
headers: {
Authorization: `Bearer ${token}`
}
})
.then(response => {
this.userForm= response.data.user;
})
},
updateProfile () {
const token = this.$store.getters.currentUser.token
// console.log(this.$store.getters.currentUser.token)
axios.put('/api/auth/update-profile',
{
name: this.userForm.name,
email: this.userForm.email,
},
{
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json" // add content-type
}
})
.then(response => {
this.userForm.name = response.data.name;
this.userForm.email = response.data.email;
swal({
icon: "success",
text: "Update Succesfully!",
});
// this.$store.commit('update');
})
}
}
}
</script>
store.js
import {getLoggedinUser} from './auth';
import {getUser} from './auth';
const user = getLoggedinUser();
const updateUser = getUser();
export default {
state: {
currentUser: user,
isLoggedin: !!user,
loading: false,
auth_error: null,
reg_error:null,
registeredUser: null,
update: null,
},
getters: {
isLoading(state){
return state.loading;
},
isLoggedin(state){
return state.isLoggedin;
},
currentUser(state){
return state.currentUser;
},
authError(state){
return state.auth_error;
},
regError(state){
return state.reg_error;
},
registeredUser(state){
return state.registeredUser;
},
update(state){
return state.update;
}
},
mutations: {
login(state){
state.loading = true;
state.auth_error = null;
},
loginSuccess(state, payload){
state.auth_error = null;
state.isLoggedin = true;
state.loading = false;
state.currentUser = Object.assign({}, payload.user , {token: payload.access_token});
localStorage.setItem("user", JSON.stringify(state.currentUser));
},
loginFailed(state, payload){
state.loading = false;
state.auth_error = payload.error;
},
logout(state){
localStorage.removeItem("user");
state.isLoggedin = false;
state.currentUser = null;
},
registerSuccess(state, payload){
state.reg_error = null;
state.registeredUser = payload.user;
},
registerFailed(state, payload){
state.reg_error = payload.error;
},
update(state, payload) {
state.currentUser = payload.data;
}
},
actions: {
login(context){
context.commit("login");
},
// update(context){
// // state.currentUser.update(context);
// }
}
};
auth.js
export function registerUser(credentials){
return new Promise((res,rej)=>{
axios.post('/api/auth/register', credentials)
.then(response => {
res(response.data);
})
.catch(err => {
rej('An error occured.. try again later.')
})
})
}
export function login(credentials){
return new Promise((res,rej)=>{
axios.post('/api/auth/login', credentials)
.then(response => {
setAuthorization(response.data.access_token);
res(response.data);
})
.catch(err => {
rej('Wrong Email/Password combination.')
})
})
}
export function getLoggedinUser(){
const userStr = localStorage.getItem('user');
if(!userStr){
return null
}
return JSON.parse(userStr);
}
export function getUser(credentials){
return new Promise((res,rej)=>{
axios.get('/api/auth/userprofile', credentials)
.then(response => {
// setAuthorization(response.data.access_token);
res(response.data);
})
.catch(err => {
rej('No User')
})
})
}
Please help me if you have any ideas or solution.

You're not running any dispatches of your store.
You have defined update mutation, but commented it.
Uncomment this
// this.$store.commit('update')
and modify it like so:
this.$store.commit('update', { data: response.data })
Next do the same for your login request.

Related

Vuejs Passing dynamic data from parent to child component

I've got a view and a component. I'm trying to do auth here.
As a user, I input username and password, click login. This emits the information to the parent component, which makes a fetch request to API gateway in AWS. This fetch response has a header X-Session-Id that I'm interested in.
I've got the emit bit working fine.
However, I'm unable to pass the header value back to the component, and I'm unable to set new_password_required to true, which would add a new input field for a new password, as well as replace the login button with a reset password button.
I feel like I need to do this with props, but I'm unable to successfully pass the values from parent to child.
Also, should the reset password bit have its own component?
Here's my code below. This is my first frontend, so I'm not familiar with how I am supposed to share it (e.g. with a playground). Also, I'm trying to stick to vanilla vue for now since I'm learning (I've only get vue-router installed I think)
parent:
<template>
<div id="app" class="small-container">
<login-form #login:user="loginUser($event)" />
</div>
</template>
<script>
import LoginForm from "#/components/LoginForm.vue";
export default {
name: "Login",
components: {
LoginForm
},
data() {
return {
session_id: String,
new_password_required: Boolean
};
},
methods: {
async loginUser(loginEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/login/user",
{
method: "POST",
body: JSON.stringify(loginEvent)
}
);
const data = await response.json();
console.log(data);
if (data.headers["X-Session-Id"] != null) {
this.session_id = data.headers["X-Session-Id"];
this.new_password_required = true;
}
} catch (error) {
console.error(error);
}
},
async resetPassword(resetPasswordEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/reset/user/password",
{
method: "POST",
body: JSON.stringify(resetPasswordEvent)
}
);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
}
};
</script>
Component:
<template>
<div id="login-form">
<h1>Serverless App</h1>
<form>
<label for="email_address">Email Address:</label><br />
<input
v-model="login_details.email_address"
type="text"
id="email_address"
name="email_address"
/><br />
<label for="password">Password:</label><br />
<input
v-model="login_details.password"
type="password"
id="password"
name="password"
/>
<label v-if="new_password_required" for="new_password"
>New Password:</label
><br />
<input
v-if="new_password_required"
v-model="login_details.new_password"
type="password"
id="new_password"
name="new_password"
/>
</form>
<button v-if="!new_password_required" #click="loginUser($event)">
Login
</button>
<button v-if="new_password_required" #click="resetPassword($event)">
Reset Password
</button>
</div>
</template>
<script>
export default {
name: "LoginForm",
props: {
session_id: String,
new_password_required: {
type: Boolean,
default: () => false
}
},
data() {
return {
login_details: {
email_address: "",
password: "",
new_password: ""
}
};
},
methods: {
loginUser() {
console.log("testing loginUser...");
const loginEvent = {
email_address: this.login_details.email_address,
password: this.login_details.password
};
this.$emit("login:user", loginEvent);
},
resetPassword() {
console.log("testing resetPassword...");
const resetPasswordEvent = {
email_address: this.login_details.email_address,
password: this.login_details.password,
new_password: this.login_details.new_password,
session_id: this.login_details.sessionId
};
this.$emit("reset:Password", resetPasswordEvent);
}
}
};
</script>
Your child component looks good, however, you need to pass the props through in the parent component as shown here:
<login-form #login:user="loginUser($event)" :session-id="xidhere"
:new-password-required="newPasswordRequired"/>
As these values are updated in the parent component, the child component should be updated.
As a note, name your props using camel case, and then use kebab-case in your HTML.
So your login-form props should be updated to:
props: {
sessionId: String,
newPasswordRequired: {
type: Boolean,
default: () => false
}
},
Also, as you are emitting the event to parent, there may be no need to send the session id to the child, just add this to your api call before you send it.
Figured it out. I created a new child component for resetting password. Perhaps it can be dry'd up a bit? I'm new at this. Happy for any pointers :)
PARENT
<template>
<div id="app" class="small-container">
<login-form v-if="!new_password_required" #login:user="loginUser($event)" />
<reset-password-form
v-if="new_password_required"
:session_id="session_id"
#reset:password="resetPassword($event)"
/>
</div>
</template>
<script>
import LoginForm from "#/components/LoginForm.vue";
import ResetPasswordForm from "#/components/ResetPasswordForm.vue";
export default {
name: "Login",
components: {
LoginForm,
ResetPasswordForm
},
data() {
return {
session_id: "",
new_password_required: false
};
},
methods: {
async loginUser(loginEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/login/user",
{
method: "POST",
body: JSON.stringify(loginEvent)
}
);
const data = await response.json();
console.log(data);
if (data.headers["X-Session-Id"] != null) {
this.session_id = data.headers["X-Session-Id"];
this.new_password_required = true;
}
} catch (error) {
console.error(error);
}
},
async resetPassword(resetPasswordEvent) {
try {
const response = await fetch(
process.env.VUE_APP_API_GATEWAY_ENDPOINT + "/reset/user/password",
{
method: "POST",
body: JSON.stringify(resetPasswordEvent)
}
);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
}
};
</script>
CHILD: login-form
<template>
<div id="login-form">
<h1>Serverless Release Dashboard</h1>
<form>
<label for="email_address">Email Address:</label><br />
<input
v-model="login_details.email_address"
type="text"
id="email_address"
name="email_address"
/><br />
<label for="password">Password:</label><br />
<input
v-model="login_details.password"
type="password"
id="password"
name="password"
/>
</form>
<button #click="loginUser($event)">
Login
</button>
</div>
</template>
<script>
export default {
name: "LoginForm",
data() {
return {
login_details: {
email_address: "",
password: ""
}
};
},
methods: {
loginUser() {
console.log("testing loginUser...");
const loginEvent = {
email_address: this.login_details.email_address,
password: this.login_details.password
};
this.$emit("login:user", loginEvent);
}
}
};
</script>
CHILD: reset-password-form
<template>
<div id="reset-password-form">
<h1>Serverless Release Dashboard</h1>
<form>
<label for="email_address">Email Address:</label><br />
<input
v-model="login_details.email_address"
type="text"
id="email_address"
name="email_address"
/><br />
<label for="password">Password:</label><br />
<input
v-model="login_details.password"
type="password"
id="password"
name="password"
/>
<label for="new_password">New Password:</label><br />
<input
v-model="login_details.new_password"
type="password"
id="new_password"
name="new_password"
/>
</form>
<button #click="resetPassword($event)">
Reset Password
</button>
</div>
</template>
<script>
export default {
name: "ResetPasswordForm",
props: {
email_address: String,
password: String,
session_id: String
},
data() {
return {
login_details: {
email_address: "",
password: "",
new_password: "",
session_id: ""
}
};
},
methods: {
resetPassword() {
console.log("testing resetPassword...");
const loginEvent = {
email_address: this.email_address,
password: this.password,
new_password: this.login_details.new_password,
session_id: this.session_id
};
this.$emit("reset:password", loginEvent);
}
}
};
</script>

Vuejs - How to get the current id in a url of an api with axios

When the client clicks on the button, I want him to block another client, so I want my id to be dynamic: http://example/user/:id.
My template:
<template>
<div class>
<div v-for="post in posts" :key="post.id">
<div>
<div>{{ post.name }}</div>
<div>{{ post.id }}</div>
<button #click='BlockUser'>Block</button>
</div>
</div>
</div>
</template>
And my script:
<script>
const axios = require('axios');
export default {
name: 'User',
data() {
return {
posts: [],
errors: [],
id: {
id: ""
},
}
},
methods: {
getData() {
axios.get(`http://example/user`)
.then(result => {
this.posts = result.data
console.log(result)
})
},
BlockUser() {
axios.get('http://example/user/blacklist/:id' + encodeURIComponent(this.id.id))
.then(response => {
console.log(response)
})
},
},
}
</script>
Initially, I set a value for the id in data id a number and it worked. But now that I put an empty string.
it returns an undefined
If your posts have user_id field inside, you can pass it to BlockUser method, and use it in the request url.
<button #click='BlockUser(post.user_id)'>Block</button>
BlockUser(user_id) {
axios.get(`http://example/user/blacklist/${user_id}`)
.then(response => {
console.log(response)
})
},

Vue.js return data from axios

<template>
<div class="comment">
<div v-for="(comment, index) in comments" :key="index">
{{ getUser(comment.student_idx) }}
</div>
</div>
</template>
<script>
import axios from 'axios'
import server from '#/models/server'
export default {
data() {
return {
url: server,
comments: {}
}
},
props: ['idx'],
created() {
axios.get(`${this.url}/bamboo/reply?post=${this.idx}`)
.then(response => {
if (response.data.status === 200) {
this.comments = response.data.data.replies
}
})
},
methods: {
getUser (idx) {
axios.get(`${this.url}/member/student/${idx}`)
.then(response => {
if (response.data.status === 200) {
return response.data.data.member.name
}
})
}
}
}
</script>
I would like to load the comments at created and print them out using v-for.
In v-for, I would like to load the member.name from each comment
But {{ getUser(comment.student_idx) }} is undefined.
I don't know how to return data from axios
Help me please!!
Your method should not be async for stable run code. My recomendation is next code:
<template>
<div class="comment">
<div v-for="(comment, index) in comments" :key="index">
{{ comments['user'] }}
</div>
</div>
</template>
<script>
import axios from 'axios'
import server from '#/models/server'
export default {
data() {
return {
url: server,
comments: []
}
},
props: ['idx'],
created() {
axios.get(`${this.url}/bamboo/reply?post=${this.idx}`)
.then(response => {
if (response.data.status === 200) {
this.comments = response.data.data.replies;
if(this.comments)
for(let comment of this.comments){
this.getUser(comment, comment.student_idx);
}
}
})
},
methods: {
getUser (comment, idx) {
axios.get(`${this.url}/member/student/${idx}`)
.then(response => {
if (response.data.status === 200) {
this.$set(comment, 'user', response.data.data.member.name);
}
})
}
}
}
</script>

Formatting authentication request object from VueJS properly

I have a VueJS/Vuex frontend consuming an express/postgres api.
In Postman, both registration and login work with a request like:
{ "user": { "email": "user#gmail", "password":...}}
From the Vue app, registration works as expected, but for login, instead of sending a request object like
{ "user": { "email": "user#gmail", "password":...}}
, which is what the api is expecting, it is sending only,
{ "email": "user#gmail", "password":...}
This results in the api throwing:
TypeError: Cannot read property 'email' of undefined
Here is my Login component:
<template>
<div class="ui stackable three column centered grid container">
<div class="column">
<h2 class="ui dividing header">Log In</h2>
<Notification
:message="notification.message"
:type="notification.type"
v-if="notification.message"
/>
<form class="ui form" #submit.prevent="login">
<div class="field">
<label>Email</label>
<input type="email" name="email" v-model="email" placeholder="Email" required>
</div>
<div class="field">
<label>Password</label>
<input type="password" name="password" v-model="password" placeholder="Password" required>
</div>
<button class="fluid ui primary button">LOG IN</button>
<div class="ui hidden divider"></div>
</form>
<div class="ui divider"></div>
<div class="ui column grid">
<div class="center aligned column">
<p>
Don't have an account? <router-link to="/signup">Sign Up</router-link>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
import Notification from '#/components/Notification'
export default {
name: 'LogIn',
components: {
Notification
},
data () {
return {
email: '',
password: '',
notification: {
message: '',
type: ''
}
}
},
// beforeRouteEnter (to, from, next) {
// const token = localStorage.getItem('tweetr-token')
// return token ? next('/') : next()
// },
methods: {
login () {
this.$store
.dispatch('login', {
email: this.email,
password: this.password
})
.then(() => {
console.log(this.$store.user)
// redirect to user home
this.$router.push('/')
})
.catch(error => console.log(error))
}
}
}
</script>
And this is my store.js:
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
// import SubscriptionsService from './services/SubscriptionsService'
Vue.use(Vuex)
const VUE_APP_ROOT_API = 'http://localhost:8000'
export default new Vuex.Store({
state: {
status: '',
user: JSON.parse(localStorage.getItem('user'))
},
mutations: {
auth_request (state) {
state.status = 'Signing in...'
},
set_user (state, user) {
state.user = user
localStorage.setItem('user', JSON.stringify(user))
console.log(user)
},
auth_success (state) {
state.status = 'success'
},
auth_error (state) {
state.status = 'Invalid credentials'
},
logout (state) {
state.status = ''
state.user = null
localStorage.removeItem('user')
}
},
actions: {
register ({ commit }, user) {
return new Promise((resolve, reject) => {
commit('auth_request')
console.log(process.env.VUE_APP_ROOT_API)
axios({ url: VUE_APP_ROOT_API + '/api/auth', data: user, method: 'POST' })
.then(async resp => {
const user = resp.data.user
commit('auth_success')
commit('set_user', user)
resolve(resp)
})
.catch(err => {
commit('auth_error', err)
localStorage.removeItem('token')
reject(err)
})
})
},
login ({ commit }, user) {
return new Promise((resolve, reject) => {
commit('auth_request')
console.log(user);
axios({ url: VUE_APP_ROOT_API + '/api/auth/login', data: user, method: 'POST' })
.then(resp => {
const user = resp.data.user
// console.log(user)
// console.log(resp)
commit('auth_success')
commit('set_user', user)
resolve(resp)
})
.catch(err => {
commit('auth_error')
commit('logout')
reject(err)
})
})
},
logout ({ commit }) {
return new Promise((resolve) => {
commit('logout')
localStorage.removeItem('token')
delete axios.defaults.headers.common['authorization']
resolve()
})
}
},
getters: {
isAuthenticated: state => !!state.user,
authStatus: state => state.status,
user: state => state.user
}
})
for comparison, here is my working SignUp component:
<template>
<div class="ui stackable three column centered grid container">
<div class="column">
<h2 class="ui dividing header">Sign Up, it's free!</h2>
<form class="ui form" #submit.prevent="signup">
<div class="field">
<label>Username</label>
<input type="username" name="username" v-model="username" placeholder="Username">
</div>
<div class="field">
<label>Email</label>
<input type="email" name="email" v-model="email" placeholder="Email">
</div>
<div class="field" >
<label>Password</label>
<input type="password" name="password" v-model="password" placeholder="Password">
</div>
<button class="fluid ui primary button">SIGN UP</button>
<div class="ui hidden divider"></div>
</form>
<div class="ui divider"></div>
<div class="ui column grid">
<div class="center aligned column">
<p>
Got an account? <router-link to="/login">Log In</router-link>
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SignUp',
data () {
return {
email: '',
password: '',
username: '',
notification: {
message: '',
type: ''
}
}
},
methods: {
signup: function () {
let data = {
user: {
email: this.email,
password: this.password,
username: this.username
}
}
this.$store.dispatch('register', data)
.then(() => this.$router.push('/'))
.catch(err => console.log(err))
},
}
}
</script>
How do I format the request object to match what the api expects?
Try sending the payload in a similar structure you have for your sign up function:
.dispatch('login', {
user: {
email: this.email,
password: this.password
}
})

vue2 data change not updating view

I have a simple file upload component FileUpload.vue
<template>
<div>
<input type="file" name="file" id="file" v-on:change="handleFileUpload()" ref="file">
<button class="btn shadow-lg first" #click="addFiles()">SELECT FILE</button>
<button class="btn shadow-lg" v-on:click="handleSubmit()">UPLOAD</button>
<p> {{uploadPercentage}} </p>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'FileUpload',
data() {
return {
file: '',
uploadPercentage: 0
}
},
methods: {
handleFileUpload: () => {
this.file = document.getElementById('file').files[0];
},
addFiles: () => {
document.getElementById('file').click();
},
handleSubmit: () => {
let formData = new FormData();
formData.append('file', this.file);
axios.post('/file/create',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: (progressEvent) => {
console.log(this.uploadPercentage);
this.uploadPercentage = parseInt(Math.round(progressEvent.loaded * 100 / progressEvent.total));
}
}
)
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.log(error);
})
}
}
}
</script>
The data uploadPercentage is initialized to 0. As the file upload starts, the uploadPercentage value changes in the onUploadProgress method.
If I console log uploadPercentage, it shows the changes in the value. It goes from 0 to 100.
console.log(this.uploadPercentage)
But in my view, uploadPercentage never changes. It always shows the initial value 0.
<p> {{uploadPercentage}} </p>
What am I doing wrong?
Thanks in advance.