“TypeError: Cannot read property ‘$emit’ of undefined” – VueJS

Total
1
Shares

I am trying to implement a simple authentication in vuejs. I have a list of objects in which I have authentic username and password. I am iterating over this list and checking with the entered username and password. If there is a match then I am emitting an event and updating my variable authenticated. But the problem is inside the login in the forEarch loop i am not able to access the emit.

This is my Login.vue file

_x000D_

_x000D_

<template>_x000D_
    <div id="login">_x000D_
        <h1>Login</h1>_x000D_
        <b-form-input v-model="input.username" placeholder="Username"></b-form-input>_x000D_
        <br/>_x000D_
        <b-form-input v-model="input.password" placeholder="Password" type="password"></b-form-input>_x000D_
        <br/>_x000D_
        <b-button variant="primary" v-on:click="login()">Submit</b-button>_x000D_
    </div>_x000D_
</template>_x000D_
_x000D_
<script>_x000D_
    _x000D_
    export default {_x000D_
        name: 'Login',_x000D_
        data() {_x000D_
            return {_x000D_
                input: {_x000D_
                    username: "",_x000D_
                    password: ""_x000D_
                }_x000D_
            }_x000D_
        },_x000D_
        methods: {_x000D_
            login() {_x000D_
                var enteredUsername = this.input.username;_x000D_
                var enteredPassword = this.input.password;_x000D_
                if(enteredUsername !== "" && enteredPassword !== "") {_x000D_
                    this.$parent.mockAccount.forEach(function (element) {_x000D_
                        if (enteredUsername === element.username && enteredPassword === element.password) {_x000D_
                            this.$emit("authenticated", true)_x000D_
                            this.$router.replace({name: "secure"})_x000D_
                        }_x000D_
                    })_x000D_
                }_x000D_
            }_x000D_
        }_x000D_
    }_x000D_
</script>_x000D_
_x000D_
<style scoped>_x000D_
    #login {_x000D_
        width: 500px;_x000D_
        border: 1px solid #CCCCCC;_x000D_
        background-color: #FFFFFF;_x000D_
        margin: auto;_x000D_
        margin-top: 200px;_x000D_
        padding: 20px;_x000D_
    }_x000D_
</style>

_x000D_

_x000D_

_x000D_

Here is my App.vue file

_x000D_

_x000D_

<template>_x000D_
  <div id="app">_x000D_
    <div id="nav">_x000D_
      <router-link v-if="authenticated" to="/login" v-on:click.native="logout()" replace>Logout</router-link>_x000D_
    </div>_x000D_
    <router-view/>_x000D_
  </div>_x000D_
</template>_x000D_
_x000D_
<script>_x000D_
_x000D_
    export default {_x000D_
        name: 'App',_x000D_
        data() {_x000D_
            return {_x000D_
                authenticated: false,_x000D_
                mockAccount: [_x000D_
                    {_x000D_
                        username: "a",_x000D_
                        password: "a"_x000D_
                    },_x000D_
                    {_x000D_
                        username: "rick",_x000D_
                        password: "rick2018"_x000D_
                    },_x000D_
                    {_x000D_
                        username: "nick",_x000D_
                        password: "nick2018"_x000D_
                    },_x000D_
                    {_x000D_
                        username: "paul",_x000D_
                        password: "paul2018"_x000D_
                    }]_x000D_
            }_x000D_
        },_x000D_
        mounted() {_x000D_
            if(!this.authenticated) {_x000D_
                this.$router.replace({ name: "Login" });_x000D_
            }_x000D_
        },_x000D_
        methods: {_x000D_
            setAuthenticated(status) {_x000D_
                this.authenticated = status;_x000D_
            },_x000D_
            logout() {_x000D_
                this.authenticated = false;_x000D_
            }_x000D_
        }_x000D_
    }_x000D_
</script>_x000D_
_x000D_
<style>_x000D_
  body {_x000D_
    background-color: #F0F0F0;_x000D_
  }_x000D_
  h1 {_x000D_
    padding: 0;_x000D_
    margin-top: 0;_x000D_
  }_x000D_
  #app {_x000D_
    width: 1024px;_x000D_
    margin: auto;_x000D_
  }_x000D_
</style>

_x000D_

_x000D_

_x000D_

This is the error i am getting
enter image description here


Solution

ES5 functions have their own this, so change

this.$parent.mockAccount.forEach(function (element) {
  if (enteredUsername === element.username && enteredPassword === element.password) {
    this.$emit("authenticated", true)
    this.$router.replace({name: "secure"})
  }
})

either to an ES6 arrow function (which have the same this as the context they’re defined in)

this.$parent.mockAccount.forEach((element) => {
  if (enteredUsername === element.username && enteredPassword === element.password) {
    this.$emit("authenticated", true)
    this.$router.replace({name: "secure"})
  }
})

or use explicit binding with Function.prototype.bind() (ES5):

this.$parent.mockAccount.forEach(function (element) {
  if (enteredUsername === element.username && enteredPassword === element.password) {
    this.$emit("authenticated", true)
    this.$router.replace({name: "secure"})
  }
}.bind(this))

or use a closure:

const self = this;
this.$parent.mockAccount.forEach(function (element) {
  if (enteredUsername === element.username && enteredPassword === element.password) {
    self.$emit("authenticated", true)
    self.$router.replace({name: "secure"})
  }
})
Leave a Reply

Your email address will not be published. Required fields are marked *