VueJS file upload field cannot be reset - vue.js

In my vuejs application, I have a modal to insert some user data with a file upload.
I should be able to clear all the form fields once when an user successfully upload any content.
All my fields get reset apart from the file upload field.
It will remained as it is.
Inside my form.vue, for the file upload I have
<div class="mb-1">
<dashboard-input-label
identifier="document"
:settings="{ help: true, helpDirection: 'left' }"
>
Upload document
<template slot="helpText">
Maximum filesize is 5 MB. The default allowed file extensions
are: pdf, jpeg and png.
</template>
</dashboard-input-label>
<validator-v2
:identifier="identifier"
name="document_file"
:rules="{ required: { message: 'Document is required.' } }"
>
<custom-file-upload
ref="documentDocument"
v-model="documentFile"
:max-upload-file-size="5"
name="document_file"
></custom-file-upload>
</validator-v2>
</div>
and following is my button
<loading-button ref="submitBtn" size="normal">
Save & Add new
</loading-button>
This is my submitBtn method,
storeDocumentAndAddNew() {
this.isAddNew = true
const loader = this.$refs.submitBtn
this.storeDocument(loader)
},
And for the field reset I have followings,
reset() {
this.documentName= null
this.dateOfIssue= null
this.expiryDate= null
this.documentNumber = null
this.doesNotHaveDocumentNumber = false
this.doesNotExpire = false
this.documentFile = null
this.doesOptIn = false
this.isAddNew = false
this.documentFile = ''
this.$refs.documentDocument.value = null
},
Once I hit submit, every field get reset except this file upload field.
Following is my custom-file-upload.vue component
<template>
<div>
<div class="flex items-center flex-wrap">
<div #click="selectFile" class="flex items-center cursor-pointer w-full form-input custom-file-upload ">
<p class="flex justify-between items-center w-full overflow-hidden">
<span class="font-regular text-base flex items-center justify-center">
<span v-if="filename !== null">{{ filename | limitString }} </span>
<span class="cs--file-upload-current-file overflow-hidden inline-block" v-else><slot
name="currentFile"></slot></span>
</span>
<span class="text-certstyle-text-light ">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="feather feather-upload"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline
points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
</span>
</p>
<input #change="showFileName" ref="fileInput" type="file" :name="name">
<div class="flex flex-col fixed top-0 right-0 z-50">
<toastr-alert #hidden="resetToastrMessage" v-if="message !== ''" :message="message"
:type="type"></toastr-alert>
</div>
</div>
</div>
</div>
</template>
Even though I've used, this.$refs.documentDocument.value = null it's not clearing my file upload field....
Update
I have used #wendt88's answer and then the file is removed. But the previously uploaded, document's name will be remained there as it is..As an example, if I tried to upload a document called, dummy.pdf, it'll show the dummy.pdf text even after a successful submission...
Now my reset looks likes this,
reset() {
this.documentName= null
this.dateOfIssue= null
this.expiryDate= null
this.documentNumber = null
this.doesNotHaveDocumentNumber = false
this.doesNotExpire = false
this.documentFile = null
this.doesOptIn = false
this.isAddNew = false
this.$refs.documentDocument.$refs.fileInput.value = null
},
Following is my complete custom-file-upload.vue
<template>
<div>
<div class="flex items-center flex-wrap">
<div #click="selectFile" class="flex items-center cursor-pointer w-full form-input custom-file-upload ">
<p class="flex justify-between items-center w-full overflow-hidden">
<span class="font-regular text-base flex items-center justify-center">
<span v-if="filename !== null">{{ filename | limitString }} </span>
<span class="cs--file-upload-current-file overflow-hidden inline-block" v-else><slot
name="currentFile"></slot></span>
</span>
<span class="text-certstyle-text-light ">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="feather feather-upload"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline
points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
</span>
</p>
<input #change="showFileName" ref="fileInput" type="file" :name="name">
<div class="flex flex-col fixed top-0 right-0 z-50">
<toastr-alert #hidden="resetToastrMessage" v-if="message !== ''" :message="message"
:type="type"></toastr-alert>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.custom-file-upload input {
position: absolute;
cursor: pointer;
height: 0;
width: 0;
opacity: 0;
}
.cs--file-upload-current-file {
max-width: 220px;
}
label {
font-weight: 400 !important;
}
</style>
<script>
// Mixins
import HasToastrMessage from '#/Mixins/HasToastrMessage.js';
// Helper
import Helper from '#/Support/Helper.js';
export default {
mixins: [HasToastrMessage],
props: ['name', 'value', 'maxUploadFileSize'],
data() {
return {
file: null,
filename: null,
message: "",
type: "",
fileSize: 0,
maxFileSize: 10,
validFileTypes: [
'application/pdf',
'image/jpeg',
'image/png'
]
}
},
mounted() {
if (Helper.isset(this.maxUploadFileSize)) {
this.maxFileSize = this.maxUploadFileSize;
}
},
methods: {
showFileName(e) {
this.filename = collect(e.target.value.split('\\')).last();
let file = e.target.files[0];
let fileSize = file.size / 1000000;
if (fileSize > this.maxFileSize) {
this.showToastrErrorMessage(`The uploaded file is too big. Max size: ${this.maxFileSize} MB.`);
this.$refs.fileInput.value = null
this.filename = null
} else if (!this.validFileTypes.includes(file.type)) {
this.showToastrErrorMessage(`The uploaded file is invalid. Please upload valid type.`);
this.$refs.fileInput.value = null
this.filename = null
} else if (this.filename) {
this.file = file
this.showToastrSuccessMessage(`Your file is successfully uploaded.`);
this.$emit('uploaded')
} else {
this.showToastrErrorMessage(`Your file could not be uploaded. Please try again.`);
}
},
resetToastrMessage() {
this.message = "";
this.type = "";
},
selectFile() {
this.$refs.fileInput.click();
},
}
}
</script>

