How to access data inside a modal using Vue js? - vue.js

I'm editing a candidate_profile with a relationship with the Users table. I was able to access the User Object within the candidate_profile and display it, but I just want to display the name property.
Here is a screenshot of the User object in the textarea field. How can I do this?
Here is my data:
data() {
return {
candidateProfiles: {
user: ''
},
editCandidateProfileData: {},
}
};
},
Here is my button which calls the editProfile function:
<button type="button" class="btn btn-lg btn-success"
v-on:click="editProfile(candidateProfile)">Request an Interview</button>
editProfile(candidateProfile) {
this.editCandidateProfileData = {...candidateProfile};
this.showInterviewRequestModal();
},
Finally, inside my modal I can get the User object like this:
<b-form-group label="Name" label-for="name">
<b-form-textarea
id="experience"
v-model="editCandidateProfileData.user"
placeholder="Tell us a little about your experience..."
:rows="10"
:max-rows="12"
>
</b-form-textarea>
<div class="invalid-feedback" v-if="errors.user">{{ errors.user[0] }}</div>
</b-form-group>

Related

How to programmatically remove a rendered v-for element from the DOM when using a click event?

I am trying to use V-Animate-CSS to show a "deletion" animation when a delete button is pressed. I am struggling with trying to specify the exact element to delete programmatically within a v-for loop.
Let me explain:
I have the following vue <template> like so:
<div
v-for="x in divisionLangs"
:key="x.P_uID"
>
<button
type="button"
#click.prevent="deleteCard(x.P_uID)"
>
</button>
<transition name="bounce"
<div v-if="show" class="card-body">
<!-- card content is here -->
</div>
</transition>
My <script> section looks like so:
data() {
return {
show: true,
divisionLangs: []
}
}
deleteCard(id) {
this.show = !this.show
this.divisionLangs = this.divisionLangs.filter(x => x.P_uID !== id)
},
The data for the divisionLangs array looks like so:
[
{
P_uID: 789,
..blah...
},
{
P_uID: 889,
...blah...
}
]
How can I structure this code so only the matching card item is deleted from the rendered list and not ALL of the card items? What happens right now is that all of the cards are deleted on the deleteCard method.
You should define a child component for each item like below:
Parent component:
<tmplate>
<child-component v-for="x in divisionLangs" :key="x.P_uID" />
</template>
And in child component:
<button
type="button"
#click.prevent="deleteCard(x.P_uID)"
>
</button>
<transition name="bounce"
<div v-if="show" class="card-body">
<!-- card content is here -->
</div>
</transition>
And Script part of child component:
data() {
return {
show: true,
divisionLangs: []
}
}
deleteCard(id) {
this.show = !this.show
this.divisionLangs = this.divisionLangs.filter(x => x.P_uID !== id)
},

Toggle button text on a loop VUE

I have a loop with products, each with a product card.
I want to be able to toggle the button when clicked from Add to cart to Remove from cart.
The problem is all of the products buttons toggle at the same time, and I wan't ONLY the individual product card buttons to be toggled referencing each individual product.
In my HTML
<div v-for="product of products" :key="product.id">
<span class="btn btn-primary mt-5 modal-toggle-btn" #click="addGift(product, text, 'index')" v-show="!isAdded">Añadir a la box</span>
<span class="btn btn-primary mt-5 modal-toggle-btn" #click="removeGift(product, 'index')" v-show="isAdded">Quitar de la box</span>
</div>
Vue data
isAdded: false
My Vue methods
addGift(product, index){
this.campaign.selectedproducts.push({name: product.name });
this.isAdded = true
},
removeGift(product, index){
this. campaign.selectedproducts.splice(index, 1)
this.isAdded = false
},
My suggestion is to:
Divide the product buttons as an individual component.
Use addedIds as an array to store added product ids instead of isAdded boolean.
Communicate parent and child click events with Vue event handling.
Store clicked product id in to the addedProductId on click events.
Check against addedProductId to make sure a product was added or
not in child component.
Example:
ProductButtons.vue (child component)
<template>
<div>
<span class="btn btn-primary mt-5 modal-toggle-btn" #click="addGift" v-show="!isAdded">Añadir a la box</span>
<span class="btn btn-primary mt-5 modal-toggle-btn" #click="removeGift" v-show="isAdded">Quitar de la box</span>
</div>
</template>
<script>
export default {
name: "ProductButtons",
props: {
product: { type: Object, required: true },
addedIds: { type: Array, required: true },
},
computed: {
isAdded() {
return this.addedIds.indexOf(this.product.id) > -1;
},
},
methods: {
addGift(){
this.$emit('addGift', this.product);
},
removeGift(product){
this.$emit('addGift', this.product);
},
}
}
</script>
In Your HTML
<template v-for="product of products" :key="product.id">
<product-buttons :product="product" :addedIds="addedIds" #addGift="addGift" #removeGift="removeGift"></product-buttons>
</template>
Vue data
addedIds: []
Your Vue methods
addGift(product){
this.campaign.selectedproducts.push({name: product.name });
// save product id as an added id
const index = this.addedIds.indexOf(product.id);
if (index === -1) {
this.addedIds.push(product.id);
}
},
removeGift(product){
this.campaign.selectedproducts.splice(index, 1);
// remove product id
const index = this.addedIds.indexOf(product.id);
if (index > -1) {
this.addedIds.splice(index, 1);
}
},

