Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

muathye's avatar
Level 41

I created new vite project with vuejs and I got `undefined variable`

The project hosted on https://zid-assignment-frontend.netlify.app I get this error on console when I try to fill in credit card

TypeError: Cannot read properties of undefined (reading 'cardNumber')
    at h (index.6bec21b0.js:5:27071)
    at I.onKeyup.C.<computed>.C.<computed> (index.6bec21b0.js:5:27853)
    at tt (index.6bec21b0.js:1:13032)
    at Pe (index.6bec21b0.js:1:13110)
    at HTMLInputElement.n (index.6bec21b0.js:1:53350)

This is the code

<script setup ts>
import { ref } from 'vue'


const Months = [
  { month: '0' + 1 },
  { month: '0' + 2 },
  { month: '0' + 3 },
  { month: '0' + 4 },
  { month: '0' + 5 },
  { month: '0' + 6 },
  { month: '0' + 7 },
  { month: '0' + 8 },
  { month: '0' + 9 },
  { month: 10 },
  { month: 11 },
  { month: 12 },
];
const Years = [
  { year: 2022 },
  { year: 2023 },
  { year: 2024 },
  { year: 2025 },
  { year: 2026 },
  { year: 2027 },
  { year: 2028 },
  { year: 2029 }
];

const monthSelection = ref('')
const yearSelection = ref('')
const timeToExpire = 9;

const cardNumber = ref('');

const cardVadid = ref('');

const cardType = ref('');

const cClass = ref('');
const cvv = ref('');
const cardHolder = ref('')
const regx = [
  { name: 'Visa', logo: 'https://seeklogo.com/images/V/visa-logo-CF29426B98-seeklogo.com.png', re: '^4', },
  { name: 'Hipercard', logo: 'https://cdn.worldvectorlogo.com/logos/hipercard.svg', re: /^(606282\d{10}(\d{3})?)|(3841\d{15})$/ },
  { name: 'MasterCard', logo: 'https://logodownload.org/wp-content/uploads/2014/07/mastercard-logo-novo-3.png', re: /^(5[1-5]|677189)|^(222[1-9]|2[3-6]\d{2}|27[0-1]\d|2720)/ },
  { name: 'Discover', logo: 'https://i.pinimg.com/originals/b3/d7/85/b3d7853a11dcc8c424866915ddd4d3e3.png', re: /^(6011|65|64[4-9]|622)/ },
  { name: 'Elo', logo: 'https://seeklogo.com/images/E/elo-logo-0B17407ECC-seeklogo.com.png', re: /^(4011(78|79)|43(1274|8935)|45(1416|7393|763(1|2))|50(4175|6699|67[0-7][0-9]|9000)|627780|63(6297|6368)|650(03([^4])|04([0-9])|05(0|1)|4(0[5-9]|3[0-9]|8[5-9]|9[0-9])|5([0-2][0-9]|3[0-8])|9([2-6][0-9]|7[0-8])|541|700|720|901)|651652|655000|655021)/ },
  { name: 'American Express', logo: 'https://ccard-generator.com/assets/images/cardmedium/american-express.png', re: /^3[47]\d{13,14}$/ }
];
const btnClassName = ref('');


function inputFormat() {
  let text = this.cardNumber.split(" ").join("");

  this.cardVadid = text;
  if (text.length > 0) {

    text = text.match(new RegExp(/.{1,4}/, 'g')).join(" ")

    .replace(new RegExp(/[^\d]/, 'ig'), " ");
  }

  this.cardNumber = text;

  this.GetCardType(this.cardVadid);
}

function validCreditCard(value) {
  let inputValidate = document.getElementById('cardNumber');

  let numCheck = 0,
    bEven = false;
  value = value.toString().replace(new RegExp(/\D/g, ""));
  for (let n = value.length - 1; n >= 0; n--) {
    let cDigit = value.charAt(n),
      digit = parseInt(cDigit, 10);

    if (bEven && (digit *= 2) > 9) digit -= 9;
    numCheck += digit;
    bEven = !bEven;
  }
  let len = value.length;


  if (numCheck % 10 === 0 && len === 16 && this.cardType) {
    inputValidate.classList.remove('notValid');
    inputValidate.classList.add('valid');
  }

  else if (!numCheck % 10 === 0 && len === 16) {
    inputValidate.classList.remove('valid');
    inputValidate.classList.add('notValid');

  } else {
    inputValidate.classList.remove('valid');
    inputValidate.classList.remove('notValid');
  }

}