this.$refs.documentDocument is a vue component, to clear it´s value property use this.$set(this.$refs.documentDocument, 'value', null) -> https://v2.vuejs.org/v2/guide/reactivity.html
otherwise documentDocument does not know that you have changed it´s value
but if you want to clear the file input value, run (https://stackoverflow.com/a/16222877/1826106):
this.$refs.documentDocument.$refs.fileInput.value = null;
this.$set(this.$refs.documentDocument, 'filename', null);

Related

AxiosError: Request failed with status code 403

I tried using this api-key provided by RapidAPI for my code. I made sure that the API key entered is correct. I was previously using the .env file,'X-RapidAPI-Host': process.env.COIN_RANKING_HOST, for both the api host and api key but to make sure that the error is not caused by the .env file not loading, I entered both directly. This is my code:
import Header from '../components/Header'
import PortfolioChart from '../components/PortfolioChart'
import BuyTokens from '../components/BuyTokens'
import Notice from '../components/Notice'
import Asset from '../components/Asset'
import { BiDotsHorizontalRounded } from 'react-icons/bi'
import { AiOutlinePlus } from 'react-icons/ai'
import axios from 'axios'
const styles = {
wrapper: 'w-screen h-screen flex flex-col',
mainContainer: 'w-2/3 h-full m-auto flex mt-16',
leftMain: 'flex flex-col w-3/4 h-full p-6 overflow-y-scroll',
portfolioAmountContainer: 'flex flex-col ',
portfolioAmount: 'text-white text-4xl',
portfolioPercent: 'text-white font-bold text-sm',
pastHour: 'text-gray-400',
chartContainer:
'text-5xl flex justify-center w-full h-1/3 text-white mt-11 mb-11',
buyingPowerContainer:
'w-full border-t mb-24 border-b h-16 border-[#30363b] flex justify-between items-center p-4',
buyingPowerTitle: 'text-white font-bolder text-lg',
buyingPowerAmount: 'text-white font-bolder text-xl',
notice: 'flex border border-[#30363b] mx-11 my-4 p-5 flex-col flex-1',
noticeContainer: 'flex-1',
noticeTitle: 'text-gray-500',
noticeMessage: 'text-white font-bold',
noticeCTA: 'font-bold text-green-500 cursor-pointer mt-5',
rightMain:
'flex flex-col flex-1 h-4/5 bg-[#1E2123] mt-6 rounded-lg overflow-y-scroll noScroll',
rightMainItem: 'flex items-center text-white p-5 border-b border-[#30363b]',
ItemTitle: 'flex-1 font-bold',
moreOptions: 'cursor-pointer text-xl',
}
export default function Home({coins}) {
console.log(coins)
return(
<div className={styles.wrapper}>
<Header />
<div className={styles.mainContainer}>
<div className={styles.leftMain}>
<div className={styles.portfolioAmountContainer}>
<div className={styles.portfolioAmount}>
23 SOLANA
</div>
<div className={styles.portfolioPercent}>
+0.0008(+0.57%)
<span className={styles.pastHour}>
Past Hour
</span>
</div>
</div>
<div>
<div className={styles.chartContainer}>
<PortfolioChart />
</div>
</div>
<div className={styles.buyingPowerContainer}>
<div className={styles.buyingPowerTitle}>Buying Power</div>
<div className={styles.buyingPowerAmount}>12 SOLANA</div>
</div>
<div className={styles.notice}>
<div className={styles.noticeContainer}>
<div className={styles.noticeTitle}>
Send Funds
</div>
<div className={styles.noticeMessage}>
Transfer your funds here.
</div>
<BuyTokens />
</div>
</div>
<Notice />
</div>
<div className={styles.rightMain}>
<div className={styles.rightMainItem}>
<div className={styles.ItemTitle}>
Crypto Currencies
</div>
<BiDotsHorizontalRounded className={styles.moreOptions} />
</div>
<Asset coin={"BTC"} price={0.89} />
<Asset coin={"SOL"} price={0.90} />
<Asset coin={"ETH"} price={-1} />
<Asset coin={"USDC"} price={2} />
<div className={styles.rightMain}>
<div className={styles.ItemTitle}>
Lists
</div>
<AiOutlinePlus className={styles.moreOptions} />
</div>
</div>
</div>
</div>
)
}
export const getStaticProps = async () => {
const options = {
method: 'GET',
url: 'https://coinranking1.p.rapidapi.com/coins',
params: {
referenceCurrencyUuid: 'yhjMzLPhuIDl',
timePeriod: '24h',
tiers: '1',
orderBy: 'marketCap',
orderDirection: 'desc',
limit: '50',
offset: '0',
},
headers: {
'X-RapidAPI-Host': 'coinranking1.p.rapidapi.com',
'X-RapidAPI-Key': '23734db4e2msha5580c4a7b981c0p1557d0jsn9c9dcf2b8505',
},
}
const res = await axios.request(options)
const coins = res.data.data.coins
return {
props: { coins },
}
}
The error showing in the terminal is this.The error was very lengthy and I couldn't post the entire error due to character limit. I have put up the initial two lines and the last two lines of the error though.
error - [AxiosError: Request failed with status code 403] {
code: 'ERR_BAD_REQUEST',
...
data: { message: 'You are not subscribed to this API.' }
},
page: '/'
}
I am unable to figure out why I am getting this error. Please help.
I am subscribed to RapidAPI.

