4

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>>'.

2
  • I had quite a lot of issue using the Class-style vue components, so I just always opted to use the Vue.extend({/*definitions here*/}) Commented Jun 30, 2019 at 7:51
  • Really? How would I re-write this into that format? Commented Jun 30, 2019 at 7:55

1 Answer 1

4

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");
        }
    }
})
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks. Wow, OK. So how does Vue.component('', {}) really differ?
I've refactored to use the .extend approach but when I try to set headerImgUrl it's now read-only.
Here is a stackoverflow answer about the difference between extend and component: stackoverflow.com/questions/40719200/what-is-vue-extend-for
for readonly purpose you can create a getter and a private field of headerImgUrl
No, the problem is the other way around it seems to be readonly when I want read/write. I'm getting a false positive with a typescript error but the app runs ok so I need to investigate my environment I think.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.