Reset form state VueJS and VueRouter - javascript

I am currently working on a step by step registration created with VueJS and VueRouter.
The basic premise of the form is that a user has to select a signup option before proceeding to enter his personal details. However, I would like the form to be reset every time a different sign up option is selected.
VueJs + VueRouter Multi Step Form
/**
*
* #type {Vue}
*/
let page2Component = Vue.component('pageTwo', {
data: function() {
return {
step1: {
firstname: '',
lastname: ''
}
}
},
template: `<div>
<h2>Registration</h2>
<form action="" method="post" role="form">
<legend>Details</legend>
<div class="form-group">
<label for="firstname">First name</label>
<input type="text" class="form-control" name="firstname" id="firstname" placeholder="First name..." v-model.trim="step1.firstname">
</div>
<div class="form-group">
<label for="lastname">Last name</label>
<input type="text" name="lastname" class="form-control" id="lastname" placeholder="Last name..." v-model.trim="step1.lastname">
</div>
</form>
<div>
<router-link to="/" class="btn btn-default">Back</router-link>
<router-link to="/" class="btn btn-primary">Next Page</router-link>
</div>
</div>`,
created: function () {
},
methods: {
resetUserDetails: function() {
this.step1.firstname = '';
this.step1.lastname = '';
}
}
});
/**
*
* #type {Vue}
*/
let page1Component = Vue.component('pageOne', {
data: function() {
return {
selectedSignUpOption: ''
}
},
template: `<div>
<h2>Registration</h2>
<form action="" method="post" role="form">
<legend>Sign Up Options</legend>
<div class="form-group">
<div>
<div class="radio">
<label>
<input type="radio" name="signup_option" value="user" v-model.trim="selectedSignUpOption"> Register As A User
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="signup_option" value="content-editor" v-model.trim="selectedSignUpOption"> Register As A Content Editor
</label>
</div>
</div>
</div>
</form>
<div>
<router-link to="/page2" class="btn btn-primary" v-bind:disabled="!signupOptionSelected">Next Page</router-link>
</div>
</div>`,
created: function () {
},
watch: {
'$route'(to, from) {
// react to route changes...
// don't forget to call next()
console.log('to: ', to);
console.log('from: ', from);
console.log(this);
},
selectedSignUpOption: function() {
console.log(this);
console.log(this.selectedSignUpOption);
}
},
computed: {
signupOptionSelected: function() {
return this.selectedSignUpOption !== '';
}
}
});
/**
*
* #type {Vue}
*/
let wizardComponent = Vue.component('wizardForm', {
template: `<div>
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>`,
created: function () {
let children = [];
children.push({path: '/', component: page1Component});
children.push({path: '/page2', component: page2Component});
router.addRoutes(children);
}
});
/**
* Vue App Initialisation
*/
Vue.config.devtools = true;
Vue.use(VeeValidate);
const routes = [];
const router = new VueRouter({
routes // short for routes: routes
});
console.log('routes', routes);
document.addEventListener('DOMContentLoaded', function () {
/**
*
*/
let vm = new Vue({
el: '#app',
router,
});
});
I have included a link to the form that I am working on. Any one have any idea as to how I can achieve this? Ideally, I would like to be able to call the resetUserDetails method in the page2Component when the sign up option is different to the last one that was selected.
Any suggestions would be appreciated. I am using VueJS 2 btw.
Thanks,
Emmanuel

For this to work you should pass selected value as to second component.
your route config will look like this
children.push({path: '/:option/page2', component: page2Component});
form component
watch: {
'$route.params.option'(to, from) {
if((this.prevSelection && this.$route.params.option) && (this.$route.params.option !== this.prevSelection)){
this.prevSelection = this.$route.params.option
this.resetUserDetails()
}
}
}
For more information please have a look at this

Related

I am attempting to create a firebase login page on a web app, however when I attempt to login, nothing happens [duplicate]