Data is not coming if i use async in vue js 3

I am implementing Quiz App but here I am facing an issue if I put static data in array the questions are coming.
Data is not coming if i use async in Vue JS 3
But If I Call data from the API the questions are not showing.
when I console the questions are showing in console and not showing in the front end.
For ref please find the attached code and image.
Home.vue (please see fetchQuestionsFromServer function)
<template>
<main class="flex h-screen items-center justify-center bg-gray-100">
<!-- quiz container -->
<QuizComplatePage v-if="endofQuiz" />
<div
class="overflow-hidden bg-white flex-none container relative shadow-lg rounded-lg px-12 py-6"
>
<img
src="#/assets/images/abstract.svg"
alt=""
class="absolute -top-10 left-0 object-none"
/>
<!-- contents -->
<div class="relative z-20">
<!-- score container -->
<div class="text-right text-gray-800">
<p class="text-sm leading-3">Score</p>
<p class="font-bold">{{score}}</p>
</div>
<!-- timer container -->
<div class="bg-white shadow-lg p-1 rounded-full w-full h-5 mt-4">
<div class="bg-blue-700 rounded-full w-11/12 h-full"
:style= "`width: ${timer}%`"
></div>
</div>
<!-- question container -->
<div
class="rounded-lg bg-gray-100 p-2 neumorph-1 text-center font-bold text-gray-800 mt-8"
>
<div class="bg-white p-5">
{{currentQuestion.question}}
</div>
</div>
<!-- options container -->
<div class="mt-8">
<!-- option container -->
<div v-for="(choice,item) in currentQuestion.choices" :key="item">
<div
class="neumorph-1 option-default bg-gray-100 p-2 rounded-lg mb-3 relative"
:ref="optionchosen"
#click="onOptionClick(choice,item)"
>
<div
class="bg-blue-700 p-1 transform rotate-45 rounded-md h-10 w-10 text-white font-bold absolute right-0 top-0 shadow-md"
>
<p class="transform -rotate-45">+10</p>
</div>
<div class="rounded-lg font-bold flex p-2">
<!-- option ID -->
<div class="p-3 rounded-lg">{{item}}</div>
<!-- option name -->
<div class="flex items-center pl-6">{{choice}}</div>
</div>
</div>
</div>
<!-- option container -->
</div>
<!-- progress indicator container -->
<div class="mt-8 text-center">
<div class="h-1 w-12 bg-gray-800 rounded-full mx-auto"></div>
<p class="font-bold text-gray-800">{{questionCounter}}/{{questions.length}}</p>
</div>
</div>
</div>
</main>
</template>
<script>
import { onMounted, ref } from 'vue'
import QuizComplatePage from './QuizCompleteOverlay.vue'
export default{
components: {
QuizComplatePage
},
setup(){
//data
let canClick = true
let score = ref(0)
let timer = ref(100)
let endofQuiz = ref(false)
let questionCounter = ref(0)
const currentQuestion = ref({
question: '',
answer: 1,
choices: [],
});
const questions = []
const loadQuestion = () =>{
canClick = true
timer.value=100
//Check question array had questions or not
if(questions.length > questionCounter.value){
currentQuestion.value = questions[questionCounter.value]
console.log('Current Question is : ', currentQuestion.value);
questionCounter.value++
}else{
endofQuiz.value = true
console.log('Out of Questions');
}
}
//methods
let itemsRef = []
const optionchosen = (element) =>{
if(element){
itemsRef.push(element)
}
}
const clearSelected = (divselected) =>{
setTimeout(()=>{
divselected.classList.remove('option-correct')
divselected.classList.remove('option-wrong')
divselected.classList.add('option-default')
loadQuestion()
},1000)
}
const onOptionClick = (choice,item) =>{
if(canClick)
{
const divContainer = itemsRef[item]
const optionId = item+1
if(currentQuestion.value.answer ===optionId){
console.log('You are Correct');
score.value += 10
divContainer.classList.add('option-correct')
divContainer.classList.remove('option-default')
}else{
console.log('You are Wrong');
divContainer.classList.add('option-wrong')
divContainer.classList.remove('option-default')
}
timer.value=100
canClick=false
//to go next question
clearSelected(divContainer)
console.log(choice,item);
}else{
console.log('Cant Select Option');
}
}
const countDownTimer = () =>{
let interval= setInterval(()=>{
if(timer.value>0){
timer.value--
}else{
console.log('Time is over');
clearInterval(interval)
}
},150)
}
const fetchQuestionsFromServer = async function(){
fetch('https://opentdb.com/api.php?amount=10&category=18&type=multiple')
.then((res) =>{
return res.json()
})
.then((data) =>{
const newQuestions = data.results.map((serverQuestion) =>{
const arrangedQuestion = {
question: serverQuestion.question,
choices: '',
answer: ''
}
const choices = serverQuestion.incorrect_answers
arrangedQuestion.answer = Math.floor(Math.random() * 4 + 1)
choices.splice(arrangedQuestion.answer-1 , 0 , serverQuestion.correct_answer)
arrangedQuestion.choices = choices
return arrangedQuestion
})
console.log('new formated questions' , newQuestions);
questions.value = newQuestions
loadQuestion()
countDownTimer()
console.log('questions: =>' , questions.value);
})
}
//lifecycle hooks
onMounted(() =>{
fetchQuestionsFromServer()
})
//return
return {
timer,
currentQuestion,
questions,
score,
questionCounter,
loadQuestion,
onOptionClick,
optionchosen,
endofQuiz,
}
}
}
</script>
<style scoped>
.neumorph-1 {
box-shadow: 6px 6px 18px rgba(0, 0, 0, 0.09), -6px -6px 18px #ffffff;
}
.container {
max-width: 400px;
border-radius: 25px;
}
</style>
QuizComplatePage.vue
<template>
<div class="w-screen h-screen absolute z-30 bg-white bg-opacity-30 flex justify-center items-center">
<div class="bg-green-700 p-4 text-center text-white">
<p class="font-bold text-2xl">All DOne!</p>
<p class="my-4 font-bold text-3xl">100% Score</p>
<!-- Buttons -->
<div class="flex justify-center">
<div class="rounded-full py-1 w-28 border cursor-pointer hover:text-black hover:bg-white">Done</div>
<div class="rounded-full py-1 w-28 border cursor-pointer hover:text-black hover:bg-white">Retry</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'QuizComplatePage'
}
</script>
<style>
</style>
Image.
You are not using value when you need to:
For instance look in this function
questions.length should be questions.value.length
Also currentQuestion.value = questions.value[questionCounter.value]
Start by fixing that.
Everything that is a ref in your setup needs to be accessed by .value in anything inside your setup. Outside your setup it will have this and can be treated normally. Mixing these two up is the most common error.
const loadQuestion = () =>{
canClick = true
timer.value=100
//Check question array had questions or not
if(questions.value.length > questionCounter.value){
currentQuestion.value = questions.value[questionCounter.value]
console.log('Current Question is : ', currentQuestion.value);
questionCounter.value++
}else{
endofQuiz.value = true
console.log('Out of Questions');
}
}
Here is my solution
let questions = []
questions = newQuestions
Now I am able to load questions.

