Delete button spawning multiple confirm modals in vue-bootstrap> - javascript

Looking to solve this problem with having a unique ref to the modal in this loop, tried giving the ref a grade.id at the end but wouldn't work with String interpolation with the b-modal ref, also tried :ref.
This is the code in the vue file, and below that a screenshot of what it looks like, when I press to delete a grade then it shows all of the modals as the modal is in a loop.
<template>
<div>
<b-form #submit="onSubmit" #reset="onReset">
<b-row>
<b-col class="underline">Add Grades</b-col>
</b-row>
<br />
<b-row>
<b-col>
<b-form-group
inline
label-cols-sm="4"
label-cols-lg="3"
id="input-group-1"
label="Select Farm:"
>
<b-form-select v-model="form.farm" :options="farms" required />
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group label-cols-sm="4" label-cols-lg="3" label="name:">
<b-form-input
v-model="form.gradeName"
size="sm"
placeholder="name"
></b-form-input>
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group
label-cols-sm="4"
label-cols-lg="3"
label="min weight(gms):"
>
<b-form-input
v-model="form.gradeMinWeight"
size="sm"
placeholder="minimum"
></b-form-input>
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group
label-cols-sm="4"
label-cols-lg="3"
label="max weight(gms):"
>
<b-form-input
v-model="form.gradeMaxWeight"
size="sm"
placeholder="maximum"
></b-form-input>
</b-form-group>
</b-col>
</b-row>
<b-row class="my-1">
<b-col>
<b-form-group label-cols-sm="4" label-cols-lg="3" label="shape:">
<b-form-select v-model="form.gradeShape">
<option value="rounded">rounded</option>
<option value="flat">flat</option>
<option value="curve">curve</option>
</b-form-select>
</b-form-group>
</b-col>
<b-col>
<b-form-group label-cols-sm="4" label-cols-lg="3" label="dimension:">
<b-form-select v-model="form.gradeDimension">
<option value="rounded">seeds</option>
<option value="rounded">small</option>
<option value="flat">medium</option>
<option value="curve">large</option>
<option value="curve">extra large</option>
<option value="curve">jumbo</option>
</b-form-select>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-button type="submit" variant="primary" block>Add</b-button>
</b-col>
<b-col>
<b-button type="reset" variant="secondary" block>Cancel</b-button>
</b-col>
</b-row>
</b-form>
<br />
<b-row>
<b-col class="underline">My Grades</b-col>
</b-row>
<br />
<div v-if="grades.length > 0">
<b-card-group class="mb-4" v-for="grade in grades" :key="grade.id">
<b-card :title="getTitle(grade)">
<b-card-text class="list-card">
{{ getProperties(grade) }}
<span style="float: right">
<b-button
class="mr-1"
type="reset"
variant="danger"
#click="showModal"
size="sm"
>
<!-- Fix css to icon middle -->
<b-icon icon="trash-fill" size="is-small"></b-icon>
</b-button>
<b-modal
scrollable
id="modal-delete-grade"
ref=' `modal-delete-grade'
centered
#ok="actionDelete(grade.id)"
class="d-block text-center"
title="Confirm Delete Grade"
>
<p>
Confirm that you are about to delete grade
{{ getTitle(grade) }}
</p>
</b-modal>
</span>
</b-card-text>
</b-card>
</b-card-group>
</div>
</div>
</template>
<script>
import makeToast from "../../helper/toast";
import farmService from "../../service/FarmService";
import { getUser } from "#/helper/storage";
import userService from "#/service/UserService";
import DashEdit from "#/views/DashEdit";
import { contentMixin } from "#/mixins/contentMixin";
import { setupMixin } from "#/mixins/setupMixin";
export default {
name: "AddSetupGrade",
mixins: [contentMixin, setupMixin],
data() {
return {
farms: [],
grades: [],
form: {
farm: null,
gradeName: null,
gradeMinWeight: null,
gradeMaxWeight: null,
gradeShape: null,
gradeDimension: null,
},
};
},
mounted() {
this.getUserFarm();
this.getGrades();
},
created() {
this.getGrades();
},
methods: {
async onSubmit(evt) {
try {
evt.preventDefault();
let response = await farmService.addGrades({
name: this.form.gradeName,
farmId: this.form.farm.id,
properties: {
minWeight: this.form.gradeMinWeight,
maxWeight: this.form.gradeMaxWeight,
units: "gms",
shape: this.form.gradeShape,
dimension: this.form.gradeDimension,
},
});
if (response.status === 200) {
makeToast.call(
this,
"Success",
"Successfully created grade",
"success"
);
this.onReset();
this.getGrades();
}
} catch (e) {
console.log(`Error in creating grade. ${e}`);
makeToast.call(this, "Error", "Error adding grade", "danger");
}
},
onReset() {
this.form.gradeName = null;
this.form.gradeMinWeight = null;
this.form.gradeMaxWeight = null;
this.form.gradeShape = null;
this.form.gradeDimension = null;
},
async getUserFarm() {
try {
let { id: userId } = getUser();
let response = await userService.getFarmsOfUser(userId);
let farmList = response.data.data;
this.farms = farmList.map((each) => {
return {
value: { id: each.id, name: each.name },
text: each.name,
};
});
this.farms.push({ value: null, text: "Select Farm" });
} catch (e) {
console.log(`Error: ${e}`);
}
},
getTitle(grade) {
if (grade) {
return grade.name + " - " + grade.alias;
}
},
showModal(gradeId) {
this.$bvModal.show("modal-delete-grade");
},
keyValString(obj) {
return Object.entries(obj).reduce((str, [key, value], index) => {
if (value) {
if (str) str += ", ";
str += `${key}: ${value}`;
}
return str;
}, "");
},
getProperties(grade) {
return (
(grade && grade.properties && this.keyValString(grade.properties)) ||
"No properties on grade."
);
},
async getGrades() {
try {
let response = await farmService.getGrades();
this.grades = response.data.data;
} catch (e) {
console.log(e);
}
},
async actionDelete(id) {
try {
let response = await farmService.deleteGrade(id);
this.$bvModal.hide("modal-delete-grade");
this.getGrades();
} catch (e) {
console.log(e.response.data);
}
},
},
};
</script>
<style scoped>
.underline {
border-bottom: 2px solid currentColor;
border-bottom-color: #d3d3d3;
font-weight: bold;
font-size: 16px;
}
.list-card {
font-family: Open Sans;
font-size: 14px;
line-height: (48 / 24);
letter-spacing: -0.046em;
color: black;
}
.card-title {
font-size: 1em;
font-family: Open Sans;
}
</style>