function GetCardType(number) {
  this.regx.forEach((item) => {
    if (number.match(item.re) != null) {
      this.cardType = item.logo;

      this.cClass = item.name.toLowerCase();
    } else if (!number) {
      this.cardType = '';
      this.cClass = '';
    }
  });

  this.validCreditCard(number);
}

function mouseDw() {
  this.btnClassName = 'btn__active';
}

function mouseUp() {
  this.btnClassName = '';
}

function blr() {
  let cr = document.getElementsByClassName('card--credit__card')[0];
  cr.classList.remove('cvv-active')
}
function fcs() {
  let cr = document.getElementsByClassName('card--credit__card')[0];
  cr.classList.add('cvv-active')
}
</script>
<template>
  <main id="credit-card-page">
    <div class="card">
      <div class="container">
        <div class="card-container">
          <div class="card--credit__card">
            <div class="front">
              <div class="Cheader">
                <div class="card__logo">
                  <img v-bind:class='cClass' :src="cardType" alt="">
                </div>
                <div class="card__chip">
                  <svg x="0px" y="0px" viewBox="0 0 100 125">
                    <g transform="translate(0,-952.36218)">
                      <path
                        d="M 18,18 C 11.869627,18 7,23.41809 7,30 l 0,40 c 0,6.5819 4.869627,12 11,12 21.333333,0 42.666667,0 64,0 6.130373,0 11,-5.4181 11,-12 L 93,30 C 93,23.41809 88.130373,18 82,18 60.666667,18 39.333333,18 18,18 z m 0,2 23.46875,0 C 37.589245,22.564948 35,27.361832 35,33 l 0,4.6875 C 34.069867,37.259411 33.060307,37 32,37 L 9,37 9,30 c 0,-5.60609 4.050373,-10 9,-10 z m 30,0 4,0 c 6.043676,0 11,5.47378 11,13 0,2.009661 0,4.023816 0,6.03125 -0.251164,0.237363 -0.495555,0.475252 -0.71875,0.75 C 60.845717,41.54836 60,44.04215 60,47 l 0,5 c 0,2.9578 0.845717,5.4204 2.28125,7.1875 C 62.508873,59.467697 62.743321,59.757384 63,60 l 0,7 c 0,7.5263 -4.956311,13 -11,13 l -4,0 C 41.956311,80 37,74.5263 37,67 l 0,-7 c 0.256679,-0.242616 0.491127,-0.532303 0.71875,-0.8125 C 39.154283,57.4204 40,54.9578 40,52 l 0,-5 C 40,44.04215 39.154283,41.54836 37.71875,39.78125 37.491127,39.501051 37.256679,39.241295 37,39 l 0,-6 C 37,25.47378 41.956324,20 48,20 z M 58.53125,20 82,20 c 4.949627,0 9,4.39391 9,10 l 0,7 -23,0 c -1.060307,0 -2.069867,0.259411 -3,0.6875 L 65,33 C 65,27.361832 62.410755,22.564948 58.53125,20 z M 9,39 32,39 c 1.641597,0 3.092532,0.68336 4.1875,2.03125 C 37.282468,42.37914 38,44.41785 38,47 l 0,5 c 0,2.5822 -0.717532,4.5896 -1.8125,5.9375 C 35.092532,59.2854 33.641597,60 32,60 L 9,60 z m 59,0 23,0 0,21 -23,0 C 66.358403,60 64.907468,59.2854 63.8125,57.9375 62.717532,56.5896 62,54.5822 62,52 l 0,-5 c 0,-2.58215 0.717532,-4.62086 1.8125,-5.96875 C 64.907468,39.68336 66.358403,39 68,39 z M 35,61.34375 35,67 c 0,5.625803 2.572319,10.430848 6.4375,13 L 18,80 C 13.050373,80 9,75.6061 9,70 l 0,-8 23,0 c 1.060307,0 2.069867,-0.220772 3,-0.65625 z m 30,0 C 65.930133,61.779228 66.939693,62 68,62 l 23,0 0,8 c 0,5.6061 -4.050373,10 -9,10 L 58.5625,80 C 62.427681,77.430848 65,72.625803 65,67 z"
                        transform="translate(0,952.36218)" />
                    </g>
                  </svg>
                </div>
              </div>
              <div class="Cbody">
                <p>{{cardNumber}}</p>
              </div>
              <div class="Cfooter">
                <div class="name">
                  <span>CARD HOLDER</span>
                  <p>{{cardHolder}}</p>
                </div>
                <div class="expiration__data">
                  <span>EXPIRES</span>
                  <p v-if='monthSelection'>{{monthSelection}} / {{yearSelection}}</p>

                </div>
              </div>
            </div>
            <div class="back">
              <div class="strip"></div>
              <div class="cvv-container">
                <p>cvv: {{cvv}}</p>
                <div class="cvv-strip"></div>
              </div>
            </div>
          </div>

        </div>

        <div class="card--form">
          <form>
            <label for="cardNumber">Card Number</label>
            <input v-on:keyup='inputFormat()' v-model='cardNumber' maxlength="19" type="text" id="cardNumber">
            <label for="cardName">Card Name</label>
            <input v-model='cardHolder' maxlength="25" type="text" id="cardName">
            <div class="date--expiration">
              <div>
                <select v-model='monthSelection' name="expirationDate__month">
                  <option value="">Month</option>
                  <option v-for='item in Months' :value="item.month">{{item.month}}</option>
                </select>
                <select v-model='yearSelection' name="expirationDate__year">
                  <option value="">Year</option>
                  <option v-for='item in Years' :value="item.year">{{item.year}}</option>
                </select>
              </div>
              <div class="cvv">
                <label for="cvvInput">CVV</label>
                <input v-model='cvv' maxlength="4" type="text" id="cvvInput" onfocus="fcs()" onblur="blr()">
              </div>
            </div>
          </form>
          <button @mousedown='mouseDw' @mouseup='mouseUp' :class='btnClassName' class="btn">Submit</button>
        </div>
      </div>
    </div>
  </main>