With Vue.Js How can I call data from Backend into Modal in FrontEnd

In the image below, I call the current version with the Component from the Result field, but I cannot print the cloned project to the modal on the right. If anyone has an idea on how to fix this I would be very happy.
// COMPARE ACTION
GetCompareListByProject() {
// var _projectNo = this.$route.params.name;
var _this = this;
axios
.post(
GetURL() +
"Electric/GetVersionListByProject/" +
"-" +
"/" +
this.CloneSourceProjectNo
)
.then((resp) => {
if (resp.data.Response == true) {
var revisionList = [];
var data = resp.data.Data;
for (var i = 0; i < data.length; i++) {
if (revisionList.indexOf(data[i].RevisionNo) < 0)
revisionList.push(data[i].RevisionNo);
}
_this.CloneModalList = {
Revisions: revisionList,
Data: data,
};
_this.CloneNewNumber = {
Revision: resp.data.Revision,
Version: resp.data.Version,
};
}
});
},
CompareVersion() {
axios
.post(
GetURL() +
"Electric/OpenElectricProjectById/" +
this.OpenModalSelected.Id
)
.then((resp) => {
console.log(resp.data);
this.$store.state.project.ProjectTab.filter(
(p) => p.title == "Electric"
)[0].form = resp.data.Data.InputMain[0];
this.$store.state.project.Models.Project.Transformer.Electric =
resp.data.Data.Electric;
this.$store.state.general.ProjectMessage =
"Opened Version R" +
resp.data.Data.InputMain[0].RevisionNo +
"V" +
resp.data.Data.InputMain[0].VersionNo;
this.$swal({
position: "top-end",
icon: "success",
title: "Project Opened",
showConfirmButton: false,
timer: 1500,
});
this.$store.state.general.isLoading = false;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<b-modal
size="xl modal-full"
id="modal-compare"
title="Compare Project"
ok-only
ok-variant="secondary"
ok-title="Cancel"
>
<b-card class="w-50 h-10" style="float: left">
<b-tabs class="tabList" v-model="tabIndex">
<b-tab
#click="SetTabType('Result')"
v-for="(elec, index) in $store.state.project.Models.Project
.Transformer.Electric"
:key="index"
:active="index == 0"
>
<!-- <template #title>
<span class="line-tab">|</span>
{{ GetResultPageTitle(elec, index) }}
</template> -->
<ResultParameters
:Electric="elec"
:InputMain="
$store.state.project.Models.Project.Transformer.InputMain[0]
"
style="overflow-x: auto"
>
</ResultParameters>
</b-tab>
</b-tabs>
</b-card>
<b-card class="w-50 h-10" style="float: right">
<b-form-group
class="mb-0"
label-cols-sm="12"
label-cols-xl="4"
label="Clone Project"
label-class="text-left"
>
<b-input-group>
<b-input v-model="CloneSourceProjectNo"></b-input>
<b-input-group-append>
<b-input-group-text
style="cursor: pointer"
#click="GetCompareListByProject"
><i class="fa fa-sync-alt"></i
></b-input-group-text>
</b-input-group-append>
<b-button
class="w-100 waterGreen"
style="margin-top: 5px"
#ok="CompareVersion"
:ok-disabled="CompareModalSelected == null"
>
<i class="fa fa-bars"></i> Compare
</b-button>
</b-input-group>
</b-form-group>
<div class="mt-2"></div>
<div
v-for="rev in CloneModalList.Revisions"
:key="rev"
class="tree-view"
>
<h5>R{{ rev }}</h5>
<div
class="tree-item"
v-for="vers in CloneModalList.Data.filter(
(x) => x.RevisionNo == rev
)"
:key="vers.Id"
>
<span :class="GetInput().Id == vers.Id ? 'font-weight-bold' : ''">
<i
class="fas fa-check-circle releasedVersIcon"
v-if="vers.ActiveVersion"
></i>
V{{ vers.VersionNo }} : {{ vers.Description }}
<b-radio
:value="vers"
v-model="CompareModalSelected"
style="width: 16px; float: right; margin-top: -1px"
></b-radio>
</span>
</div>
</div>
</b-card>
</b-modal>

Vue $refs undefined when accessing property of element

I have an element part of a modal, like this:
<template>
<portal to="portal-level-3">
<div
v-if="isOpen"
class="fixed z-50 px-4 pb-4 inset-0 flex items-center justify-center"
:class="[isContentHeightGreaterThanHolder ? 'items-baseline' : 'items-center']"
>
<div
class="fixed inset-0 transition-opacity"
enter-active-class="ease-out duration-300"
enter-class="opacity-0"
enter-to-class="scale-100"
leave-active-class="ease-in duration-200"
leave-class="opacity-100"
leave-to-class="opacity-0"
>
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<div
v-if="isOpen"
v-click-outside="closeModal"
ref="refContent"
:class="[
notFullWidth ? '' : 'w-full',
modalWidthClass
]"
class="bg-white rounded-lg overflow-hidden shadow-xl transform transition-all px-4 pt-5 pb-4"
enter-active-class="ease-out duration-300"
enter-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enter-to-class="opacity-100 translate-y-0 sm:scale-100"
leave-active-class="ease-in duration-200"
leave-class="opacity-100 translate-y-0 sm:scale-100"
leave-to-class="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<div class="flex mb-4">
<div v-if="title" class="flex-1">
<h3 class="text-lg leading-6 font-medium text-brand">
{{ title }}
</h3>
</div>
<div class="pl-3">
<button
#click.prevent="closeModal()"
type="button"
class="text-gray-400 hover:text-gray-500 focus:outline-none focus:text-gray-500 transition ease-in-out duration-150"
>
<font-awesome-icon class="h-6 w-6" :icon="['fal', 'times']" />
</button>
</div>
</div>
<slot></slot>
</div>
</div>
</portal>
</template>
In the Vue instance, I watch for the modal opening like this:
watch: {
visible(val) {
this.isOpen = val
},
isOpen(val) {
if (val) {
this.$nextTick(() => {
let holderHeight = this.$el.offsetHeight
let contentHeight = this.$refs.refContent.offsetHeight
this.isContentHeightGreaterThanHolder = contentHeight >= (holderHeight - 30)
})
}
}
},
But this results in:
"TypeError: Cannot read property 'offsetHeight' of undefined"
But if I console.log(this.$refs) I get:
{}
refContent: div.bg-white.rounded-lg.overflow-hidden.shadow-xl.transform.transition-all.px-4.pt-5.pb-4.w-full.max-w-xl
##clickoutsideContext: {id: 5, methodName: "closeModal", documentHandler: ƒ, bindingFn: ƒ}
accessKey: ""
align: ""
ariaAtomic: null
ariaAutoComplete: null
ariaBusy: null
ariaChecked: null
ariaColCount: null
ariaColIndex: null
ariaColSpan: null
ariaCurrent: null
ariaDescription: null
ariaDisabled: null
ariaExpanded: null
....
offsetHeight: 805
Why can I not access this.$refs.refContent.offsetHeight? Thanks!

Unable to get line between two consecutive steps that get completed in el-step

I am using vuejs as my frontend framework and for UI I am using element-ui. Recently, I used el-step component and I am facing error in it. Even if two consecutive steps gets completed I am unable to see line between steps.
I followed the solution given here but it didn't worked for me.
Following is the JSFiddle of my code:-
https://jsfiddle.net/ywdg7cu6/
In console, I am getting following error:-
Error in callback for watcher "$parent.active": "TypeError: t.calcProgress is not a function
prevChild.calcProgress is not a function
Your el-popover should not be in your el-steps component.
var Main = {
data() {
return {
ifLocality: false,
ifLocation: false,
ifArea: false,
ifOtherMetrics: false,
active: 0
};
},
mounted() {
this.ifLocality = true
},
methods: {
getLocation() {
this.ifLocality = false
this.ifLocation = true
if (this.active++ > 3) this.active = 0;
},
getOtherMetrics() {
this.ifLocation = false
this.ifOtherMetrics = true
if (this.active++ > 3) this.active = 0;
},
getArea() {
this.ifOtherMetrics = false
this.ifArea = true
if (this.active++ > 3) this.active = 0;
},
getReport() {
this.ifArea = false
if (this.active++ > 3) this.active = 0;
},
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
#import url("//unpkg.com/element-ui#2.9.1/lib/theme-chalk/index.css");
<script src="//unpkg.com/vue/dist/vue.js"></script>
<script src="//unpkg.com/element-ui#2.9.1/lib/index.js"></script>
<div id="app">
<template>
<el-container>
<el-header>
<el-row>
<el-steps :active="active" align-center>
<el-step v-popover:step-1 title="Step 1" icon="el-icon-search" description="Step 1"></el-step>
<el-step v-popover:step-2 title="Step 2" icon="el-icon-location" description="Step 2"></el-step>
<el-step v-popover:step-3 title="Step 3" icon="el-icon-s-tools" description="Provide Other Credentials"></el-step>
<el-step v-popover:step-4 title="Step 4" icon="el-icon-edit" description="step 4"></el-step>
</el-steps>
<!-- Step - 1 - Search Locality -->
<el-popover ref="step-1" placement="bottom" width="300" trigger="manual" v-model="ifLocality">
<h4 class="heading">Step1</h4>
<el-row type="flex" justify="center">
<el-button class="button" type="primary" round #click="getLocation">Next<i class="el-icon-arrow-right"></i></el-button>
</el-row>
</el-popover>
<!-- Step - 2 - Pin Down Your Home -->
<el-popover ref="step-2" placement="bottom" width="300" trigger="manual" v-model="ifLocation">
<h4 class="heading">Step 2</h4>
<el-row class="popover" type="flex" justify="center">
<el-button type="primary" round #click="getOtherMetrics">Next <i class="el-icon-arrow-right"></i></el-button>
</el-row>
</el-popover>
<!-- Step - 3 - Provide Other Credentials -->
<el-popover ref="step-3" placement="bottom" width="300" trigger="manual" v-model="ifOtherMetrics">
<div class="other-metrics">
<h4 class="heading">Step 3</h4>
</div>
<el-row class="popover" type="flex" justify="center">
<el-button type="primary" round #click="getArea">Next <i class="el-icon-arrow-right"></i></el-button>
</el-row>
</el-popover>
<!-- Step - 4 - Trace Your Roof -->
<el-popover ref="step-4" placement="bottom" width="300" trigger="manual" v-model="ifArea">
<h4 class="heading">Step 4</h4>
<el-row class="popover" type="flex" justify="center">
<el-button type="primary" round #click="getReport">Next <i class="el-icon-arrow-right"></i></el-button>
</el-row>
</el-popover>
</el-row>
</el-header>
</el-container>
</template>
</div>