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>
Related
I'm working on a handlebar's page that show some books. Books can be filtered by title or by author or by city/library.
The page is rendered well the first time, but if I use filters and recall the handler with new parameters, the query is executed well (I can see new books' list) but handlebar doesn't update the page with new data
here is a part of my code:
#Get()
#Render("ciclo/index")
async getCicloBooks(
#Query("ciclo") ciclo,
#Query("codProv") codProvincia,
#Query("comune") idComune,
#Query("title") title,
#Query("author") author
) {
let books = undefined;
let comuni = undefined;
let librariesId = [];
const libraries = [];
let data = {};
this.logger.debug("[getCicloBooks] ciclo: " + ciclo);
const province = await this.cicloService.getProvince();
// Titolo
if (title != undefined) {
this.logger.log(`[getCicloBooks] per ciclo: ${ciclo} e titolo ${title}`);
const query = { ciclo: ciclo, title: title };
books = await this.bookService.getBooksByAdvancedQuery(query);
// TODO considerare che lo stesso libro può essere in più librerie
data = {
pageTitle: `Trovate n librerie che hanno in catalogo ${title}`,
ciclo: `${ciclo}`,
books: books,
libraries: null,
provincie: province,
comuni: comuni,
comune: null,
author: `${author}`,
title: `${title}`,
};
} // Autore
else if (author != undefined) {
this.logger.log(`[getCicloBooks] per ciclo: ${ciclo} e autore ${author}`);
const query = { ciclo: ciclo, author: author };
books = await this.bookService.getBooksByCicloAndAuthor(query);
// TODO considerare che lo stesso libro può essere in più librerie
data = {
pageTitle: `Trovate n librerie con libri di ${author}`,
ciclo: `${ciclo}`,
books: books,
libraries: null,
provincie: province,
comuni: comuni,
comune: null,
author: `${author}`,
title: `${title}`,
};
} // Provincia e comune
else if (idComune != undefined) {
this.logger.log(`[getCicloBooks] ciclo: ${ciclo} id comune ${idComune}`);
const comune = await this.cicloService.getComune(idComune);
const allLibraries = await this.cicloService.getAllLibrariesInCities();
this.logger.log("[getCicloBooks] comune " + JSON.stringify(comune));
librariesId = [];
allLibraries.forEach((library) => {
if (library.comune.id == idComune) {
libraries.push(library);
librariesId.push(library.id);
}
});
this.logger.log("[getCicloBooks] ids " + JSON.stringify(librariesId));
const query = { ciclo: ciclo, id: In(librariesId) };
// join book e libreria oppure find in table book_library_libreria where libreriaID in [libreriaId])
books = await this.bookService.getBooksByAdvancedQuery(query);
// this.logger.log("[getCicloBooks] books " + JSON.stringify(books));
books.forEach((book) => {
const libs = [];
book.library.forEach((l) => {
librariesId.forEach((id) => {
if (l.id == id) {
libs.push(l);
}
});
});
book.library = libs;
});
// this.logger.log("[getCicloBooks] books " + JSON.stringify(books));
data = {
pageTitle: `Tutti i libri disponibili per il ciclo: ${ciclo}`,
ciclo: `${ciclo}`,
books: books,
libraries: allLibraries,
provincie: province,
comuni: comuni,
comune: comune,
author: `${author}`,
title: `${title}`,
};
} else {
books = await this.bookService.getBooksByCiclo(ciclo);
data = {
pageTitle: `Tutti i libri disponibili per il ciclo: ${ciclo}`,
ciclo: `${ciclo}`,
books: books,
libraries: null,
provincie: province,
comuni: comuni,
comune: null,
author: `${author}`,
title: `${title}`,
};
}
this.logger.debug("[getCicloBooks] books: " + JSON.stringify(books));
if (books === undefined) {
data = {
title: "Book Store",
subtitle: `Attualmente non abbiamo nessun libro in catalogo per il ciclo ${ciclo}`,
books: null,
province: province,
comuni: comuni,
};
}
// this.logger.log(books);
return { viewData: data };
}
while this is the template code that doesn't update when new data are given
{{#> app}}
{{#*inline "content"}}
<div class="container">
<div class="row" style="margin-top: 25px;">
<div class="col">
<input id="ciclo" name="ciclo" value="{{viewData.ciclo}}" type="hidden">
<h3 class="fw-bold mt-5 text-center">{{ viewData.pageTitle }}</h3>
<h4 class="fw-bold text-center">Raffina la tua ricerca:</h4>
</div>
</div>
<div class="row mt-auto">
<!-- Navigatore delle Tabs -->
<ul class="nav nav-tabs mb-3" id="ex1" role="tablist">
<li class="nav-item" role="presentation">
<a id="tab1" href="#tabPlace" class="nav-link active" data-mdb-toggle="tab" role="tab" aria-controls="tabPlace"
aria-selected="true">Cerca per provincia e comune</a>
</li>
<li class="nav-item" role="presentation">
<a id="tab2" href="#tabAuthor" class="nav-link" data-mdb-toggle="tab" role="tab" aria-controls="tabAuthor"
aria-selected="false">Cerca per autore</a>
</li>
<li class="nav-item" role="presentation">
<a id="tab3" href="#tabTitle" class="nav-link" data-mdb-toggle="tab" role="tab" aria-controls="tabTitle"
aria-selected="false">Cerca per titolo</a>
</li>
</ul>
<!-- Tabs navs -->
</div>
<!-- Tabs content -->
<div id="search-content-tabs" class="tab-content">
<!-- Cerca per comune -->
<div id="tabPlace" class="tab-pane fade show active" role="tabpanel" aria-labelledby="tab-1">
<div class="container">
<div class="row mt-2" style="border: 1px;">
{{!-- blocco provincia --}}
<div class="col-lg-1 col-xs-1" style="margin-top: 8px;"> Provincia:</div>
{{#with viewData}}
<div class="col-lg-3 col-xs-3">
<select name="provincia" id="provinciaDropdown" class="form-select" aria-label="example">
<option value="">Seleziona la Provincia</option>
{{#each provincie}}
<option value="{{codice}}">{{provincia}}</option>
{{/each}}
</select>
</div>
{{/with}}
{{!-- blocco comune --}}
<div class="col-lg-1" style="margin-top: 8px;"> Comune:</div>
<div class="col-lg-3">
<select id="comuneDropdown" name="comune" class="form-select" aria-label="example">
<option value="">Seleziona il Comune</option>
</select>
</div>
</div> <!-- riga dei select provincia e comune -->
<div class="row mt-4"> <!-- riga bottone cerca -->
<div class="col-2">
**<button id="cityBtn" type="submit" class="btn btn-primary" onclick="onCityClicked(event)">Cerca</button>**
</div>
</div>
</div>
</div>
<!-- Cerca per comune -->
<!-- Cerca per autore -->
<div id="tabAuthor" class="tab-pane fade" role="tabpanel" aria-labelledby="tab-2">
<div class="container">
<div class="row mt-2" style="border: 1px;">
{{!-- blocco autore --}}
{{!-- <div class="col-lg-1" style="margin-top: 8px;"> Autore:</div> --}}
<div class="col-lg-3 form-outline">
<input id="formAuthor" type="text" class="form-control form-control-lg" />
<label class="form-label" for="formAuthor">Cerca per autore del libro</label>
</div>
</div>
<div class="row mt-4"> <!-- riga bottone cerca -->
<div class="col-2">
<button id="authorBtn" type="submit" class="btn btn-primary" onclick="onAuthorClicked(event)">Cerca</button>
</div>
</div>
</div>
</div>
<!-- Cerca per autore -->
<!-- Cerca per Titolo -->
<div id="tabTitle" class="tab-pane fade" role="tabpanel" aria-labelledby="tab-3">
<div class="container">
<div class="row mt-2" style="border: 1px;">
<div class="col-lg-1 form-outline">
<input id="formTitle" type="text" class="form-control form-control-lg" />
<label class="form-label" for="formTitle">Cerca per titolo del libro</label>
</div>
</div>
<div class="row mt-4"> <!-- riga bottone cerca -->
<div class="col-2">
<button id="titleBtn" type="submit" class="btn btn-primary" onclick="onTitleClicked(event)">Cerca</button>
</div>
</div>
</div>
</div>
<!-- Cerca per Titolo -->
</div>
<!-- Tabs content -->
</div>
<!-- Blocco Risultato ricerca -->
{{ log viewData}}
<ul id="booksPreview">
{{#each viewData.books}}
<li style="list-style-type: none">
<div class="container mt-5">
<div class="row bt-2" style="margin-top: 20px;">
<div class="col-lg-3 col-md-2 col-lg-2 mb-2">
<img src="/covers/{{getCoverImage}}" style="height: 320px; width: 230px;">
</div>
<div class="col-lg-7 col-md-8 col-lg-6 mb-2">
<div class="text-md-start text-black text-capitalize fa-2x">{{title}}</div>
<div class="text-md-start">Autore {{getAuthor}}</div>
<div class="text-md-start">Prezzo {{getPrice}} €</div>
<div class="text-md-start">Abstract {{getDescription}}</div>
</div>
<div class="col-lg-2 col-md-2 mb-2">
<div class="bi-text-left ">
Aggiungi al carrello
</div>
</div>
</div>
</div>
</li>
{{/each}}
</ul>
<!-- Blocco Risultato ricerca -->
{{/inline}}
{{/app}}
And this is part of scripts's functions called on serch button click
$(document).ready(function () {
$("#provinciaDropdown").on("change", function () {
console.log(`Selected provincia: ${this.value}`);
var country_id = this.value;
$("#comuneDropdown").html("");
uri = `http://${host}:${port}/admin/getComuni?prov=${country_id}`;
$.ajax({
url: uri,
type: "GET",
success: function (result) {
/* console.log(' Result listaComuni ' + result.viewData.comuni); */
if ($.isArray(result.viewData.comuni)) {
$("#comuneDropdown").html(
'<option value="">Seleziona il Comune</option>'
);
comuni = result.viewData.comuni;
for (i = 0; i < comuni.length; i++) {
str = `<option value="${comuni[i].id}"> ${comuni[i].comune} </option>`;
// console.log(str);
$("#comuneDropdown").append(str);
}
} // isArray
}, // success
});
}); // onChange
$("#comuneDropdown").on("change", function () {
var idComune = this.value;
console.log(`Selected comune with id ${this.value}`);
}); // onChange comuneDropdown
});
function onCityClicked(event) {
event.preventDefault();
var city = document.getElementById("comuneDropdown").value;
var ciclo = document.getElementById("ciclo").value;
console.log(`[onCityClicked] ciclo: ${ciclo} city: ${city} `);
var uri = `http://${host}:${port}/ciclo?ciclo=${ciclo}&comune=${city}`;
console.log(`[onCityClicked] uri: ${uri} `);
$.ajax({
url: uri,
type: "GET",
success: function (result) {
data = result.viewData;
console.log("----- SUCCESS -----");
}, // success
}); // ajax
}
So, from main page I show the book's page (default query is executed and template page is rendered successfully), then when I try to change filters, the book's page is called with choosen filters, the query is excuted well (I check logs) but the template doesn't update. I got the previous page
I added to original code the <ul id=".."> <li></li> </ul> tags, trying to intercept at runtime some action on id and clean the book's list before the new render, but I wasn't able to do
Can someone help me?
Thanks
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>
I really don't know what to do about it.
I'll try to explain:
I need to output value "senderName" in block of chat. For it, iteration over the arrays is used on mounted method. There are successfully output in console and Vue debagger extension, but the field for name is empty.If you enter text instead of the {{}} template, then everything is displayed.
This is html code
<div
v-for="chat in chats"
:key="chat.id"
#click="changeChat(chat.private_chat_id)"
class="card chat-chart"
style="
width: 100%;
margin-bottom: 1px;
box-shadow: none;
border: 1px solid #eee;
"
>
<div
class="card-body mt--3"
style="
height: 100px !important;
padding: 0.8rem i !important;
"
>
<div class="row align-items-center justify-content-start">
<div class="col-2">
<span class="avatar avatar-sm rounded-circle">
<img
src="https://ptetutorials.com/images/user-profile.png"
alt="avatar"
/>
</span>
</div>
<div class="col-8 d-flex align-items-center">
<h4 class="card-title align-self-center mb-0">
{{ chat.senderName }}
</h4>
</div>
</div>
<div
class="row align-items-center justify-content-end mt--4 mb-4"
style="height: 1px"
>
<div
v-if="chat.unreadCount > 0"
class="d-inline-block rounded-circle bg-success text-light ml-2 p-1"
style="
color: white !important;
font-weight: bold;
width: 30px;
height: 30px;
"
>
<!-- <p
class="text-center font-weight-bold"
style="line-height: 1.4"
>
{{ (chat.unreadCount > 99) ? 99 : chat.unreadCount
}}
</p> -->
</div>
</div>
<div
class="row align-items-center justify-content-start ml-1"
>
<p>chat.msg</p>
</div>
</div>
</div>
And here mounted method
mounted() {
// Get all chats for user with id - userId
axios
.get("/api/users/" + this.userId + "/privatechat")
.then((res) => {
appChats.chats = res.data;
// console.log(this.chats);
// Itereate array of user's private chats
for (let i = 0; i <= res.data.length; i++) {
if (!res.data[i]) {
continue;
} else {
// console.log(res.data[i]);
let chatObj = res.data[i];
// Get info of the chat with its id
axios
.get("/api/privatechat/" + chatObj.private_chat_id)
.then((response) => {
// console.log(response.data);
for (values of response.data) {
// If user's id of the chat equals id of our user then skip
if (values.user_id == appChats.userId) continue;
else {
// console.log(values.user_id);
appChats.chats[i]["senderId"] = values.user_id;
// Get userName of our interlocutor
axios
.get("/api/users/" + values.user_id)
.then((r) => {
appChats.chats[i]["senderName"] =
r.data[0].username;
});
}
}
});
}
}
});
},
There are some pics:
https://i.imgur.com/tSKBIeK.png
https://i.imgur.com/nxbhOvE.png // this pic is about Vue js extension
I'm using Axios to fetch data from a server, I'm trying to do a PUT request and I need to get data info from 3 tables in order to fill the form, when I do the PUT it sometimes works and sometimes doesn't, but when I open the browser terminal to debug the problem, the PUT request always works, also I notice that another component without nested GET requests always works fine, but I can't fetch the data from the server if those GET requests aren't nested.
Here is my script code, I don't know what I'm doing wrong with this.
<template>
<div class="container-fluid">
<div style="margin:40px;background-color:rgba(255, 255, 255, 0.7);">
<div class="row">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item">Home</li>
<li class="breadcrumb-item">Usuarios</li>
<li class="breadcrumb-item">Roles</li>
<li class="breadcrumb-item"><a v-bind:href="rol_url">{{rol_name}}</a></li>
<li class="breadcrumb-item active" aria-current="page">Editar Rol</li>
</ol>
</nav>
</div>
<div class="row">
<div class="col-md-8 offset-md-2" style="margin-bottom:80px;">
<div class="row">
<div class="col">
<button onclick="window.history.back();" class="btn btn-primary" style="background:#003e1e;border-color:#003e1e;">
<font-awesome-icon icon="arrow-left" size="lg"></font-awesome-icon>
</button>
</div>
</div>
<div> </div>
<div class="row justify-content-center">
<div class="col-5 align-self-center">
<form>
<div class="form-group">
<label for="rolName">Nombre del rol:</label>
<input v-model="rol_name" type="text" class="form-control" id="rolName" aria-describedby="rolName" placeholder="Nombre">
</div>
<div class="form-group">
<label for="rolModules">Modulos del rol:</label>
<multiselect v-model="rol_mod" :options="modules" :multiple="true" :close-on-select="true" :clear-on-select="false" :hide-selected="true" :preserve-search="true" placeholder="Seleccione los modulos" label="name" track-by="modulo" :preselect-first="false">
</multiselect>
</div>
<div v-for='(module, index) in rol_mod' :key='index' class="form-group">
<label for="rolModules">Permisos de {{module.name}}</label>
<multiselect v-model="module.permisos" :options="permits" :multiple="true" :close-on-select="true" :clear-on-select="false" :hide-selected="true" :preserve-search="true" placeholder="Seleccione los permisos del modulo" label="name" track-by="_id" :preselect-first="false">
</multiselect>
</div>
<div class="form-group">
<label for="rolStates">Estado del rol:</label>
<multiselect v-model="rol_state" :options="states" track-by="name" label="name" :searchable="false" :close-on-select="true" :show-labels="true" :placeholder="rol_state_get">
</multiselect>
</div>
<div class="form-group">
<label for="permitDescription">Descripción:</label>
<textarea v-model="rol_description" class="form-control" aria-label="permitDescription"
placeholder="Descripción" :rows="6" :max-rows="10"></textarea>
</div>
<div> </div>
<div class="row justify-content-center">
<div class="col-4 text-center">
<button class="btn btn-primary" v-on:click="submit()" style="background:#003e1e;border-color:#003e1e;">
<font-awesome-icon icon="save" size="lg"></font-awesome-icon>
Guardar
</button>
</div>
<div class="col-4 text-center">
<a class="btn btn-primary" style="background:#003e1e;border-color:#003e1e;" v-bind:href="rol_url">
<font-awesome-icon icon="times-circle" size="lg"></font-awesome-icon>
Cancelar
</a>
</div>
</div>
<div> </div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
const axios = require('axios');
var API_IP = process.env.VUE_APP_API_IP
export default {
components: {
Multiselect
},
data () {
return {
rol_auditoria: {},
modules: [],
permits: [],
rol_name: "",
rol_state: "",
rol_state_get: "",
rol_description: '',
states: [
{ name: "Activo", activo: "true" },
{ name: "Inactivo", activo: "false" }
],
rol_mod: [],
rol_url: ""
}
},
mounted () {
axios
.get(API_IP+'/rol/'+this.$route.params.id)
.then(response => {
this.rol_auditoria = response.data.data.auditoria;
this.rol_name = response.data.data.nombre;
this.rol_state = response.data.data.activo;
response.data.data.activo? this.rol_state_get="Activo" : this.rol_state_get="Inactivo";
this.rol_description = response.data.data.descripcion
this.rol_id = response.data.data._id
this.rol_url = "/roles/"+response.data.data._id
for (var k in response.data.data.modulos){
var mod_info = {}
console.log(response.data.data.modulos[k].modulo.nombre);
mod_info["_id"] = response.data.data.modulos[k]._id
mod_info["modulo"] = { "_id" : response.data.data.modulos[k].modulo._id }
mod_info["name"] = response.data.data.modulos[k].modulo.nombre
var mod_per = []
for (var j in response.data.data.modulos[k].permisos){
var perms = {}
perms["_id"] = response.data.data.modulos[k].permisos[j]._id
perms["name"] = response.data.data.modulos[k].permisos[j].nombre
mod_per.push(perms)
}
mod_info["permisos"] = mod_per
this.rol_mod.push(mod_info)
}
axios
.get(API_IP+"/module/")
.then(response => {
for(var k in response.data.data){
var mod = {}
mod["modulo"] = { "_id" : response.data.data[k]._id }
mod["name"] = response.data.data[k].nombre;
this.modules.push(mod);
}
axios
.get(API_IP+"/permit/")
.then(response => {
for(var k in response.data.data){
var per = {}
per["name"] = response.data.data[k].nombre;
per["_id"] = response.data.data[k]._id;
this.permits.push(per);
}
});
});
})
},
methods: {
submit: function() {
axios
.put(API_IP+"/rol/"+this.$route.params.id, {
auditoria: this.rol_auditoria,
activo: this.rol_state.activo,
_id: this.rol_id,
nombre: this.rol_name,
descripcion: this.rol_description,
modulos: this.rol_mod
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
this.$router.push({ name: 'showrol', params: { id: this.rol_id} });
}
}
}
</script>
This may be a long shot without having information from the console output and the actual info or errors being returned from the GET requests, but I noticed a lot of "this" use on your code.
You are double nesting axios calls, which are async. "this" tends to be hard to debug in javascript, even if youre using arrow functions which should be relatively safe.
Please try to add:
let self = this;
Before you start your GET requests, and use "self" instead of "this" inside your promises.
This may be obvious, but I dont see you calling this.submit() anywhere in your code. Where inside the GET callbacks are you calling SUBMIT for the PUT request?
I am trying to integrate a payment gateway on a webpage using the stripe API. I am referring to this code snippet HERE. I am having trouble with loading JQUERY and kind of been struggling for some time to get it working. I am getting this error
Uncaught TypeError: Cannot read property 'addMethod' of undefined
I think the problem is because of not loading the jquery properly but not sure where its going wrong.
code
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<style>
/* Padding - just for asthetics on Bootsnipp.com */
body { margin-top:20px; }
/* CSS for Credit Card Payment form */
.panel-title {display: inline;font-weight: bold;}
.checkbox.pull-right { margin: 0; }
.pl-ziro { padding-left: 0px; }
.form-control.error {
border-color: red;
outline: 0;
box-shadow: inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(255,0,0,0.6);
}
label.error {
font-weight: bold;
color: red;
padding: 2px 8px;
margin-top: 2px;
}
.payment-errors {
font-weight: bold;
color: red;
padding: 2px 8px;
margin-top: 2px;
}
</style>
</head>
<div class="container">
<div class="row">
<div class="col-xs-12 col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><img class="pull-right" src="http://i76.imgup.net/accepted_c22e0.png">Payment Details</h3>
</div>
<div class="panel-body">
<form role="form" id="payment-form">
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="cardNumber">CARD NUMBER</label>
<div class="input-group">
<input type="text" class="form-control" name="cardNumber" placeholder="Valid Card Number" required autofocus data-stripe="number" />
<span class="input-group-addon"><i class="fa fa-credit-card"></i></span>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-7 col-md-7">
<div class="form-group">
<label for="expMonth">EXPIRATION DATE</label>
<div class="col-xs-6 col-lg-6 pl-ziro">
<input type="text" class="form-control" name="expMonth" placeholder="MM" required data-stripe="exp_month" />
</div>
<div class="col-xs-6 col-lg-6 pl-ziro">
<input type="text" class="form-control" name="expYear" placeholder="YY" required data-stripe="exp_year" />
</div>
</div>
</div>
<div class="col-xs-5 col-md-5 pull-right">
<div class="form-group">
<label for="cvCode">CV CODE</label>
<input type="password" class="form-control" name="cvCode" placeholder="CV" required data-stripe="cvc" />
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label for="couponCode">COUPON CODE</label>
<input type="text" class="form-control" name="couponCode" />
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<button class="btn btn-success btn-lg btn-block" type="submit">Start Subscription</button>
</div>
</div>
<div class="row" style="display:none;">
<div class="col-xs-12">
<p class="payment-errors"></p>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<script>
var $form = $('#payment-form');
$form.on('submit', payWithStripe);
/* If you're using Stripe for payments */
function payWithStripe(e) {
e.preventDefault();
/* Visual feedback */
$form.find('[type=submit]').html('Validating <i class="fa fa-spinner fa-pulse"></i>');
var PublishableKey = 'mykey'; // Replace with your API publishable key
Stripe.setPublishableKey(PublishableKey);
Stripe.card.createToken($form, function stripeResponseHandler(status, response) {
console.log
if (response.error) {
/* Visual feedback */
$form.find('[type=submit]').html('Try again');
/* Show Stripe errors on the form */
$form.find('.payment-errors').text(response.error.message);
$form.find('.payment-errors').closest('.row').show();
} else {
/* Visual feedback */
$form.find('[type=submit]').html('Processing <i class="fa fa-spinner fa-pulse"></i>');
/* Hide Stripe errors on the form */
$form.find('.payment-errors').closest('.row').hide();
$form.find('.payment-errors').text("");
// response contains id and card, which contains additional card details
var token = response.id;
console.log(token);
// AJAX
$.post('/account/stripe_card_token', {
token: token
})
// Assign handlers immediately after making the request,
.done(function(data, textStatus, jqXHR) {
$form.find('[type=submit]').html('Payment successful <i class="fa fa-check"></i>').prop('disabled', true);
})
.fail(function(jqXHR, textStatus, errorThrown) {
$form.find('[type=submit]').html('There was a problem').removeClass('success').addClass('error');
/* Show Stripe errors on the form */
$form.find('.payment-errors').text('Try refreshing the page and trying again.');
$form.find('.payment-errors').closest('.row').show();
});
}
});
}
/* Form validation */
jQuery.validator.addMethod("month", function(value, element) {
return this.optional(element) || /^(01|02|03|04|05|06|07|08|09|10|11|12)$/.test(value);
}, "Please specify a valid 2-digit month.");
jQuery.validator.addMethod("year", function(value, element) {
return this.optional(element) || /^[0-9]{2}$/.test(value);
}, "Please specify a valid 2-digit year.");
validator = $form.validate({
rules: {
cardNumber: {
required: true,
creditcard: true,
digits: true
},
expMonth: {
required: true,
month: true
},
expYear: {
required: true,
year: true
},
cvCode: {
required: true,
digits: true
}
},
highlight: function(element) {
$(element).closest('.form-control').removeClass('success').addClass('error');
},
unhighlight: function(element) {
$(element).closest('.form-control').removeClass('error').addClass('success');
},
errorPlacement: function(error, element) {
$(element).closest('.form-group').append(error);
}
});
paymentFormReady = function() {
if ($form.find('[name=cardNumber]').hasClass("success") &&
$form.find('[name=expMonth]').hasClass("success") &&
$form.find('[name=expYear]').hasClass("success") &&
$form.find('[name=cvCode]').val().length > 1) {
return true;
} else {
return false;
}
}
$form.find('[type=submit]').prop('disabled', true);
var readyInterval = setInterval(function() {
if (paymentFormReady()) {
$form.find('[type=submit]').prop('disabled', false);
clearInterval(readyInterval);
}
}, 250);
</script>
Your problem is that:
You are trying to use this jquery validation plugin without including it to your page.
add this to your page
<script type="text/javascript" src="http://cdn.jsdelivr.net/jquery.validation/1.13.1/jquery.validate.min.js"></script>
DEMO