Show on several elements in the same component in vuejs

Looping out a number of boxes within the same component in vuejs.
Each box has a button that reveals more text using v-on:click.
The problem is that all the boxes respond to this at the same time.
Is there a way to target the specific button being clicked if there are several buttons in a component?
Is there some way to isolate each button so they all dont activate at once?
<div class="filter-list-area">
<button #click="show =!show"> {{text}} </button>
<ul class="filter-list-item-area">
<li class="filter-list-item " v-for="(items, key) in packages">
<div>
<img class="fit_rating">
</div>
<div class="filter-list-item-info" >
<h3>{{items.partner_university}}</h3>
<p> Match: {{items.match_value}}</p>
<div v-for="(courses, key) in courses">
<transition name="courses">
<div class="courses2" v-show="show">
<p v-if="courses.Pu_Id === items.pu_Id">
{{courses.Course_name}}
</p>
</div>
</transition>
</div>
</div>
</li>
</ul>
</div>
</template>
<script>
import testdata from '../App.vue'
export default {
data (){
return{
text: 'Show Courses',
testFilter: 'Sweden',
show: false
}
},
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
testuni: Array,
list: Array,
packages: Array,
courses: Array
},
methods:{
afunction(){
console.log(this.show);
},
changeText(){
if(this.show){
this.text = 'Hide Courses'
}
else{
this.text = "Show Courses"
}
}
},
mounted() {
this.afunction();
},
watch: {
show:
function() {
this.afunction()
this.changeText()
}
},
}
EDIT: I've created this before you posted the code example, but you could use same principle:
In your data add showMoreText, which will be used to track if show more data should be shown.
I would agree with #anaximander that you should use child components here
Simple example how to show/hide
<template>
<div>
<div v-for="(box, index) in [1,2,3,4,5]">
<div>
Box {{ box }} <button #click="toggleMore(index)">More</button>
</div>
<div v-show="showMoreText[index]">
More about box {{ box }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
showMoreText: {},
}
},
methods: {
toggleMore(index) {
/*
Adds a property to a reactive object, ensuring the new property is
also reactive, so triggers view updates.
https://vuejs.org/v2/api/#Vue-set
*/
this.$set(this.showMoreText, index, ! this.showMoreText[index])
}
}
}
</script>
This sounds like an ideal situation for a new child component, which will allow each instance of the new component to have its own separate state.
The child components can emit events to the parent, if cross-component communication is necessary.

Using Vee-validate to disable button until form is filled out correctly

