Data not propagated from parent to child component - javascript

Vue suggests to use property-down and event-up pattern to propagate the data between parent and child components. Based on this I created a simple select-box component which lists all USA states and which emits 'state-selected' event. I'm subscribing to this event in my parent class and everything is ok.
Here is my code:
<template>
<select v-model="selectedStatus" id="orderStatusSelectBox" class="form-control" name="status" v-on:change="onChange" v-bind:preselectedStatus="filters.selectedStatus">
<option value="" selected="selected" disabled>Select status</option>
<option value="1">New</option>
<option value="2">Scheduling</option>
<option value="3">In production</option>
<option value="4">Completed</option>
<option value="5">On hold</option>
<option value="6">Cancelled</option>
<option value="7">Problem</option>
</select>
</template>
<script>
export default {
data: function () {
return {
selectedStatus: ''
}
},
mounted() {
this.selectedStatus = (this.preselectedStatus ? this.preselectedStatus : '');
},
component: 'select-order-status',
methods: {
onChange() {
this.$emit('statusSelected', this.selectedStatus);
}
},
props: ['preselectedStatus'],
}
</script>
In my parent component apart from this select box child component I have several buttons (quick filters) where you can quickly filter New orders or Completed orders. I added on-click events on those buttons so I can select new status and propagate that value to my child component.
Here is the code:
<template>
<div>
<div class="row filters">
<div class="col-md-12">
<h4>QUICK FILTERS:</h4>
</div>
</div>
<div class="row filters">
<div class="col-md-4">
<div class="row">
<div class="col-md-3">
<button type="button" class="btn btn-yellow m5 fixed-width-btn" v-on:click="statusSelected('1')">New Order</button>
<button type="button" class="btn btn-success m5 fixed-width-btn" v-on:click="statusSelected('4')">Completed</button>
</div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col-md-2">
<div class="form-group">
<order-status-select v-on:statusSelected="statusSelected" v-bind:selectedStatus="filters.selectedStatus"></order-status-select>
</div>
</div>
</div>
<hr>
<span>
<div class="col-md-3">
<div class="form-group">
<select id="agentSelectBox" class="form-control" name="actions">
<option value="" selected="selected">Select action</option>
<option value="1">Action 1</option>
</select>
</div>
</div>
<div class="col-md-2">
<button type="button" class="btn btn-default">Apply</button>
</div>
<div class="col-md-2">
<button type="button" class="btn btn-default">Export CSV</button>
</div>
</div>
</div>
</template>
<script>
import OrderStatusSelectComponent from '../../reusable/OrderStatusSelectComponent.vue';
export default {
data: function () {
return {
filters: {
selectedStatus: '',
resultsPerPage: 10,
currentPage: 1,
},
}
},
mounted() {
},
component: 'filters',
methods: {
search() {
this.$emit('search', this.filters);
},
statusSelected(selectedStatus) {
this.filters.selectedStatus = selectedStatus;
}
},
components: {
'order-status-select': OrderStatusSelectComponent,
},
}
</script>
The problem is, when I click on any button (quick filter) that data is not propageted to child component.
I saw several other posts suggesting Vuex and Vue Bus but is there any other recommended way to handle this problem?
Here is peace of code that is making problems.
<order-status-select v-on:statusSelected="statusSelected" v-bind:selectedStatus="filters.selectedStatus"></order-status-select>
So from my parent component I want to pass filters.selectedStatus to the child component. When application renders first time status in filters.selectedStatus is correctly selected in my child component, but when ever I change filters.selectedStatus variable after, that will not reflect in child component.

Your child component is only checking the preselectedStatus on mount, and after the component has finished mounting it never looks at that property again, even if its value changes.
One way to deal with this is to have the child component watch its properties for changes, and respond as needed:
...
watch: {
preselectedStatus() {
// property value changed:
this.selectedStatus = (this.preselectedStatus ? this.preselectedStatus : '');
}
},
...

Related

vue3 v-model not work in js auto complete