I'm trying to get the sign in part working on my webapp but it's not working properly.
Whenever I press the login button the page either refreshes and the url gets updated with the credentials and stays at the same page OR the router gets pushed and goes to the 'homepage' without logging the user in.
I also followed this guide for reference: https://blog.logrocket.com/vue-firebase-authentication/
What's weird is that the sign up part is working just fine.
SignIn.vue
<div class="card-body">
<form>
<!-- email -->
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
<input id="email" type="email" class="form-control" name="email" placeholder="e-mail" value required autofocus v-model="form.email" />
</div>
<!-- password -->
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-key"></i></span>
</div>
<input id="password" type="password" class="form-control" name="password" placeholder="password" required v-model="form.password" />
</div>
<!-- error -->
<div v-if="error" class="alert alert-danger animated shake">{{ error }}</div>
<br />
<!-- login -->
<div class="form-group d-flex justify-content-between">
<div class="row align-items-center remember"><input type="checkbox" v-model="form.rememberMe" />Remember Me</div>
<input type="submit" #click="submit" value="Login" class="btn float-right login_btn" />
</div>
</form>
</div>
Script in SignIn.vue
<script>
import firebase from 'firebase';
export default {
data() {
return {
form: {
email: '',
password: '',
rememberMe: false
},
error: null
};
},
methods: {
submit() {
firebase
.auth()
.signInWithEmailAndPassword(this.form.email, this.form.password)
.catch(err => {
this.error = err.message;
})
.then(data => {
this.$router.push({ name: 'home' });
});
}
}
};
</script>
Store.js
import Vue from 'vue';
import Vuex from 'vuex';
import profile from './modules/profile';
import authenticate from './modules/authenticate';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
profile,
authenticate
}
});
Authenticate.js in store
const state = {
user: {
loggedIn: false,
data: null
}
};
const getters = {
user(state) {
return state.user;
}
};
const mutations = {
SET_LOGGED_IN(state, value) {
state.user.loggedIn = value;
},
SET_USER(state, data) {
state.user.data = data;
}
};
const actions = {
fetchUser({ commit }, user) {
commit('SET_LOGGED_IN', user !== null);
if (user) {
commit('SET_USER', {
displayName: user.displayName,
email: user.email
});
} else {
commit('SET_USER', null);
}
}
};
export default {
state,
mutations,
actions,
getters
};
It is probably because you assign the submit type to your button, your form is submitted before the Firebase method is triggered.
You should change the button code from
<input type="submit" #click="submit" value="Login" class="btn float-right login_btn" />
to
<input type="button" #click="submit" value="Login" class="btn float-right login_btn" />
See the W3 specification for more detail on button types.

How do I retrieve a variable from .js file?

How do I access a variable defined in a js file from a vue file? Is there a way to pass this variable to the vue file? In the code below, I've got a template.js file and a contact.vue file. I am converting mjml to html in the template file and I need to access the output saved to the plainHmtl variable from the vue file.
template.js
const mjml2html = require('mjml')
const Vue = require('vue')
const app = new Vue({
data: {
name: ''
},
template: `
<mj-section>
<mj-column>
<mj-text font-size="20px" color="#F45E43" font-family="helvetica">Hello {{ name }}</mj-text>
</mj-column>
</mj-section>`
})
const renderer = require('vue-server-renderer').createRenderer({
template: `
<mjml>
<mj-body>
<!--vue-ssr-outlet-->
</mj-body>
</mjml>`
})
renderer.renderToString(app).then(html => {
const htmlWithoutDataServerRenderedAttribute = html.replace(`data-server-rendered="true"`, '')
const plainHtml = mjml2html(htmlWithoutDataServerRenderedAttribute)
console.log(plainHtml.html)
})
contact.vue
<template>
<div class="sign_up" id="signupform">
<main role="main">
<div class="SignUp_container">
<form class="form-signup" #submit.prevent="processForm">
<input type="text" name="name" placeholder="Enter First Name" required/>
<input type="text" name="lname" placeholder="Enter Last Name" required/>
<input type="email" name="mailaddr" placeholder="Your email address" required/>
<div class="sign_cancel_buttons">
<router-link to="/">
<button id="canlbtn" type="cancel" class="clbtn">
Cancel
</button>
</router-link>
<button id="signupbtn" type="submit" name="sendmsg-button" class="signbtn">
Sign Up
</button>
</div>
</form>
</div>
</main>
</div>
</template>
<script>
export default {
methods: {
// how do I access plainHtml here?
}
}
</script>
In template.js, define a variable where you will have the final result of converting your html, once defined just add this to the end of the file:
export { varHTML }
Now in contact.vue you have to import and use the export we made from template.js, consider that you must modify the import path according to the case that you are handling for your files:
import { varHTML } from './template.js'
<script>
export default {
data() {
return {
plainHtml: null
}
}
methods: {
// how do I access plainHtml here?
// any method that is going to be needed now will have plainHtml available
},
created () {
this.plainHtml = varHTML
}
}
</script>

Event after click element in Vue