I want to disable my submit button until my form is filled out correctly, this is what I have so far:
<form>
<input type="text" class="form-control" v-validate="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />
<span v-show="errors.has('email')">{{ errors.first('email') }}</span>
<button v-if="errors.any()" disabled="disabled" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
<button v-else="errors.any()" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
</form>
The above only prints an error message and disables my submit button after I've started inputting a value. I need it to be disabled from the start, before I start interacting with the input, so that I cannot send an empty string.
Another question is if there is a better way than using v-ifto do this?
EDIT:
userCreate: {
customerId: null,
userPrincipalName: '',
name: 'unknown',
isAdmin: false,
isGlobalAdmin: false,
parkIds: []
}
Probably simpliest way is to use ValidationObserver slot for a form. Like this:
<ValidationObserver v-slot="{ invalid }">
<form #submit.prevent="submit">
<InputWithValidation rules="required" v-model="first" :error-messages="errors" />
<InputWithValidation rules="required" v-model="second" :error-messages="errors" />
<v-btn :disabled="invalid">Submit</v-btn>
</form>
</ValidationObserver>
More info - Validation Observer
Setting up the button to be :disabled:"errors.any()" disables the button after validation. However, when the component first loads it will still be enabled.
Running this.$validator.validate() in the mounted() method, as #im_tsm suggests, causes the form to validate on startup and immediately show the error messages. That solution will cause the form to look pretty ugly. Also, the Object.keys(this.fields).some(key => this.fields[key].invalid); syntax is super ugly.
Instead, run the validator when the button is clicked, get the validity in the promise, and then use it in a conditional. With this solution, the form looks clean on startup but if they click the button it will show the errors and disable the button.
<button :disabled="errors.any()" v-on:click="sendInvite();">
Send Invite
</button>
sendInvite() {
this.$validator.validate().then(valid=> {
if (valid) {
...
}
})
}
Validator API
One way to disable a button until all the values you need are filled, is to use a computed property that will return bool if all values are assigned or not
Example:
Create a computed property like this:
computed: {
isComplete () {
return this.username && this.password && this.email;
}
}
And bind it to the html disabled attribute as:
<button :disabled='!isComplete'>Send Invite</button
This means, disable the button if !isComplete is true
Also, in your case you don't need two if/else-bound buttons. You can use just one to hide/show it based on if the form is completed or has any errors:
<button :disabled="errors.any() || !isCompleted" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">Send Invite</button>
This button will be disabled until all fields are filled and no errors are found
Another way is to make use of v-validate.initial
<input type="text" class="form-control" v-validate.initial="'required|email'" name="email" placeholder="Email" v-model="userCreate.userPrincipalName" />
This will execute the validation of the email input element after the page is loaded. And makes that your button is disabled before interacting with the input.
To check whether a form is invalid or not we can add a computed property like this:
computed: {
isFormInValid() {
return Object.keys(this.fields).some(key => this.fields[key].invalid);
},
},
Now if you want to start checking immediately before user interaction with any of the fields, you can validate manually inside mounted lifecycle hooks:
mounted() {
this.$validator.validate();
}
or using computed
computed: {
formValidated() {
return Object.keys(this.fields).some(key => this.fields[key].validated) && Object.keys(this.fields).some(key => this.fields[key].valid);
}
}
and use
button :disabled="!formValidated" class="btn btn-primary" v-on:click="sendInvite();" data-dismiss="modal" type="submit">
For the current version 3 (As at the time of writing).
Step 1
Ensure form fields can be watched.
Step 2
Get a reference to the validator instance:
<ValidationObserver ref="validator">.
Step 3
Trigger validation silently whenever the form fields change.
Here's an example:
export default {
data() {
return {
form: {
isValid: false,
fields: {
name: '',
phone: '',
}
}
}
},
watch: {
'form.fields': {
deep: true,
handler: function() {
this.updateFormValidity();
}
}
},
methods: {
async updateFormValidity() {
this.form.isValid = await this.$refs.validator.validate({
silent: true // Validate silently and don't cause observer errors to be updated. We only need true/false. No side effects.
});
},
}
}
<button :disabled="form.isValid">
Submit
</button>
You can add computed properties
...
computed: {
isFormValid () {
return Object.values(this.fields).every(({valid}) => valid)
}
}
...
and it bind to the button:
<button :disabled="!isFormValid" class="btn btn-primary" type="submit">Send Invite</button>
i try this on vee-validate version ^2.0.3

Vue and Vuex: Updating state based on changes to the view

I'm trying to build
An application that renders a form, where the default input values are equal to the data from the store.
When the save button is clicked, the state will be updated according to the new data added to the view by the user.
Currently the inputs are bound to the store data, and so I have no reference to the "live" value of the inputs. When the user clicks save, how do I grab the "live" values?
Component Template
<input type="text" class="form-control" :value="item.name">
<input type="text" class="form-control" :value="item.price">
<button class="btn btn-primary" v-on:click="updateItem(item)">Save</button>
Component
data: function() {
return {}
},
methods: {
updateItem(item) {
this.$store.commit('updateItem', item);
},
},
computed: {
items() {
return this.$store.getters.getItem;
}
}
Potential Solutions
I thought I could perhaps create a "clone" of the store, and bind the inputs to the cloned item data. Then this object will be updated as the view changes, and so I can grab those "live" values, and commit the data from the view to the store. Is this a good solution?
If you wanted to update without the user having to click the button, then I would suggest one of the methods explained in the docs.
But since you want to do it wen they click the button, try something like this:
<template>
<form>
<input type="text" class="form-control" v-model="item.name">
<input type="text" class="form-control" v-model="item.price">
<button class="btn btn-primary" #click.prevent="updateItem">Save</button>
</form>
</template>
<script>
export default {
data() {
return {
item: {
name: null,
price: null,
}
}
},
mounted() {
// Set the default value of this.item based on what's in the store
this.item = this.$store.getters.getItem
},
methods: {
updateItem() {
this.$store.commit('updateItem', this.item);
}
}
}
</script>