I use javascript auto complete to finish the input text.
But I can't use v-model function of vue3 to get full value in my code.
Does any one can give me some advise, thanks.
<div class="row">
<div class="col-md-3">
<label for="fnode">fnode:</label>
<input type="text" class="form-control required" id="fnode" name="fnode" v-model="fnode">
</div>
<div class="col-md-3">
<label for="fnode">fnode:</label>
<button class="btn btn-primary form-control">{{ fnode }}</button>
</div>
</div>
<script>
Vue.createApp({
data: function(){
return {
fnode: '',
};
},
methods: {
},
mounted: function() {
},
}).mount('#form1');
</script>

Create many forms with loop

Hello I am trying to create several forms from a loop that comes from dynamic elements loaded from the database. However I think I am doing it wrong here is what I have already done. It works more or less but I would like to have the right way to proceed.
Thanks in advance
submitForm: function (e) {
e.preventDefault();
e.target.elements.techId.value // OK
this.selectUser // value is other form not form used button
}
Template
<div v-for="tech in techs" :key="tech.id" class="col-12 col-lg-3">
<h3>{{ tech.name }}</h3>
<form
name="form_tech"
method="POST"
#submit="submitForm"
>
<input type="hidden" :value="tech.id" name="techId" id="techId" />
<select
name="select_user"
class="form-select"
v-model="selectUser"
>
<option value="user_one">user one</option>
<option value="user_two">user two</option>
</select>
<button type="submit" >
Confirm
</button>
</form>
Maybe like following snippet:
const app = Vue.createApp({
data() {
return {
techs: [{id: 0, name: 'aa'}, {id: 1, name: 'bb'}, {id: 3, name: 'cc'}],
selectUser: []
};
},
methods: {
submitForm(id) {
console.log(this.selectUser[id])
}
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<div v-for="tech in techs" :key="tech.id" class="col-12 col-lg-3">
<h3>{{ tech.name }}</h3>
<form name="form_tech" method="POST"
#submit.prevent="submitForm(tech.id)"
>
<input type="hidden" :value="tech.id" name="techId" id="techId" />
<select name="select_user" class="form-select"
v-model="selectUser[tech.id]"
>
<option value="user_one">user one</option>
<option value="user_two">user two</option>
</select>
<button type="submit">Confirm</button>
</form>
</div>
</div>

Vue vite (vuex,routing), change routing depending on dropdown search value?

Looking for tips to change routing details in a searchcomponent, depending on what value the user has in a selection dropdown.
Home.vue Components
<h4>Search for {{searchObj.whatSearch}}</h4>
<input type="text" placeholder="Search..." v-model='searchObj.searchString'>
<select name="searchOption" id="searchOption" v-model='searchObj.whatSearch'>
<option :value="'songs'">Song</option>
<option :value="'artists'">Artist</option>
<option :value="'albums'">Album</option>
</select>
</div>
<button #click="getMusic(searchObj)">Searchk</button>
//this is where I import my Searchcomponent
<div v-if="ifSearched">
<search-result/>
</div>
<script>
import SearchResult from './SearchResult.vue'
export default {
data(){
return{
ifSearched: false,
searchObj:{
whatSearch: 'songs',
searchString: '',
},
}
},
components:{
SearchResult,
},
methods:{
async getMusic(searchObj){
this.ifSearched = true
return await
this.$store.dispatch('fetchYouTubeApi', searchObj)
},
},
computed:{
getYTMusic(){
return this.$store.state.musicResult.content
}
}
}
</script>
And now in my searchComponent, I want to try to depending on my:
<select name="searchOption" id="searchOption" v-model='searchObj.whatSearch'>
<option :value="'songs'">Song</option>
<option :value="'artists'">Artist</option>
<option :value="'albums'">Album</option>
</select>
Change the routerlink to different routes. So in my SearchResult component looks like this
<div>
<h3>SearchResult</h3>
<div id="searchLoop"
v-for="(result, videoId) in getYTMusic"
:key="videoId">
<router-link type="button" :to="`/musicdetails/${result.browseId}`">
<p>{{result.name}}</p>
</router-link>
</div>
</div>
<script>
export default {
computed:{
getYTMusic(){
return this.$store.state.musicResults.content
}
}
}
</script>
So if the user selected songs for example, I want to router link to
<router-link type="button" :to="`/SongDetails/${result.browseId}`">
//or /ArtistDetails
//or /AlbumDetails
And so on, is it possible to use a v-if somehow?
I created a simple solution on how you could use a property to switch between different destinations on the same <router-link> element. The HTML could look like this:
<div>
<select name="searchOption" id="searchOption" v-model="testProp">
<option v-for="link in routerLinks" :value="link.value" :key="link.value">{{link.label}}</option>
</select>
</div>
<div>
<router-link type="button" :to="`/${testProp}`">
<p>{{testProp}}</p>
</router-link>
</div>
The script part would need the following properties for this example:
export default {
data() {
return {
testProp: "",
routerLinks: [
{ label: "Songs", value: "songs" },
{ label: "Artists", value: "artists" },
{ label: "Albums", value: "albums" }
]
}
}
}
In this example the value in routerLinks is your part of the url, which you want to switch according to the selected option. The selected option will be stored in testProp, which is used in <router-link> as :to value, while label from routerLinks is used inside of <router-link>.

Where/How to define a submit destination for my self created form (Vue) component?

I want to re-use a form component through my website, but the submit button will have to handle different things every time (display different data, depending which page is calling the form-component)
I'm a little bit new to paying around with Vue components and passing data between them, up until now I did messy one-page apps.
My current plan is have the form get the inputs/filters (from the form component), and when clicking submit, it should send this data (somehow?) to the element that called it - and will know how to handle it to the specific case from where it was called. I hope this is the right approach to this kind of scenario (?).
Is my plan a proper use of Vue / a proper way to submit a form from an external form-component?
In what way do I trigger the submit to send data / run a method outside of my DashboardForm.vue component?
How do I send fetched data of DashboardForm.vue component from ReportType1.vue and re-use this functionality in ReportType2.vue.
This is my Vue Form component (DashboardForm.vue):
<template>
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<form id="mainForm" class="form-material row" method="POST">
<div class="" id="date-range">
<datepicker v-model="startDate" input-class="form-control inputDate" placeholder="Start Date" required></datepicker>
<div class="input-group-append">
<span class="input-group-text b-0 text-white"> to </span>
</div>
<datepicker v-model="endDate" input-class="form-control inputDate" placeholder="End Date" required></datepicker>
<input type="button" class="btn btn-info waves-effect waves-light" v-on:click="groupFilterDisplay(true);" value="Filter by Group"/>
<!-- <input type="button" id="submit-btn" class="btn btn-success waves-effect waves-light" v-on:click="loadNew" value="Submit"/> -->
<input type="button" id="submit-btn" class="btn btn-success waves-effect waves-light" value="Submit"/>
</div>
</form>
</div>
</div>
</div>
<transition name="fade">
<div id="groupFilter" class="popupGroupFilter" v-if="groupFilter">
<div id="filterArea">
<input type="text" v-model="searchGroupInput" placeholder="Search" class="gfSearch">
<span class="gfTitle">Filter by Group</span>
<hr>
</div>
<div class="ulTree">
<ul>
<tree_item class="item" v-bind:model="groupTree"></tree_item>
</ul>
</div>
<div v-on:click="applyGroupFilter();" class="gfClose gfApply"><span>✔ Apply</span></div>
<div v-on:click="groupFilterDisplay(false);" class="gfClose"><span>X Close</span></div>
</div>
</transition>
</div>
</template>
<script>
// import { GF } from '../mixins/GF.js';
export default {
name: 'DashboardForm',
// mixins: [GF],
data() {
return {
groupTree: window.groups,
searchGroupInput: '',
searchGroupArray: [],
groupFilterApplied: false,
groupFilterBackup: [],
selectedIds: [],
groupFilter: false,
startDate: null,
endDate: null,
mode: 0,
}
},
props: {
options: Array
},
watch: {
'searchGroupInput': function (newVal, oldVal) {
this.groupTree = this.searchGroupResult();
}
},
methods: {
recurseGroups: function (arr, action) {
},
applyGroupFilter: function () {
},
groupFilterDisplay: function (display) {
},
searchGroupResult: function () {
},
fetchGroupIds: function () {
}
}
};
</script>
This is the component that uses the DashboardForm for example (
ReportType1.vue):
<script>
import DashboardForm from "../tools/DashboardForm.vue";
export default {
components: {
DashboardForm
},
data() {
return {
};
},
created() {
},
mounted() {
},
destroyed() {
},
watch: {
},
methods: {
}
}
</script>
<!-- Template -->
<template>
<div>
<!-- Form -->
<DashboardForm/>
<!-- form result -->
<div id="resultContainer"> <datatable/> </div>
</div>
</template>
Okay if I understood you well, we are trying to build a reusable form component.
I will give you a quick overview of how VUE components communicate.
The component takes its necessary inputs using the props.
The component inner HTML can be passed from its user by using slot.
The component fire events to tell its user that there is something happened inside me.
Example of the three cases:
Your component my-form template:
<form>
<div class="row">
<slot></slot>
</div>
<div class="row">
<div class="col-12">
<button type="button" class="btn btn-default" #click="onSubmit"></button>
<button v-if="hasReset" class="btn btn-danger" #click="onReset"></button>
</div>
</div>
</form>
Your component js file:
export default {
name: 'my-form',
data() {
return {
}
},
props: {
reset: boolean
},
computed: {
hasReset: function(){
return this.reset;
}
}
methods: {
onSubmit: function(){
let data = { "name": "dummy data" };
this.$emit("submit", data);
},
onReset: function(){
let data = { "name": "" };
this.$emit("reset", data);
}
}
}
After that, you can use my-form component as below:
<my-form :reset="formHasReset" #submit="onFormSubmit" #reset="onFormReset">
<input class="col-12" type="text" name="name">
<input class="col-12" type="text" name="username">
<input class="col-12" type="password" name="password">
<input class="col-12" type="email" name="email">
</my-form>
And the javascript is:
data(){
formHasReset: true
},
methods: {
onFormSubmit: function(data){
console.log(data.name); //Should give you 'dummy data'
},
onFormReset: function(data){
console.log(data.name); //Should give you ''
}
}
I hope it is clear now for you :).

Vue.js 2.0 Vee Validate plugin not clearing errors after ajax call

I am working on a vue.js 2.0 project. I am using the Vee-Validate plugin. I have a form and when it submits, it makes an ajax call to my api. After the api call returns successfully, I am trying to clear my vee-validation so that I can invite another user with the same form, but it's not working at all.
I tried the method this.errors.clear() as suggested in their documentation
I have also thought that maybe its happening too fast, so I added a set timeout function for a couple seconds, but it still doesn't clear the errors.
Here is my Vue file with all related code:
<template>
<div v-if="user.first_time_login == 0 && user.stripe_check == 1">
<div class="viv-modal-overlay">
<div class="container">
<div class="viv-modal green">
<span class="modal-title" id="setup-team-top">Now let’s set up your team.</span>
<p>Your plan allows up to {{this.user.company.max_users}} users. Would you like to shoot out some team invites before we send you to the dashboard?</p>
<div class="invited-users" v-bind:class="{ show: show_invites }" v-if="show_invites">
<p>You can invite up to 4 more team members. Upgrade to add more.</p>
<ul>
<li v-for="invite in invites">
<img src="/img/icons/checkmark.svg" width="20" height="20" alt="success">
You invited {{invite.email}}.
<span class="clearfix"></span>
</li>
</ul>
<div class="team-done-adding">
I'm done adding team members.
</div>
</div>
<div class="modal-form">
<form id="setup-stripe-form">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<label>Team Member's Email<span>*</span></label>
<input type="text" name="email" v-model="newUser.email" v-validate data-vv-rules="required" class="form-control" :placeholder="'user#'+user.company.company_name.replace(/[^A-Z0-9]+/ig, '').toLowerCase()+'.com'">
<span v-show="errors.has('email')" class="error">{{ errors.first('email') }}</span>
</div>
<div class="form-group">
<label>Access to Leads and Contacts<span>*</span></label>
<select name="access_leads" v-model="newUser.access_leads" v-validate data-vv-rules="required" class="form-control">
<option value="1">they can see leads and contacts they created</option>
<option value="2">they can see all leads and contacts</option>
<option value="0">no access to leads and contacts</option>
</select>
<span v-show="errors.has('access_leads')" class="error">{{ errors.first('access_leads') }}</span>
</div>
<div class="form-group">
<label>Access to Proposals<span>*</span></label>
<select name="access_proposals" v-model="newUser.access_proposals" v-validate data-vv-rules="required" class="form-control">
<option value="1">they can see proposals they created</option>
<option value="2">they can see all proposals</option>
<option value="0">no access to proposals</option>
</select>
<span v-show="errors.has('access_proposals')" class="error">{{ errors.first('access_proposals') }}</span>
</div>
<div class="form-group">
<label>Access to Invoices<span>*</span></label>
<select name="access_invoices" v-model="newUser.access_invoices" v-validate data-vv-rules="required" class="form-control">
<option value="1">they can see invoices they created</option>
<option value="2">they can see all invoices</option>
<option value="0">no access to invoices</option>
</select>
<span v-show="errors.has('access_invoices')" class="error">{{ errors.first('access_invoices') }}</span>
</div>
<div class="form-group">
<label>Access to Projects<span>*</span></label>
<select name="access_projects" v-model="newUser.access_projects" v-validate data-vv-rules="required" class="form-control">
<option value="1">they can see projects they created</option>
<option value="2">they can see all projects</option>
<option value="0">no access to projects</option>
</select>
<span v-show="errors.has('access_projects')" class="error">{{ errors.first('access_projects') }}</span>
</div>
</div>
<div class="col-md-12 text-center">
<div class="modal-btn-pad">
<button type="submit" v-bind:class="{ disabled: adding_team_member }" class="btn btn-lg btn-green" #click="submitInviteForm">
<span class="sending-invite" v-if="adding_team_member">Sending Invite <img src="/img/preloader.svg" width="20" height="20"></span>
<span v-else>Continue</span>
</button><br>
<a class="light-link" href="#" #click="skipInviteForm">Skip this for now.</a>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
newUser: {
email: '',
access_leads: 1,
access_proposals: 1,
access_invoices: 1,
access_projects: 1
},
users_invited: 0,
invites: [],
show_invites: false,
adding_team_member: false
}
},
computed: mapState({
appLoading: state => state.appLoading,
user: state => state.user
}),
methods: {
submitInviteForm: function(e) {
e.preventDefault()
this.$validator.validateAll()
if (!this.errors.any()) {
//There are no errors, move forward...
//Add the team member to the database...
//Grab the authorized user
const authUser = JSON.parse(window.localStorage.getItem('authUser'))
//Create the payload...
var newTeamMember = this.newUser
//Were adding a team member now so show the loader!
this.adding_team_member = true
//Create the new company and add the owner...
this.$http.post('/api/team',
{
newTeamMember: JSON.stringify(newTeamMember)
},
{
headers: {
Authorization: 'Bearer ' + authUser.access_token
}
}).then((response) => {
if(response.status === 200) {
//Assign the user to a variable
var invitedUser = response.body
//Add the user to the list of invited users
this.invites.push({email: invitedUser.email })
//Show the invite list...
this.show_invites = true
//Jump to top
location.hash = '#setup-team-top'
//reset the new user
this.newUser.email = ''
this.newUser.access_leads = 1
this.newUser.access_proposals = 1
this.newUser.access_invoices = 1
this.newUser.access_projects = 1
//Were done adding a team member so hide the loader!
this.adding_team_member = false
//Clear the validation errors
this.errors.clear()
}
}).catch(function(error){
console.log(error);
})
}
},
skipInviteForm: function(e) {
e.preventDefault()
alert('skip invite!')
}
}
}
</script>
Try to have a look at this fiddle which was extracted from this issue.
Basically, you have to call this.$validator.clean(); after you have reset the input fields of your form.

Categories

Resources