You could move the modal outside of the loop. To pass the data of the clicked grade, simply save it to your data for example as "activeGrade". In your modal you display the data of "activeGrade". Now you can open the (same) modal with the clicked grade specific data.
I simplified your code a bit to show only the asked issue:
<template>
<div class="hello">
<b-card-group class="mb-4" v-for="grade in grades" :key="grade.id">
<b-card :title="grade.title">
<b-card-text class="list-card">
Some Text (grade properties)
<span style="float: right">
<b-button
class="mr-1"
type="reset"
variant="danger"
#click="showModal(grade)"
size="sm">
<!-- Fix css to icon middle -->
<b-icon icon="trash-fill" size="is-small"></b-icon>
</b-button>
</span>
</b-card-text>
</b-card>
</b-card-group>
<b-modal v-if="activeGrade"
scrollable
id="modal-delete-grade"
centered
#ok="actionDelete(activeGrade.id)"
class="d-block text-center"
title="Confirm Delete Grade">
<p>
Confirm that you are about to delete grade
{{ activeGrade.title }}
</p>
</b-modal>
</div>
</template>
<script>
export default {
data() {
return {
grades: [
{ id: 1, title: 'Grate 1' },
{ id: 2, title: 'Grate 2' },
{ id: 3, title: 'Grate 3' },
],
activeGrade: null
}
},
methods: {
actionDelete(gradeId) {
console.log('delete ' + gradeId)
},
showModal(grade) {
this.activeGrade = grade
this.$nextTick(() => {
this.$bvModal.show("modal-delete-grade");
})
}
}
}
</script>

Related

OTP authentication problem with FIREBASE and vuejs

Hello I'm working on an application made in vuejs and firebase and I use OTP authentication for account creation. I got to do this. When the phone number field and the verification code field are on the same page, the authentication passes and I am redirected to the next page.
Now I decided to separate the two fields (phone number and verification code) by creating a page to enter the phone number and a second page to enter the verification code. Unfortunately this does not work, the code verification page does not recognize the code entered.
please help me.
Page 1: (Login.vue)
<div class="row justify-content-center" style="background-color:#34B76E;">
<div class="col-lg-5 col-md-7">
<b-container>
<b-row class="vh-100" align-v="center">
<div>
<b-card no-body class="" style="max-width: 540px; background-color:#EFF3F8;
border-radius:20px 20px 20px 20px;">
<b-row no-gutters class="" style="">
<b-col md="5" style="background:#FFAB0E; width:200px; border-radius:20px 0px
0px 20px;">
<!--
<img src="../../assets/images/bg.png" height="300" width="170">
<b-card-img src="https://picsum.photos/400/400/?image=20" alt="Image"
class="rounded-0"></b-card-img>
-->
</b-col>
<b-col md="7">
<b-card-body>
<h2 style="color:black; font-weight:bold">Backoffice</h2>
<span style="font-size:0.8em; color:gray">Veuillez-vous connecter à votre
compte</span>
<div style="height:40px"></div>
<b-card-text style="">
<div style="width:250px;">
<b-form>
<b-form-text>
Indiquez votre numéro de téléphone
</b-form-text>
<b-form-group>
<b-form-input type="text" v-model="phone" aria-describedby="tel"
placeholder="+225 00 07 05 01 00" required style="border-radius:10px 10px 10px 10px;
text-align:center"></b-form-input>
</b-form-group>
<b-button id="sign-in-button" #click="sendCode" block
variant="success active" style="border-radius:10px 10px 10px 10px;">
Continuer
</b-button>
<div id="recaptcha-container"></div><br>
</b-form>
</div>
</b-card-text>
<div style="height:40px"></div>
<div class="row mt-3">
<div class="col-12 text-right">
<router-link to="/LoginCompte" class="text-light">
<small style="color:gray">Login Compte</small>
</router-link> <br>
<router-link to="/verify" class="text-light">
<small style="color:gray">Verify</small>
</router-link>
</div>
</div>
</b-card-body>
</b-col>
</b-row>
</b-card>
</div>
</b-row>
</b-container>
</div>
</div>
</template>
<script>
import firebase from "firebase";
export default {
name: 'login',
data(){
return{
phone: '',
appVerifier : '',
}
},
methods :{
sendCode(){
if(this.phone.length != 10){
alert('Numero invalide !');
}else{
//
let countryCode = '+225' //civ
let phoneNumber = countryCode + this.phone
let nextpage = this
//
let appVerifier = this.appVerifier
//
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult)
{
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
//
//alert('Continuer')
nextpage.$router.push({path:'/Verify'})
}).catch(function (error) {
// Error; SMS not sent
// ...
alert('Erreur, code non envoyer')
});
}
},
//
//
verifyCode(){
if(this.phone.length != 10 || this.otpCode.length != 6){
alert('Numero ou Format invalide !');
}else{
//
let vm = this
let code = this.otpCode
//
window.confirmationResult.confirm(code).then(function (result) {
// User signed in successfully.
var user = result.user;
// ...
//route to set password !
vm.$router.push({path:'./LoginCompte'})
}).catch(function (error) {
// User couldn't sign in (bad verification code?)
// ...
});
}
},
initReCaptcha(){
setTimeout(()=>{
let vm = this
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container',
{
'size': 'invisible',
'callback': function(response)
{
// reCAPTCHA solved, allow signInWithPhoneNumber.
// ...
},
'expired-callback': function()
{
// Response expired. Ask user to solve reCAPTCHA again.
// ...
}
});
//
this.appVerifier = window.recaptchaVerifier
},1000)
}
},
created(){
this.initReCaptcha()
}
}
</script>
<style>
</style>
Page 2: (Verify.vue)
<template>
<div class="row justify-content-center" style="background-color:#34B76E;">
<div class="col-lg-5 col-md-7">
<b-container>
<b-row class="vh-100" align-v="center">
<div>
<b-card no-body class="" style="max-width: 540px; background-color:#EFF3F8;
border-radius:20px 20px 20px 20px;">
<b-row no-gutters class="" style="">
<b-col md="5" style="background:#FFAB0E; width:200px; border-radius:20px 0px
0px 20px;">
</b-col>
<b-col md="7">
<b-card-body>
<h2 style="color:black; font-weight:bold">Backoffice</h2>
<span style="font-size:0.8em; color:gray">Veuillez-vous connecter à votre
compte</span>
<div style="height:40px"></div>
<b-card-text style="">
<div style="width:250px;">
<b-form hidden>
<b-form-text id="tel">
Indiquez votre numéro de téléphone
</b-form-text>
<b-input-group class="mt-3">
<b-form-input type="text" v-model="phone" id="tel" aria-
describedby="tel" placeholder="00 07 05 01 00" required style="border-radius:10px
0px 0px 10px; text-align:center"></b-form-input>
<b-input-group-append>
<b-button id="sign-in-button" #click="sendCode" block
variant="success active" style="height:40px; border-radius:0px 10px 10px 0px;">
<i class="fa fa-chevron-circle-right"></i>
</b-button>
</b-input-group-append>
</b-input-group>
<div id="recaptcha-container"></div><br>
</b-form>
<div>
<b-form>
<b-form-text id="tel">
Veuillez indiquer le code reçu par SMS
</b-form-text>
<b-form-group>
<b-form-input type="text" v-model="otpCode" aria-
describedby="tel" placeholder="00 00 00" required style="border-radius:10px 10px
10px 10px; text-align:center"></b-form-input>
</b-form-group>
<b-button #click="verifyCode" block variant="success active"
style="border-radius:10px 10px 10px 10px;">Se connecter</b-button>
</b-form>
</div>
</div>
</b-card-text>
<div style="height:40px"></div>
<div class="row mt-3">
<div class="col-12 text-right">
<router-link to="/" class="text-light">
<small style="color:gray">Vous n'avez pas reçu de code?</small>
</router-link>
</div>
</div>
</b-card-body>
</b-col>
</b-row>
</b-card>
</div>
</b-row>
</b-container>
</div>
</div>
</template>
<script>
import firebase from "firebase";
export default {
name: 'verify',
data(){
return{
phone: '',
appVerifier : '',
otpCode : ''
}
},
methods :{
sendCode(){
if(this.phone.length != 10){
alert('Numero invalid !');
}else{
//
let countryCode = '+225' //civ
let phoneNumber = countryCode + this.phone
//let nextpage = this
//
let appVerifier = this.appVerifier
//
firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier)
.then(function (confirmationResult) {
// user in with confirmationResult.confirm(code).
window.confirmationResult = confirmationResult;
//
alert('Continuer')
//nextpage.$router.push({path:'/Verify'})
}).catch(function (error) {
// Error; SMS not sent
// ...
alert('Erreur, code non envoyer')
});
}
},
//
verifyCode(){
if(this.phone.length != 10 || this.otpCode.length != 6){
alert('Numero ou Format invalide !');
}else{
//
let vm = this
let code = this.otpCode
//
window.confirmationResult.confirm(code).then(function (result) {
// User signed in successfully.
var user = result.user;
// ...
//route to set password !
vm.$router.push({path:'./LoginCompte'})
}).catch(function (error) {
// User couldn't sign in (bad verification code?)
// ...
});
}
},
initReCaptcha(){
setTimeout(()=>{
let vm = this
window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container',
{
'size': 'invisible',
'callback': function(response) {
// reCAPTCHA solved, allow signInWithPhoneNumber.
// ...
},
'expired-callback': function() {
// Response expired. Ask user to solve reCAPTCHA again.
// ...
}
});
//
this.appVerifier = window.recaptchaVerifier
},1000)
}
},
created(){
this.initReCaptcha()
}
}
</script>
<style>
</style>