This is my first post about VUE. I try use VUE in Laravel. I have this code:
index.blade.php:
#extends('front._layout.main')
#section('content')
<div class="container mx-auto" id="vue-rendered">
<p class="text-white text-3xl">Płyty</p>
<div class="mt-5 max-w-screen-md mx-auto text-text text-lg">
<div class="text-right text-xl">
<div class="text-white">Sortowanie:</div>
</div>
xxxxx
<product-list></product-list>
</div>
</div>
#endsection
ProductList.vue:
<template>
<form>
<div class="multiselect">
<div class="selectBox" id="selectBox">
<select>
<option>Typ</option>
</select>
<div class="overSelect"></div>
</div>
<div class="checkboxes">
<label for="one">
<input type="checkbox" id="one"/>First checkbox</label>
<label for="two">
<input type="checkbox" id="two"/>Second checkbox</label>
<label for="three">
<input type="checkbox" id="three"/>Third checkbox</label>
</div>
</div>
</form>
</template>
<script>
import axios from "axios";
export default {
name: "product-list",
mounted() {
this.loadProducts();
},
data() {
return {
products: [],
}
},
methods: {
loadProducts() {
axios
.get(route('json.products.get.list'))
.then(response => {
this.products = _.get(response, "data", []);
})
.catch(error => {
console.log(error);
});
}
}
}
window.onload = function () {
var selectBox = new Vue({
el: '#selectBox',
data: {
name: 'selectBox'
},
methods: {
showMultiselect: function (event) {
alert('It working')
showMultiselect();
}
}
});
}
</script>
multiselect.js:
var expanded = false;
function showMultiselect() {
var checkboxes = document.getElementsByClassName("checkboxes");
if (!expanded) {
checkboxes.style.display = "block";
expanded = true;
} else {
checkboxes.style.display = "none";
expanded = false;
}
}
app.js:
try {
window._ = require('lodash');
window.axios = require('axios');
window.route = require('#/common/route');
require('./libs/swal.js');
require('./vue');
require('./libs/multiselect.js');
window.Vue = require('vue');
Vue.component('product-list', require('./components/ProductList.vue').default);
const app = new Vue({
el: '#vue-rendered',
})
} catch (e) {
console.log(e);
}
My problem is no action after click after click #selectBox.
I want show alert and run function showMultiselect.
Htom, laravel show correct html code.
How can I repair it?
Please help me. I am beginner in Vue
Looking at the Vue file, you can handle click events using the v-on:click (or #click for shorthand) - see Vue Docs - Event Handling for more info. You can also handle conditional rendering of the checkboxes with the v-if directive - see Vue Docs - Conditional Rendering for more info.
The end result in the .vue file, using your previous code, would be something like this.
ProductList.vue:
<template>
<form>
<div class="multiselect">
<div class="selectBox" id="selectBox" #click="toggleMutliSelect">
<select>
<option>Typ</option>
</select>
<div class="overSelect"></div>
</div>
<div v-if="showMultiSelect" class="checkboxes">
<label for="one">
<input type="checkbox" id="one"/>First checkbox</label>
<label for="two">
<input type="checkbox" id="two"/>Second checkbox</label>
<label for="three">
<input type="checkbox" id="three"/>Third checkbox</label>
</div>
</div>
</form>
</template>
<script>
import axios from "axios";
export default {
name: "product-list",
mounted() {
this.loadProducts();
},
data() {
return {
products: [],
showMultiSelect: false,
}
},
methods: {
loadProducts() {
axios
.get(route('json.products.get.list'))
.then(response => {
this.products = _.get(response, "data", []);
})
.catch(error => {
console.log(error);
});
},
toggleMutliSelect() {
alert('It working')
this.showMultiSelect = !this.showMultiSelect
}
}
}
</script>

Writing custom form controls to use v-model in Vue.js

I'm trying to write a custom form control in Vue.js to learn how to package together multiple form inputs into a single custom component.
My project setup looks like this (standard webpack-simple vue cli setup):
$ tree -L 1
.
├── README.md
├── index.html
├── node_modules
├── package.json
├── src
└── webpack.config.js
Here's my top level Vue instance .vue file:
// App.vue
<template>
<div class="container">
<form v-if="!submitted" >
<div class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
<form>
<fullname v-model="user.fullName"></fullname>
<div class="form-group">
<label for="email">Email:</label>
<input id="email" type="email" class="form-control" v-model="user.email">
<label for="password">Password:</label>
<input id="password" type="password" class="form-control" v-model="user.password">
</div>
<fieldset class="form-group">
<legend>Store data?</legend>
<div class="form-check">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="storeDataRadios" id="storeDataRadios1" value="true" checked v-model="user.storeData">
Store Data
</label>
</div>
<div class="form-check">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="storeDataRadios" id="storeDataRadios2" value="false" v-model="user.storeData">
No, do not make my data easily accessible
</label>
</div>
</fieldset>
</form>
<button class="btn btn-primary" #click.prevent="submitForm()">Submit</button>
</div>
</div>
</form>
<hr>
<div v-if="submitted" class="row">
<div class="col-xs-12 col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3">
<div class="panel panel-default">
<div class="panel-heading">
<h4>Your Data</h4>
</div>
<div class="panel-body">
<p>Full Name: {{ user.fullName }}</p>
<p>Mail: {{ user.email }}</p>
<p>Password: {{ user.password }} </p>
<p>Store in Database?: {{ user.storeData }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import FullName from './FullName.vue';
export default {
data() {
return {
user: {
fullName: 'John Smith',
email: '',
password: '',
storeData: true,
},
submitted: false
}
},
methods: {
submitForm() {
this.submitted = true;
},
},
components: {
'fullname' : FullName,
}
}
</script>
<style>
</style>
And here's the custom component file:
<template>
<div class="form-group">
<label for="firstName">First name:</label>
<input id="firstName" type="text" class="form-control" :value="first" #input="emitChange(true, $event)">
<label for="lastName">Last name:</label>
<input id="lastName" type="text" class="form-control" :value="last" #input="emitChange(false, $event)">
</div>
</template>
<script>
export default {
props: ['value'],
methods: {
emitChange(isFirst, evt) {
let name = '';
let evtValue = evt.target.value == undefined ? "" : evt.target.value;
if (isFirst) {
name = evtValue +" "+ this.second;
} else {
name = this.first +" "+ evtValue;
}
this.value = name;
this.$emit('input', this.value);
}
},
computed: {
first() {
if (this.value != "")
return this.value.split(" ")[0];
else return "";
},
last() {
if (this.value != "")
return this.value.split(" ")[1];
else return "";
}
}
}
</script>
Which I also realize I'm messing up because I'm directly editing a prop value with:
this.value = name;
(not an error, but Vue.JS gives a warning).
However, even before that, typing in the first input box causes the second input box to update its value to undefined (...wat!?).
Would be grateful for advice on how to properly set up custom form control components! (and why this example isn't working).
I think, the problem here is you never know where the first name ends and where the last name starts. Take Barack Hussein Obama for instance and imagine he's Belgian, his name would be Barack Hussein van Obama. You can't safely assume which part ist first and which part is lastname.
However, if you could say the firstname is exactly one word and the rest is lastname, here's an example implementation (stripped down). To illustrate the problem, try to put in Obamas second name.
Otherwise, the component behaves like a two way bound component. You can alter the separate values on the fullname component, or edit the fullname on the root component and everything stays up to date. The watcher listens for changes from above, the update method emits changes back up.
Vue.component('fullname', {
template: '#fullname',
data() {
// Keep the separate names on the component
return {
firstname: this.value.split(' ')[0],
lastname: this.value.split(' ')[1],
}
},
props: ['value'],
methods: {
update() {
// Notify the parent of a change, chain together the name
// and emit
this.$emit('input', `${this.firstname} ${this.lastname}`);
},
},
mounted() {
// Parse the prop input and take the first word as firstname,
// the rest as lastname.
// The watcher ensures that the component stays up to date
// if the parent changes.
this.$watch('value', function (value){
var splitted = value.split(' ');
this.firstname = splitted[0];
splitted.shift();
this.lastname = splitted.join(' ');
});
}
});
new Vue({
el: '#app',
data: {
fullname: 'Barack Obama',
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.10/vue.js"></script>
<div id="app">
<fullname v-model="fullname"></fullname>
<p>Thanks, {{fullname}}</p>
<input v-model="fullname" />
</div>
<template id="fullname">
<div>
<input v-model="firstname" #input="update" type="text" id="firstname" />
<input v-model="lastname" #input="update" type="text" id="lastname" />
</div>
</template>

Aurelia issue with setting element class based on obj.id === $parent.selectedId

I completed the contact-manager tut from Aurelia.io and am incorporating it into as task manager tut I'm putting together. The markup below sets the li class based on task.id === $parent.id.
task-list.html
<template>
<div class="task-list">
<ul class="list-group">
<li repeat.for="task of tasks" class="list-group-item ${task.id === $parent.selectedId ? 'active' : ''}">
<a route-href="route: tasks; params.bind: {id:task.id}" click.delegate="$parent.select(task)">
<h4 class="list-group-item-heading">${task.name}</h4>
<span class="list-group-item-text ">${task.due | dateFormat}</span>
<p class="list-group-item-text">${task.isCompleted}</p>
</a>
</li>
</ul>
</div>
task-list.js
#inject(WebAPI, EventAggregator)
export class TaskList {
constructor(api, ea) {
this.api = api;
this.tasks = [];
ea.subscribe(TaskViewed, x => this.select(x.task));
ea.subscribe(TaskUpdated, x => {
let id = x.task.id;
let task = this.tasks.find(x => x.id == id);
Object.assign(task, x.task);
});
}
created() {
this.api.getList().then( x => this.tasks = x);
}
select(task) {
this.selectedId = task.id;
return true;
}
}
If I edit the current task, represented by
task-detail.html
<template>
<require from="resources/attributes/DatePicker"></require>
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Edit Task Profile</h3>
</div>
<div class="panel-body">
<form role="form" class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">Name</label>
<div class="col-sm-10">
<input type="text" placeholder="name" class="form-control" value.bind="task.name">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Description</label>
<div class="col-sm-10">
<input type="text" placeholder="description" class="form-control" value.bind="task.description">
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Due Date</label>
<div class="col-sm-10">
<div class="input-group date">
<input type="text" datepicker class="form-control" value.bind="task.due | dateFormat:'L'"><span class="input-group-addon"><i class="glyphicon glyphicon-th"></i></span>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Urgency</label>
<div class="col-sm-10">
<input type="range" min="1" max="5" step="1" class="form-control" value.bind="task.urgency">
</div>
</div>
</form>
</div>
</div>
<div class="button-bar">
<button class="btn btn-info" click.delegate="addTask(task)" >Add New</button>
<button class="btn btn-success" click.delegate="save()" disabled.bind="!canSave">Save Edit</button>
</div>
</template>
task-detail.js
#inject(WebAPI, EventAggregator, Utils, DialogService)
export class TaskDetail {
constructor(api, ea, utils, dialogService) {
this.api = api;
this.ea = ea;
this.utils = utils;
this.dialogService = dialogService;
}
activate(params, routeConfig) {
this.routeConfig = routeConfig;
return this.api.getTaskDetails(params.id).then(task => {
this.task = task;
this.routeConfig.navModel.setTitle(task.name);
this.originalTask = this.utils.copyObj(task);
this.ea.publish(new TaskViewed(task));
});
}
get canSave() {
return this.task.name && !this.api.isRequesting;
}
save() {
console.log(this.task);
this.api.saveTask(this.task).then(task => {
this.task = task;
this.routeConfig.navModel.setTitle(task.name);
this.originalTask = this.utils.copyObj(task);
this.ea.publish(new TaskUpdated(this.task));
});
}
canDeactivate() {
if (!this.utils.objEq(this.originalTask, this.task)) {
let result = confirm('You have unsaved changes. Are you sure you wish to leave?');
if (!result) {
this.ea.publish(new TaskViewed(this.task));
}
return result;
}
return true;
}
addTask(task) {
var original = this.utils.copyObj(task);
this.dialogService.open({viewModel: AddTask, model: this.utils.copyObj(this.task)})
.then(result => {
if (result.wasCancelled) {
this.task.name = original.title;
this.task.description = original.description;
}
});
}
}
If a value has changed, navigation away from the current task is not allowed, and that works -- that is, the contact-detail part of the UI doesn't change. However, the task <li>, that one tries to navigate to still gets the active class applied. That's not supposed to happen.
If I step along in dev tools, on the Aurelia.io contact-manager, I see that the active class is briefly applied to the list item, then it goes away.
from the contact-manager's contact-list.js This was run when clicking an <li> and no prior item selected.
select(contact) {
this.selectedId = contact.id;
console.log(contact);
return true;
}
This logs
Object {__observers__: Object}
Object {id: 2, firstName: "Clive", lastName: "Lewis", email: "lewis#inklings.com", phoneNumber: "867-5309"}
The same code on my task-manager's (obviously with "contact" replaced by task") task-list.js logs
Object {description: "Meeting With The Bobs", urgency: "5", __observers__: Object}
Object {id: 2, name: "Meeting", description: "Meeting With The Bobs", due: "2016-09-27T22:30:00.000Z", isCompleted: false…}
My first instinct is to say it's got something to do with this.selectedId = contact.id
this there would refer to what I assume is a function called select (looks like the function keyword is missing in your example?) or the global object (i.e. window)
select(contact) {
this.selectedId = contact.id;
console.log(contact);
return true;
}
Fixed it. It wasn't working because I had pushstate enabled. That clears things up. Thanks therealklanni.

Categories