Vue, TypeScript Can't Call Component Methods

Viewed 3144

I'm still pretty new to Vue and I'm having troubles working this out. From all the code examples I can find, my code looks like it should work.

I have defined a component like this:

<template>
    <div class="row">
        <div id="site-header">
            <img v-bind:src="headerImgUrl" alt="hero-image">
            <div class="hero-text strokeme">Some Hero Text</div>
        </div>

    </div>
</template>

<script lang="ts">
    import { Component, Vue } from 'vue-property-decorator';

    const bgimages = [
        { url: require('../assets/hero/hero1.png') },
        { url: require('../assets/hero/hero2.png') },
        { url: require('../assets/hero/hero3.png') },
        { url: require('../assets/hero/hero4.png') },
        { url: require('../assets/hero/hero5.png') },
        { url: require('../assets/hero/hero6.png') },
        { url: require('../assets/hero/hero7.png') },
        { url: require('../assets/hero/hero8.png') },
        { url: require('../assets/hero/hero9.png') },
        { url: require('../assets/hero/hero10.png') },
        { url: require('../assets/hero/hero11.png') },
        { url: require('../assets/hero/hero12.png') },
        { url: require('../assets/hero/hero13.png') },
        { url: require('../assets/hero/hero14.png') },
    ];

    @Component({
        data() {
            console.log(this.getHeaderImageUrl());
            return {
                headerImgUrl: bgimages[0].url,
            };
        },
        methods: {
            getHeaderImageUrl(): string {
                const min = Math.ceil(1);
                const max = Math.floor(14);
                const i = Math.floor(Math.random() * (max - min + 1)) + min;
                return bgimages[i].url;
            },
        },
    })
    export default class Home extends Vue { }
</script>

<style lang="scss">
    #site-header {
        position: relative;
        margin-bottom: 2em;
    }
    .hero-text {
        font-family: 'Patua One', cursive;
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        font-size: 6em;
        font-weight: 800;
        text-align: center;
        color: #000;
    }
    .strokeme {
        text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
    }
</style>

However, when compiling I get the error:

Error TS2339 (TS) Property 'getHeaderImageUrl' does not exist on type 'Vue'.

How can I set this up so it can call it's own methods? Also is there a better approach to get a random url into an image source?

Alternatively, how would I update the data.headerImgUrl from inside the getHeaderImageUrl() method?

If I try this:

methods: {
    getHeaderImageUrl() {
        const min = Math.ceil(1);
        const max = Math.floor(14);
        const i = Math.floor(Math.random() * (max - min + 1)) + min;
        //return bgimages[i].url;
        this.headerImgUrl = bgimages[i].url;
    },
},

It errors with:

Error TS2339 (TS) Property 'headerImgUrl' does not exist on type 'Vue'.

I'm clearly still not getting the scoping on something or have configured something wrong as this seem to be really basic stuff.

UPDATE

So I've re-written my component like this:

<template>
    <div class="row">
        <div id="site-header">
            <img v-bind:src="headerImgUrl" alt="hero-image">
            <div class="hero-text strokeme">Some Hero Text</div>
        </div>
    </div>
</template>

<script lang="ts">
    import Vue from 'vue';

    const bgimages = [
        { url: require('../assets/hero/hero1.png') },
        { url: require('../assets/hero/hero2.png') },
        { url: require('../assets/hero/hero3.png') },
        { url: require('../assets/hero/hero4.png') },
        { url: require('../assets/hero/hero5.png') },
        { url: require('../assets/hero/hero6.png') },
        { url: require('../assets/hero/hero7.png') },
        { url: require('../assets/hero/hero8.png') },
        { url: require('../assets/hero/hero9.png') },
        { url: require('../assets/hero/hero10.png') },
        { url: require('../assets/hero/hero11.png') },
        { url: require('../assets/hero/hero12.png') },
        { url: require('../assets/hero/hero13.png') },
        { url: require('../assets/hero/hero14.png') },
    ];

    export default Vue.extend({
        computed: {
            headerImgUrl() {
                return this.getHeaderImageUrl();
            },
        },
        methods: {
            getHeaderImageUrl() {
                const min = Math.ceil(1);
                const max = Math.floor(14);
                const i = Math.floor(Math.random() * (max - min + 1)) + min;
                console.log(1);
                return bgimages[i].url;
            },
        },
    });

</script>

<style lang="scss">
...
</style>

But I'm still getting an error where the method is inaccessible:

Error TS2339 (TS) Property 'getHeaderImageUrl' does not exist on type 'CombinedVueInstance>>'.

1 Answers

You actually combining two different approaches to create a component. Here are two examples:

using "vue-property-decorator" :

import { Vue, Component, Prop } from "vue-property-decorator";

@Component()
export default class HelloDecorator extends Vue {

    @Prop(type: String, default: "")
    name: string;

    @Prop({ type: Number, default: 1 })
    initialEnthusiasm: number;

    enthusiasm = this.initialEnthusiasm;

    increment() {
        this.enthusiasm++;
    }

    decrement() {
        this.enthusiasm--;
    }

    get exclamationMarks(): string {
        return Array(this.enthusiasm + 1).join('!');
    }
} 

using extend :

import Vue from "vue";
export default Vue.extend({
    props: {
        name: {
            type: String,
            default: ""
        },
        enthusiasmLevel: {
            type: Number,
            default: 1
        }
    },
    computed: {
        helloMessage() {
            return "Hello " + this.name + this.getExclamationMarks(this.enthusiasmLevel);
        }
    },
    methods: {
        getExclamationMarks(numChars) {
            return Array(numChars + 1).join('!');
        },
        onDecrement() {
            this.$emit("onDecrement");
        },
        onIncrement() {
            this.$emit("onIncrement");
        }
    }
})
Related