data to a v model ? do i have to initialize the data if its coming from another component?

I am trying to put the value of content data from another component in this.newTutorial.content push function.
so far I get the data now I need to put it inside my v-model, like putting data from a v-model to another v-model? I tried this this.newTutorial.content = this.content; on the created function but no luck yet says type check failed for prop "rules". Expected Array, got Boolean with value true.
here is my code:
<style scoped>
img.preview {
width:200px;
}
.v-btn {
height: 50px !important;
min-width: 50px !important;
}
</style>
<template>
<div id="app">
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn style="z-index:9;" color="blue lighten-1" dark rounded v-bind="attrs" v-on="on" fixed left>
<v-tooltip right >
<template v-slot:activator="{ on, attrs }">
<v-icon fab dark v-bind="attrs" v-on="on">
mdi-plus
</v-icon>
</template>
<img class="monk-ico" src="https://celfonica.s3-us-west-1.amazonaws.com/logos/monk-circle+50px.png">
<span style="display:inline;">
Add Tutorial
</span>
</v-tooltip>
</v-btn>
</template>
<div class="left">
<v-btn color="primary" #click="dialog = false" width="10px">
<v-icon>
mdi-close
</v-icon>
</v-btn>
</div>
<div class="panel-heading">
</div>
<div>
<h1>Tutorial form</h1>
<h3> create one</h3>
<form id="form" class="form-inline" v-on:submit.prevent="addTutorial">
<v-divider class="m-tb-20"></v-divider>
<h4>Author details</h4>
<div class="form-group">
<v-text-field :rules="nameRules" required label="First Name" type="text" id="tutorialFirst" class="form-control" v-model="newTutorial.first">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="nameRules" required label="Last Name" type="text" id="tutorialLast" class="form-control" v-model="newTutorial.last">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="emailRules" required label="Email" type="text" id="tutorialEmail" class="form-control" v-model="newTutorial.email">
</v-text-field>
</div>
<div class="form-goup">
<!-- Img upload input field-->
<div>
<h4 class="m-tb-20">Upload tutorial picture:</h4>
<input class="form-control" type="file" #change="previewImage" accept="image/+">
<br><v-btn class="m-tb-20" #click=" onUpload();"><v-icon>mdi-upload</v-icon></v-btn>
</div>
<div>
<p> Progress: {{uploadValue.toFixed()+"%"}}
<progress :value="uploadValue" max="100"></progress>
</p>
</div>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4>Tutorial content</h4>
<div class="form-group">
<v-select required label="Language"
id="tutorialLanguage" v-model="newTutorial.language"
multiple type="text" autocomplete tags :items="languages" class="form-control">
<template slot="selection" slot-scope="data">
<v-btn>
{{ data.item }}
</v-btn>
</template>
</v-select>
</div>
<div class="form-group">
<v-text-field :rules="titleRules" required label="Tutorial Title" type="text" id="tutorialTitle" class="form-control" v-model="newTutorial.title">
</v-text-field>
</div>
<!--tiptap--> <v-card >
<div >
<editor-menu-bar v-on:submit.prevent="addTutorial" :editor="editor" v-slot="{ commands, isActive }">
<div>
<v-btn :class="{ 'is-active': isActive.bold() }" #click="commands.bold">
<v-icon class="mdi mdi-format-bold mdi-24px"> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.italic() }" #click="commands.italic">
<v-icon class="mdi mdi-format-italic mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.underline() }" #click="commands.underline">
<v-icon class="mdi mdi-format-underline mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.code() }" #click="commands.code">
<v-icon class="mdi mdi-code-tags mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.link() }" #click="commands.link">
<v-icon class="mdi mdi-link mdi-24px"> </v-icon>
</v-btn>
<v-divider></v-divider>
</div>
</editor-menu-bar>
<editor-content label="Tutorial content" :editor="editor" v-model="content" />
</div>
</v-card>
<div class="form-group">
<v-text-field required label="Date" class="form-control" type='date' v-model='newTutorial.date'>
</v-text-field>
</div>
<div class="form-group">
<v-text-field required label="Tutorial Sample Code Link" type="url" id="tutorialCode" class="form-control" v-model="newTutorial.code">
</v-text-field>
</div>
<div>
<br>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4> Preview </h4>
<v-card class="m-tb-20" v-model="newTutorial">
<img class="preview " :src="picture"><br>
<v-card-title class="center">{{ newTutorial.title }} </v-card-title>
<v-card-subtitle> {{ newTutorial.first }} {{ newTutorial.last }} </v-card-subtitle>
<v-divider class="m-tb-20"></v-divider>
<v-card-text>{{ newTutorial.content }}</v-card-text>
{{ content }}
<v-card-text>
<h5>{{ newTutorial.language }}</h5>
<h5>{{ newTutorial.email }}</h5>
<h5>{{ newTutorial.date }}</h5>
</v-card-text>
</v-card>
<!-- Form push btn -->
<v-btn class="m-tb-20" #click="markcompleted();" type="submit" small color="primary" dark>
{{ displayText }}
</v-btn>
</form>
</div>
</v-dialog>
</div>
</template>
<script>
import firebase from '../plugins/firebase'
import EditorContent from "../components/EditorContent";
import toastr from 'toastr';
// to debug multiple Fire apps
//if (!firebase.apps.length) {
// firebase.initializeApp(config);
// this.newTutorial.userID= uid;
//}
import { Editor, EditorMenuBar } from 'tiptap'
import {
Blockquote,
CodeBlock,
HardBreak,
Heading,
OrderedList,
BulletList,
ListItem,
TodoItem,
TodoList,
Bold,
Code,
Italic,
Link,
Strike,
Underline,
History,
} from 'tiptap-extensions'
let db = firebase.database();
let messagesRef = db.ref('tutorials');
export default {
name: 'tutform',
firebase: {
tutorials: messagesRef
},
components: {
EditorMenuBar,
EditorContent,
},
data() {
return {
content: null,
editor: new Editor({
extensions: [
new Blockquote(),
new CodeBlock(),
new HardBreak(),
new Heading({ levels: [1, 2, 3] }),
new BulletList(),
new OrderedList(),
new ListItem(),
new TodoItem(),
new TodoList(),
new Bold(),
new Code(),
new Italic(),
new Link(),
new Strike(),
new Underline(),
new History(),
],
content: '',
}),
imageData:null,
picture:null,
uploadValue: 0,
dialog: false,
displayText: 'Push me!',
newTutorial: {
first: '',
email: '',
last: '',
language: [],
title: '',
content: '',
date: '',
picture:'',
code: '',
},
languages: [
'Html', 'CSS', 'VUE', 'React', 'Ruby', 'JS', 'SASS', 'Python','PHP','C#','JAVA','Other',
],
nameRules: [
v => !!v || 'you must type something',
v => v.length <= 10 || 'hum.. this monk smelling somthing strange... must be less than 10 characters',
],
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+/.test(v) || 'Please enter a valid email containing # ',
],
contentRules: [
v => !!v || 'Content is required amigo!'
],
titleRules: [
v => !!v || 'Tittle is required buddy!',
v => v.length <= 100 || 'Woots!, Lets try making this one shorter'
]
}
},
methods: {
previewImage(event){
this.uploadValue=0;
this.picture=null;
this.imageData=event.target.files[0];
},
onUpload() {
this.picture=null;
const storageRef=firebase.storage().ref(`tutorials/images/${this.imageData.name}`).put(this.imageData);
storageRef.on(`state_changed`, snapshot=>{
this.uploadValue=(snapshot.bytesTransferred/snapshot.totalBytes)*100;
}, error=>{console.log(error.message)},
()=>{this.uploadValue=100;
storageRef.snapshot.ref.getDownloadURL().then((url)=>{
this.picture=url;
this.newTutorial.picture = url;
console.log(this.picture);
toastr.success('Image Uploaded successfully');
})}
)
},
addTutorial: function() {
messagesRef.child(this.newTutorial.userID).push(this.newTutorial);
this.newTutorial.first = '';
this.newTutorial.last = '';
this.newTutorial.content = '';
this.newTutorial.email = '';
this.newTutorial.language = '';
this.newTutorial.title = '';
this.newTutorial.date = '',
this.newTutorial.picture= '',
this.newTutorial.code= '',
toastr.success('Horray! message sent successfully');
this.displayText = 'Nice job!';
this.nameRules = true;
this.emailRules = true;
this.contentRules = true;
this.titleRules = true;
},
markcompleted: function() {
this.displayText = 'hum.. somthing still missing';
}
},
// this functions trow in uid from user in data valu to {uid}
created: function(){
var user = firebase.auth().currentUser;
var uid;
if (user != null) {
uid = user.uid; // The user's ID, unique to the Firebase project. Do NOT use
// this value to authenticate with your backend server, if
// you have one. Use User.getToken() instead.
}
this.newTutorial.userID = uid;
this.newTutorial.content = this.content;
},
beforeDestroy() {
this.editor.destroy()
}
}
</script>
Then my component code for importing the content data :
<script>
export default {
name: 'EditorContent',
props: {
editor: {
default: null,
type: Object
},
value: {
default: "",
type: String
}
},
watch: {
editor: {
immediate: true,
handler(editor) {
if (!editor || !editor.element) return;
this.editor.setContent(this.value);
this.editor.on("update", ({ getHTML }) => {
this.$emit("input", getHTML());
});
this.$nextTick(() => {
this.$el.appendChild(editor.element.firstChild);
editor.setParentComponent(this);
});
}
},
value: {
handler(value) {
this.editor.setContent(value);
}
}
},
render(createElement) {
return createElement("div");
},
beforeDestroy() {
this.editor.element = this.$el;
}
};
</script>
vue data:
console error:
I see you are trying to implement two-way binding with v-model on a custom element.
You will need to create a prop called content, then emit input events to the parent component. Check this sample code out:
Child component:
<template>
<div class="date-picker">
Month: <input type="number" ref="monthPicker" :value="value.month" #input="updateDate()"/>
Year: <input type="number" ref="yearPicker" :value="value.year" #input="updateDate()"/>
</div>
</template>
<script>
export default {
props: ['value'],
methods: {
updateDate() {
this.$emit('input', {
month: +this.$refs.monthPicker.value,
year: +this.$refs.yearPicker.value
})
}
}
};
</script>
Parent component:
<template>
<div class="wrapper">
<date-picker v-model="date"></date-picker>
<p>
Month: {{date.month}}
Year: {{date.year}}
</p>
</div>
</template>
<script>
import DatePicker from './DatePicker.vue';
export default {
components: {
DatePicker
},
data() {
return {
date: {
month: 1,
year: 2017
}
}
}
})
</script>
For more information, check out this short article
The solution was to remove my content v-model inside the editor content tag and add the v-model I had made for sending the tutorials. here is the working code.
code:
<template>
<div>
<editor-content label="Tutorial content" :editor="editor" v-model="newTutorial.content" />
</div>
</template>
inside the script in my data :
newTutorial: {
first: '',
email: '',
last: '',
language: [],
title: '',
content: '',
date: '',
picture:'',
code: '',
},
inside the script in my send funciton :
addTutorial: function() {
messagesRef.child(this.newTutorial.userID).push(this.newTutorial);
this.newTutorial.first = '';
this.newTutorial.last = '';
this.newTutorial.content = '';
this.newTutorial.email = '';
this.newTutorial.language = '';
this.newTutorial.title = '';
this.newTutorial.date = '',
this.newTutorial.picture= '',
this.newTutorial.code= '',
toastr.success('Horray! message sent successfully');
this.displayText = 'Nice job!';
this.nameRules = true;
this.emailRules = true;
this.contentRules = true;
this.titleRules = true;
},
data in db :
form component code:
<style scoped>
img.preview {
width:200px;
}
.v-btn {
height: 50px !important;
min-width: 50px !important;
}
</style>
<template>
<div id="app">
<v-dialog v-model="dialog" width="500">
<template v-slot:activator="{ on, attrs }">
<v-btn style="z-index:9;" color="blue lighten-1" dark rounded v-bind="attrs" v-on="on" fixed left>
<v-tooltip right >
<template v-slot:activator="{ on, attrs }">
<v-icon fab dark v-bind="attrs" v-on="on">
mdi-plus
</v-icon>
</template>
<img class="monk-ico" src="https://celfonica.s3-us-west-1.amazonaws.com/logos/monk-circle+50px.png">
<span style="display:inline;">
Add Tutorial
</span>
</v-tooltip>
</v-btn>
</template>
<div class="left">
<v-btn color="primary" #click="dialog = false" width="10px">
<v-icon>
mdi-close
</v-icon>
</v-btn>
</div>
<div class="panel-heading">
</div>
<div>
<h1>Tutorial form</h1>
<h3> create one</h3>
<form id="form" class="form-inline" v-on:submit.prevent="addTutorial">
<v-divider class="m-tb-20"></v-divider>
<h4>Author details</h4>
<div class="form-group">
<v-text-field :rules="nameRules" required label="First Name" type="text" id="tutorialFirst" class="form-control" v-model="newTutorial.first">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="nameRules" required label="Last Name" type="text" id="tutorialLast" class="form-control" v-model="newTutorial.last">
</v-text-field>
</div>
<div class="form-group">
<v-text-field :rules="emailRules" required label="Email" type="text" id="tutorialEmail" class="form-control" v-model="newTutorial.email">
</v-text-field>
</div>
<div class="form-goup">
<!-- Img upload input field-->
<div>
<h4 class="m-tb-20">Upload tutorial picture:</h4>
<input class="form-control" type="file" #change="previewImage" accept="image/+">
<br><v-btn class="m-tb-20" #click=" onUpload();"><v-icon>mdi-upload</v-icon></v-btn>
</div>
<div>
<p> Progress: {{uploadValue.toFixed()+"%"}}
<progress :value="uploadValue" max="100"></progress>
</p>
</div>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4>Tutorial content</h4>
<div class="form-group">
<v-select required label="Language"
id="tutorialLanguage" v-model="newTutorial.language"
multiple type="text" autocomplete tags :items="languages" class="form-control">
<template slot="selection" slot-scope="data">
<v-btn>
{{ data.item }}
</v-btn>
</template>
</v-select>
</div>
<div class="form-group">
<v-text-field :rules="titleRules" required label="Tutorial Title" type="text" id="tutorialTitle" class="form-control" v-model="newTutorial.title">
</v-text-field>
</div>
<!--tiptap--> <v-card >
<div >
<editor-menu-bar v-on:submit.prevent="addTutorial" :editor="editor" v-slot="{ commands, isActive }">
<div>
<v-btn :class="{ 'is-active': isActive.bold() }" #click="commands.bold">
<v-icon class="mdi mdi-format-bold mdi-24px"> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.italic() }" #click="commands.italic">
<v-icon class="mdi mdi-format-italic mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.underline() }" #click="commands.underline">
<v-icon class="mdi mdi-format-underline mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.code() }" #click="commands.code">
<v-icon class="mdi mdi-code-tags mdi-24px "> </v-icon>
</v-btn>
<v-btn :class="{ 'is-active': isActive.link() }" #click="commands.link">
<v-icon class="mdi mdi-link mdi-24px"> </v-icon>
</v-btn>
<v-divider></v-divider>
</div>
</editor-menu-bar>
<editor-content label="Tutorial content" :editor="editor" v-model="newTutorial.content" />
</div>
</v-card>
<div class="form-group">
<v-text-field required label="Date" class="form-control" type='date' v-model='newTutorial.date'>
</v-text-field>
</div>
<div class="form-group">
<v-text-field required label="Tutorial Sample Code Link" type="url" id="tutorialCode" class="form-control" v-model="newTutorial.code">
</v-text-field>
</div>
<div>
<br>
</div>
<v-divider class="m-tb-20"></v-divider>
<h4> Preview </h4>
<v-card class="m-tb-20" v-model="newTutorial">
<img class="preview " :src="picture"><br>
<v-card-title class="center">{{ newTutorial.title }} </v-card-title>
<v-card-subtitle> {{ newTutorial.first }} {{ newTutorial.last }} </v-card-subtitle>
<v-divider class="m-tb-20"></v-divider>
<v-card-text>{{ newTutorial.content }}</v-card-text>
<v-card-text>
<h5>{{ newTutorial.language }}</h5>
<h5>{{ newTutorial.email }}</h5>
<h5>{{ newTutorial.date }}</h5>
</v-card-text>
</v-card>
<!-- Form push btn -->
<v-btn class="m-tb-20" #click="markcompleted();" type="submit" small color="primary" dark>
{{ displayText }}
</v-btn>
</form>
</div>
</v-dialog>
</div>
</template>
<script>
import firebase from '../plugins/firebase'
import EditorContent from "../components/EditorContent";
import toastr from 'toastr';
// to debug multiple Fire apps
//if (!firebase.apps.length) {
// firebase.initializeApp(config);
// this.newTutorial.userID= uid;
//}
import { Editor, EditorMenuBar } from 'tiptap'
import {
Blockquote,
CodeBlock,
HardBreak,
Heading,
OrderedList,
BulletList,
ListItem,
TodoItem,
TodoList,
Bold,
Code,
Italic,
Link,
Strike,
Underline,
History,
} from 'tiptap-extensions'
let db = firebase.database();
let messagesRef = db.ref('tutorials');
export default {
name: 'tutform',
firebase: {
tutorials: messagesRef
},
components: {
EditorMenuBar,
EditorContent,
},
data() {
return {
editor: new Editor({
extensions: [
new Blockquote(),
new CodeBlock(),
new HardBreak(),
new Heading({ levels: [1, 2, 3] }),
new BulletList(),
new OrderedList(),
new ListItem(),
new TodoItem(),
new TodoList(),
new Bold(),
new Code(),
new Italic(),
new Link(),
new Strike(),
new Underline(),
new History(),
],
content: '',
}),
imageData:null,
picture:null,
uploadValue: 0,
dialog: false,
displayText: 'Push me!',
newTutorial: {
first: '',
email: '',
last: '',
language: [],
title: '',
content: '',
date: '',
picture:'',
code: '',
},
languages: [
'Html', 'CSS', 'VUE', 'React', 'Ruby', 'JS', 'SASS', 'Python','PHP','C#','JAVA','Other',
],
nameRules: [
v => !!v || 'you must type something',
v => v.length <= 10 || 'hum.. this monk smelling somthing strange... must be less than 10 characters',
],
emailRules: [
v => !!v || 'E-mail is required',
v => /.+#.+/.test(v) || 'Please enter a valid email containing # ',
],
contentRules: [
v => !!v || 'Content is required amigo!'
],
titleRules: [
v => !!v || 'Tittle is required buddy!',
v => v.length <= 100 || 'Woots!, Lets try making this one shorter'
]
}
},
methods: {
previewImage(event){
this.uploadValue=0;
this.picture=null;
this.imageData=event.target.files[0];
},
onUpload() {
this.picture=null;
const storageRef=firebase.storage().ref(`tutorials/images/${this.imageData.name}`).put(this.imageData);
storageRef.on(`state_changed`, snapshot=>{
this.uploadValue=(snapshot.bytesTransferred/snapshot.totalBytes)*100;
}, error=>{console.log(error.message)},
()=>{this.uploadValue=100;
storageRef.snapshot.ref.getDownloadURL().then((url)=>{
this.picture=url;
this.newTutorial.picture = url;
console.log(this.picture);
toastr.success('Image Uploaded successfully');
})}
)
},
addTutorial: function() {
messagesRef.child(this.newTutorial.userID).push(this.newTutorial);
this.newTutorial.first = '';
this.newTutorial.last = '';
this.newTutorial.content = '';
this.newTutorial.email = '';
this.newTutorial.language = '';
this.newTutorial.title = '';
this.newTutorial.date = '',
this.newTutorial.picture= '',
this.newTutorial.code= '',
toastr.success('Horray! message sent successfully');
this.displayText = 'Nice job!';
this.nameRules = true;
this.emailRules = true;
this.contentRules = true;
this.titleRules = true;
},
markcompleted: function() {
this.displayText = 'hum.. somthing still missing';
}
},
// this functions trow in uid from user in data valu to {uid}
created: function(){
var user = firebase.auth().currentUser;
var uid;
if (user != null) {
uid = user.uid; // The user's ID, unique to the Firebase project. Do NOT use
// this value to authenticate with your backend server, if
// you have one. Use User.getToken() instead.
}
this.newTutorial.userID = uid;
},
beforeDestroy() {
this.editor.destroy()
}
}
</script>
child component:
<script>
export default {
name: 'EditorContent',
props: {
editor: {
default: null,
type: Object
},
value: {
default: "",
type: String
}
},
watch: {
editor: {
immediate: true,
handler(editor) {
if (!editor || !editor.element) return;
this.editor.setContent(this.value);
this.editor.on("update", ({ getHTML }) => {
this.$emit("input", getHTML());
});
this.$nextTick(() => {
this.$el.appendChild(editor.element.firstChild);
editor.setParentComponent(this);
});
}
},
value: {
handler(value) {
this.editor.setContent(value);
}
}
},
render(createElement) {
return createElement("div");
},
beforeDestroy() {
this.editor.element = this.$el;
}
};
</script>
Hope it helps someone, I did this to add tip tap editor.

