Vuejs Passing dynamic data from parent to child component - vue.js

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>

Related

Refer to the undefined value in Checking ID overlap method

In code below, I'm checking Id overlap.
I want to hand over the value set to the v-model as a payload
I'm using mapState, so I handed over this.info.userid, but it doesn't work.
It keeps saying that I'm referring to the undefined value.
What value should I refer to?
this is my registration.vue
<div class="user-details">
<div class="input-box">
<span class="details">아이디</span>
<div class="validate-box">
<Field
:rules="validateId"
type="text"
placeholder="아이디를 입력하세요"
v-model="info.userId"
name="userId"
required />
<ErrorMessage
class="error-box"
name="userId" />
</div>
<div>
<button #click="idCheck">
중복확인
</button>
</div>
</div>
</div>
import { mapState } from 'vuex'
import { Field, Form, ErrorMessage } from 'vee-validate';
methods: {
async idCheck() {
this.$store.dispatch('signup/idChecking', {
userId: this.info.userId
})
}
},
computed: {
...mapState('signup',{
info: 'Userinfo'
})
}
and code below is my store registration.js
state: {
Userinfo: {
userId: '',
password: '',
passwordConfirm: '',
userName: '',
birthDate: '',
phoneNumber: '',
email: '',
year: '',
month: '',
day: ''
}
},
actions: {
async idChecking(payload) {
const { userId } = payload
axios.get(`http://??.???.???.???:????/api/check/${userId}`)
.then(res => {
console.log(res)
})
.catch(error => {
console.log(error)
})
}
}
I resolve the problem.
I didn't put 'state' in async idChecking method.
async idChecking(state, payload) <- like this.

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

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.

VueJs getting child form data from parent component

I have a form in my child component:
<form #submit="submitForm">
<input type="text" v-model="textInput" />
</form>
export default {
name: "childComp",
data: function() {
return {
textInput: ""
}
}
}
Now from my parent component I have a button and I need to click on that button to get the child form data.
<button type="button" #click="getFormData()"> click </button>
export default {
name: "ParentComp",
data: function() {
return {
formData: []
}
},
methods: {
getFormData(d) {
formData.push(d);
console.log(d)
}
}
}
Appreciate your help.
Even though you got it solved by using $ref, i would recommend utilizing the power of a v-model implementation into your custom component.
This is a more clean approach, and by doing this, you'll always have the form data at hand, instead of having to actually retrieve it when you want to use it.
It can be done by doing the following:
Parent
<button type="button" #click="getFormData()"> click </button>
<childComp v-model="formData" />
export default {
name: "ParentComp",
data: function() {
return {
formData: {}
}
},
methods: {
getFormData() {
console.log(this.formData)
}
}
}
Child
<form>
<input type="text" v-model="selected.text" />
</form>
export default {
name: "childComp",
props: ['value'],
computed: {
selected: {
get() {
return this.value;
},
set(value) {
this.$emit('input', value);
}
}
}
}
I found the solutions using ref="". Not sure how complex it will get in future.
Here is what I did.
My parent component:
<button type="button" #click="getFormData()"> click </button>
<childComp ref="childComp" />
export default {
name: "ParentComp",
data: function() {
return {
formData: []
}
},
methods: {
getFormData() {
const data = this.$refs.childComp.submitForm()
formData.push(data);
console.log(data)
}
}
}
My child component:
<form #submit="submitForm">
<input type="text" v-model="textInput" />
</form>
export default {
name: "childComp",
data: function() {
return {
textInput: ""
}
},
submitForm() {
return this.form;
}
}

Vue.js component form sends null values

the model is Entity. No errors are thrown. The form gets submitted and a new Entity is saved. However, the description and title are both recorded as 'nil'. to here is src/components/NewEntity.vue:
<template>
<div class="entities">
<h1>Add Entity</h1>
<div class="form">
<div>
<input type="text" name="title" placeholder="TITLE" v-model="title">
</div>
<div>
<textarea rows="15" cols="15" placeholder="DESCRIPTION" v-model="description"></textarea>
</div>
<div>
<button class="app_entity_btn" #click="addEntity">Add</button>
</div>
</div>
</div>
</template>
<script>
import EntitiesService from '#/services/EntitiesService'
export default {
name: 'NewEntity',
data () {
return {
title: '',
description: ''
}
},
methods: {
async addEntity () {
await EntitiesService.addEntity({
title: this.title,
description: this.description
})
this.$router.push({ name: 'Entities' })
}
}
}
</script>
here is where the script imports from (for some reason SO would not initially allow me to include Services/Api.js):
services/EntitiesServices:
import Api from '#/services/Api'
export default {
fetchEntities () {
return Api().get('/entities')
},
addEntity (params) {
return Api().post('entities', {
title: params.title,
description: this.description
})
}
services/Api.js:
import axios from 'axios'
export default() => {
return axios.create({
baseURL: 'http://localhost:3000',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Access-Control-Allow-Origin': '*'
}
})
}
OG Solution by cperez
Depending on your server, you need to 'stringify' your parameters object within your post function in two possible ways. If your server accepts JSON, then:
return Api().post('entities',
JSON.stringify({
title: params.title,
description: this.description
})
)
If you server doesn't accept JSON, you have to convert the object into encoded URL parameters with the querystring module:
var qs = require('querystring');
return Api().post('entities',
qs.stringify({
title: params.title,
description: this.description
})
)

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.