</template>
<style lang="scss" scoped>
.card {
  width: 100%;
  height: 90vh;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  display: flex;
  justify-content: center;
  align-items: center;
  width: fit-content;

  .container {
    position: absolute;
    height: 100%;
    width: 430px;
  }


  .card-container {
    top: 0;
    perspective: 1000px;
    position: relative;
    width: 350px;
    height: 200px;
    z-index: 2;
    left: 40px;
    top: 110px;
  }

  .card--credit__card {
    width: 350px;
    height: 200px;
    border-radius: 10px;
    background: url("https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcRtX-QkYwDKEXw_jriWgI5vplXUadOusPHRitOZKZCfBqJqK7IU");
    background-size: cover;
    position: relative;
    z-index: 2;
    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    -moz-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    position: absolute;
    transition: 1s ease;
    transform-style: preserve-3d;
  }

  .card--form {
    width: 430px;
    height: 460px;
    background: #fff;
    margin-left: auto;
    margin-right: auto;
    border-radius: 10px;
    position: absolute;
    z-index: 1;
    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    -moz-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
  }

  form {
    width: 100%;
    height: 65%;
    position: absolute;
    bottom: 0;
    display: flex;
    align-items: flex-start;
    flex-direction: column;
    font-size: 13px;

    input {
      width: 90%;
      height: 25px;
      border: 2px solid #ccc;
      outline: none;
      border-radius: 5px;
      padding-left: 5px;
      margin-left: auto;
      margin-right: auto;

      &:focus {
        box-shadow: 0 0 5px #d4eafd;
        border-color: #4bdee5;
      }
    }
  }

  label {
    padding: 15px 0px 0px 20px;
    color: #595959;
  }

  .date--expiration {
    width: 90%;
    height: 50px;
    margin: 20px 0px 0px 0px;
    padding: 0px 5px 0px 5px;
    margin-left: auto;
    margin-right: auto;
    display: flex;
    justify-content: space-between;
    align-items: center;

    input {
      width: 100px;
    }
  }

  select {
    width: 100px;
    height: 33px;
    border: 2px solid #ccc;
    outline: none;
    border-radius: 5px;
  }

  .card__logo {
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-user-drag: none;
    -webkit-touch-callout: none;
    pointer-events: none;
    margin-right: 5px;
    margin-top: 5px;
  }

  img {
    width: 60px;
    height: 40px;
    user-select: none;
  }

  img[src=""] {
    display: none;
  }

  .visa {
    width: 58px;
    height: 25px;
  }

  .elo {
    height: 27px;
  }

  .Cheader {
    width: 100%;
    height: 30%;
    display: flex;
    justify-content: space-between;
    align-content: center;
    flex-direction: row-reverse;
  }

  .card__chip {
    width: 55px;
    height: 40px;
    background: #D4AF37;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 7px;
    margin-left: 5px;
    margin-top: 5px;

    svg {
      width: 58px;
      height: 80px;
      position: relative;
      top: 7px;
    }
  }

  .Cbody {
    width: 100%;
    height: 33px;
    position: absolute;
    bottom: 40%;
    display: flex;
    align-items: center;

    p {
      font-size: 30px;
      margin-left: 8%;
      color: white;
    }
  }

  .Cfooter {
    width: 95%;
    height: 33px;
    position: absolute;
    bottom: 6%;
    margin-left: 5px;
    display: flex;
    align-items: center;

    .name {
      width: 70%;
      height: 100%;
      color: white;
      display: flex;
      align-items: center;
      flex-direction: column;
      margin: 0;
      padding: 0;
    }
  }

  .name {
    p {
      position: absolute;
      left: 0;
      top: 20px;
    }

    span {
      font-size: 10px;
      position: absolute;
      left: 0;
      top: 0;
    }
  }

  .expiration__data {
    width: 30%;
    height: 100%;
    color: white;

    p {
      position: absolute;
      right: 0;
      top: 20px;
    }

    span {
      font-size: 10px;
      position: absolute;
      right: 0;
      top: 0;
    }
  }

  .btn {
    width: 90%;
    height: 45px;
    position: absolute;
    bottom: 7%;
    left: 50%;
    transform: translateX(-50%);
    border: none;
    border-radius: 4px;
    background: #2e2e2e;
    box-shadow: 3px 10px 20px 0px rgba(0, 0, 0, 0.3);
    font-size: 20px;
    color: white;
    transition: .1s ease;
    cursor: pointer;
    outline: none;
  }

  .btn__active {
    bottom: 6%;
    left: 49%;
  }

  .cvv-active {
    transform: rotateY(180deg);
  }

  .front {
    width: 350px;
    height: 200px;
    position: absolute;
    backface-visibility: hidden;
  }

  .back {
    position: absolute;
    backface-visibility: hidden;
    width: 350px;
    height: 200px;
    color: white;
    transform: rotateY(180deg);
    border-radius: 10px;
    background: url("https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcRtX-QkYwDKEXw_jriWgI5vplXUadOusPHRitOZKZCfBqJqK7IU");
    background-size: cover;
    z-index: 2;
    -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    -moz-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
  }

  .strip {
    width: 100%;
    height: 40px;
    background: linear-gradient(135deg, #404040, #1a1a1a);
    position: relative;
    top: 25px;
  }

  .cvv-container {
    width: 90%;
    height: 40px;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);

    p {
      z-index: 2;
      position: absolute;
      left: 65%;
      top: 25%;
      color: black;
      margin: 0;
    }

    .cvv-strip {
      width: 100%;
      height: 35px;
      background: white;
      position: absolute;
      top: 5%;
    }
  }

  .valid {
    border-color: #4BB54380;
    box-shadow: 0 0 2px #4BB54380;
    background: #4BB54310;

    &:focus {
      border-color: #4bb54310;
      box-shadow: 0 0 2px #4BB543;
    }
  }

  .notValid {
    border-color: #D8000C;
    box-shadow: 0 0 2px #D8000C;
    background-color: brown !important;

    &:focus {
      border-color: #D8000C;
      box-shadow: 0 0 2px #D8000C;
      background-color: brown !important;
    }
  }
}
</style>
0 likes
7 replies
MohamedTammam's avatar

When using ref to access/modify the value in the Javascript part you need to use .value

Example

this.cardType.value = ''

You need to value that pattern for the rest of the components.

1 like
muathye's avatar
Level 41

Thank you @MohamedTammam the project is working on dev but not working on build I tried your suggestion and it is stopped working on dev too.

1 like
Sinnbeck's avatar

@muathye easiest way is to write debugger on a line in js to stop on that line. Open the dev tools (f12) in the browser and refresh

1 like
muathye's avatar
Level 41

@Sinnbeck It works probably on npm run dev but not working on npm run build. And I can't add debugger on dev tools while the project is in production from npm run build

1 like
Sinnbeck's avatar

@muathye You can run npm run build locally. No need to test on production

1 like
Niush's avatar

It should be:

function inputFormat() {
  let text = cardNumber.value.split(" ").join("");
  
  cardVadid.value = text;

  //....

  cardNumber.value = text;

  this.GetCardType(cardVadid.value);
}

You need .value and do not need this.

1 like

Please or to participate in this conversation.