How to use object as a tag while using the Form Tags component in bootstrap vue

To explain the question, using the code from documentation as below:
<template>
<div>
<b-form-tags v-model="value" no-outer-focus class="mb-2">
<template v-slot="{ tags, inputAttrs, inputHandlers, addTag, removeTag }">
<b-input-group aria-controls="my-custom-tags-list">
<input
v-bind="inputAttrs"
v-on="inputHandlers"
placeholder="New tag - Press enter to add"
class="form-control">
<b-input-group-append>
<b-button #click="addTag()" variant="primary">Add</b-button>
</b-input-group-append>
</b-input-group>
<ul
id="my-custom-tags-list"
class="list-unstyled d-inline-flex flex-wrap mb-0"
aria-live="polite"
aria-atomic="false"
aria-relevant="additions removals"
>
<!-- Always use the tag value as the :key, not the index! -->
<!-- Otherwise screen readers will not read the tag
additions and removals correctly -->
<b-card
v-for="tag in tags"
:key="tag"
:id="`my-custom-tags-tag_${tag.replace(/\s/g, '_')}_`"
tag="li"
class="mt-1 mr-1"
body-class="py-1 pr-2 text-nowrap"
>
<strong>{{ tag }}</strong>
<b-button
#click="removeTag(tag)"
variant="link"
size="sm"
:aria-controls="`my-custom-tags-tag_${tag.replace(/\s/g, '_')}_`"
>remove</b-button>
</b-card>
</ul>
</template>
</b-form-tags>
</div>
</template>
<script>
export default {
data() {
return {
value: ['apple', 'orange', 'banana', 'pear', 'peach']
}
}
}
</script>
The above code gives the output as follows:
But when I have the values as an array of objects instead of short strings how to render the properties of the objects as tag elements?
for example: If I have value: [{name: 'apple', color: 'red'}, {name:'mango', color: 'yellow'}] then how to get the same output as above?
I tried something something like <strong>{{ tag.name }}</strong> and it doesn't work and gives me only empty tags to remove as follows:
Any ideas on how to achieve what I wanted to do here?
What you're trying to do is not support yet according to this issue
You'll have to map your array of objects to a string array and utilize that.
Then once you're ready to "use" your tags, you could map them back based on the original objects.
Here's a rather simple example of what could be done.
new Vue({
el: "#app",
computed: {
mappedTags() {
/* This is case sensitive */
return this.options.filter(option => this.value.includes(option.name))
}
},
data: {
value: [],
options: [{
name: 'Mango',
color: 'Orange'
},
{
name: 'Orange',
color: 'Orange'
},
{
name: 'Lemon',
color: 'Yellow'
},
{
name: 'Apple',
color: 'Red'
},
{
name: 'Banana',
color: 'Yellow'
},
]
}
})
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/bootstrap-vue#2.4.0/dist/bootstrap-vue.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.2.2/dist/bootstrap-vue.js"></script>
<style>
/* Only added for better visibility on the text */
.text-stroke {
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
}
</style>
<div id="app" class="p-3">
Valid (<b>Casesensitive</b>) tags - [Banana, Apple, Orange, Mango]
<b-form-tags v-model="value" no-outer-focus class="mb-2">
<template v-slot="{ tags, inputAttrs, inputHandlers, addTag, removeTag }">
<b-input-group aria-controls="my-custom-tags-list">
<b-input
v-bind="inputAttrs"
v-on="inputHandlers"
placeholder="New tag - Press enter to add"></b-input>
<b-input-group-append>
<b-button #click="addTag()" variant="primary">Add</b-button>
</b-input-group-append>
</b-input-group>
<ul class="list-unstyled d-inline-flex flex-wrap mb-0">
<b-card
v-for="tag in mappedTags"
:key="tag.name"
:id="`my-custom-tags-tag_${tag.name.replace(/\s/g, '_')}_`"
tag="li"
class="mt-1 mr-1"
body-class="py-1 pr-2 text-nowrap"
>
<strong :style="`color: ${tag.color}`" class="text-stroke">
{{ tag.name }}
</strong>
<b-button
#click="removeTag(tag.name)"
variant="link"
>
Remove
</b-button>
</b-card>
</ul>
</template>
</b-form-tags>
{{ mappedTags }}
</div>
Replace {{ tag.name }} with {{ JSON.parse(tag).name }}.
That should solve the problem.
You have a similar example where I needed the input of the user and its selection and gather does two inputs values inside an array of objects.
You need to add a v-model to your inputs this way you can pass it to a custom function
<b-form-input
v-model="werkTagNombre"
<b-form-select
v-model="werkTagExp"
...
"#click="addingWerkTag({werkTagNombre, werkTagExp})"
Complete template code
<b-form-tags v-model="freelance.tags.nombre">
<template v-slot="{ inputAttrs, inputHandlers }">
<b-input-group aria-controls="tags-list">
<b-form-input
v-model="werkTagNombre"
v-bind="inputAttrs"
v-on="inputHandlers"
placeholder="Seleccione"
class="werk-input werk-shadow-input mr-2"
>
</b-form-input>
<b-input-group-append>
<b-form-select
v-model="werkTagExp"
:options="experienciaOptions"
class="werk-input werk-shadow-input ml-2 mr-2"
>
</b-form-select>
</b-input-group-append>
<b-input-group-append>
<b-button class="shadow-none ml-2" style="border-radius:6px;"#click="addingWerkTag({werkTagNombre, werkTagExp})"> ADD</b-button>
</b-input-group-append>
</b-input-group>
<span style="font-size: 10px; color: #a5a5a5;">TAGS:</span>
<ul id="tags-list" class="list-unstyled d-inline-flex flex-wrap mb-0 mt-1">
<b-badge
v-for="(werkTag, index) in freelance.tags"
:key="index"
tag="li"
class="mx-1 my-1 werk-tags"
>
{{werkTag.nombre}} {{werkTag.experiencia}}
</b-badge>
</ul>
</template>
</b-form-tags>
Script
export default {
data() {
return {
freelance: {
tags: [
{
nombre:" Test name",
experiencia:1
}
],
}
...
werkTagNombre: '',
werkTagExp: '0',
experienciaOptions: [
{ value: '0', text: 'Selecciona'},
{ value: 1, text: 'menor a 2 años'},
{ value: 2, text: '3 a 5 años'},
{ value: 3, text: 'mayor a 5 años'},
]
...
methods:{
addingWerkTag(valor){
this.freelance.tags.push({nombre: valor.werkTagNombre, exp: valor.werkTagExp});
},
...
}
If you want you can also pass the addTag default function like this:
Inside the template slot add the functions like "addtag"
<template v-slot="{ inputAttrs, inputHandlers, addTag }">
...
So you can add it to your custom function like this:
#click="addingWerkTag({ werkTagNombre, werkTagExp, addTag })"

Printing received JSON into a table

I have a VueJS template which includes a table and a function that sends out a request to the backend API with the specified key and then receives back a JSON response.
What I am having trouble is with writing the function so it takes the output it receives and then prints into the table.
JSON Sample:
{"driver_id":1,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"13","driver_trailer":"83","driver_status":"driving","has_violations":false},
{"driver_id":2,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"58","driver_trailer":"37","driver_status":"sleeping","has_violations":true},
{"driver_id":3,"driver_name":"{driver_first_name}, {driver_last_name}","driver_truck":"80","driver_trailer":"27","driver_status":"driving","has_violations":true},
Here is my code:
<template>
<b-container fluid>
<!--Search Controls-->
<b-row>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Filter" class="mb-0">
<b-input-group>
<b-form-input v-model="filter" placeholder="Type to Search" />
<b-input-group-append>
<b-btn :disabled="!filter" #click="filter = ''">Clear</b-btn>
</b-input-group-append>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Sort" class="mb-0">
<b-input-group>
<b-form-select v-model="sortBy" :options="sortOptions">
<option slot="first" :value="null">-- none --</option>
</b-form-select>
<b-form-select :disabled="!sortBy" v-model="sortDesc" slot="append">
<option :value="false">Asc</option>
<option :value="true">Desc</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Sort direction" class="mb-0">
<b-input-group>
<b-form-select v-model="sortDirection" slot="append">
<option value="asc">Asc</option>
<option value="desc">Desc</option>
<option value="last">Last</option>
</b-form-select>
</b-input-group>
</b-form-group>
</b-col>
<b-col md="6" class="my-1">
<b-form-group horizontal label="Per page" class="mb-0">
<b-form-select :options="pageOptions" v-model="perPage" />
</b-form-group>
</b-col>
</b-row>
<!--Search Controls-->
<!-- Main table element -->
<b-table show-empty
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-direction="sortDirection"
#filtered="onFiltered"
>
<template slot="truck_number" slot-scope="row">{{row.value.driver_truck}}</template>
<template slot="trailer_number" slot-scope="row">{{row.value.driver_trailer}}</template>
<template slot="violation_date" slot-scope="row">{{row.value.violation_date}}</template>
<!-- View Specific Violation -->
<template slot="actions" slot-scope="row">
<b-button #click="getSpecificViolation(logbook_id)" id="view_specific_violation">View Violation</b-button>
</template>
</b-table>
<b-row>
<b-col md="6" class="my-1">
<b-pagination :total-rows="totalRows" :per-page="perPage" v-model="currentPage" class="my-0" />
</b-col>
</b-row>
</b-container>
</template>
<script>
export default {
name: 'List of Violations',
data () {
return {
items: items,
fields: [
{ key: 'truck_number', label: 'Truck Number', sortable: true, 'class': 'text-center' },
{ key: 'trailer_number', label: 'Trailer Number', sortable: true, 'class': 'text-center' },
{ key: 'violation_date', label: 'Date of Violation' },
{ key: 'actions', label: 'Actions' }
],
currentPage: 1,
perPage: 5,
totalRows: items.length,
pageOptions: [ 5, 10, 15 ],
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
filter: null,
}
},
},
computed: {
sortOptions () {
return this.fields
.filter(f => f.sortable)
.map(f => { return { text: f.label, value: f.key } })
}
},
created() {
this.id = this.$route.params.id;
},
methods: {
onFiltered (filteredItems) {
// Trigger pagination to update the number of buttons/pages due to filtering
this.totalRows = filteredItems.length
this.currentPage = 1
}
navigate() {
router.go(-1);
}
//Send Request all Violations for Driver_ID
axios.post('http://localhost:3030/api/allViolations/driverID',
this.driver_id, // the data to post
{ headers: {
'Content-type': 'application/x-www-form-urlencoded',
}
}).then(response => ....???); //print api json response
}
}
</script>
First of all, your code structure is disrupted. You shouldn't define computed,created and methods functions outside the export default module.
I created a sandbox for you just to rearrange the code and assign the value of response to items variable.
.then(response => (this.items = response.data));
If i assume correctly you are using Bootstrap-Vue plugin. After obtaining the items object you can follow the documentation from here
First of all, your initial value for items in data should be [] (an empty array).
After that, pretty much all you should need to do is set this.items = response.data when you get the JSON back. Although the code you pasted seems to be bad (the axios.post call is directly under methods).

How to use same popover for more than one target?

I am trying to use vue bootstrap popover for more than one target in vue js 2.x. i am able to change target property dynamically but it's still targeting the last target always. Is that possible with popover control or not? sample code as follows:
JSFiddle Sample is here
My example code is as follows:
new Vue({
el: '#app',
data: {
name: '',
email: '',
popover: {
target: '',
show: false,
message: ''
}
},
methods: {
callPopover() {
setTimeout(() => {
this.setPopover()
}, 3000)
},
setPopover() {
this.popover.target = 'txtName'
this.popover.show = true
if (!this.name) {
this.popover.message = 'please enter your name to register!!'
} else if (this.name === 'John') {
this.popover.message = 'Hey John!!'
} else if (!this.email) {
this.popover.target = 'txtEmail'
this.popover.message = 'email example is xx#yy.com'
} else {
this.popover.target = 'txtEmail'
this.popover.message = 'thanks for entering details!!'
}
},
onClosePopover() {
this.popover.show = false
}
},
mounted() {
this.callPopover()
}
})
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.css" />
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-row class="mb-2 mt-4 ml-4">
<b-col cols="6">
<b-form-input id="txtName" v-model="name" type="text" placeholder="Enter your name"></b-form-input>
</b-col>
<b-col cols="6">
</b-col>
</b-row>
<b-row class="mb-2 ml-4">
<b-col cols="6">
<b-form-input id="txtEmail" v-model="email" type="email" placeholder="Enter your email"></b-form-input>
</b-col>
<b-col cols="6">
<b-btn #click="setPopover"> Show popover</b-btn>
</b-col>
</b-row>
<b-popover :target="popover.target" :show.sync="popover.show" triggers="" placement="topright">
<div class="popover-body">
<p>{{popover.message}}</p>
<div class="d-flex justify-content-center">
<b-btn #click="onClosePopover" size="sm"> gotcha!!</b-btn>
</div>
</div>
</b-popover>
</div>

Categories