mchandleraz
mchandleraz

Reputation: 381

How can I merge HTML attributes with component data in Vue?

I'm trying to extract some repeated code into a Vue component. I want to be able to pass a list of CSS classes into the component using the class HTML attribute, and merge those with a "default class" that is defined in the component. I want the public interface of this component to resemble a standard HTML element, but if I try to use "class" as a prop Vue throws an error about using a JS keyword. If I try to use $attrs, my default class gets wiped out.

Is it possible to merge HTML attributes with local component data, and use the result in my template? Below is what I would like to achieve:

<template>
  <img src="imageUrl" class="classes"
</template>

export default {
  computed: {
    imageUrl() { return 'urlString' },
  },
  classes() { return `${this.$attrs.class} default-class` }
}

And I'd expect an implementer to be able to use my component like so:

<CustomImageComponent class="class-name another-class" />

Which I'd expect to render this:

<img src="urlString" class="class-name another-class default-class" />

Upvotes: 4

Views: 3480

Answers (3)

Daniel
Daniel

Reputation: 35704

It already happens (automatically)

using <CustomImageComponent class="class-name another-class" />

will render<template><img src="imageUrl" class="my-default-class"/></template>

as <img src="imageUrl" class="my-default-class class-name another-class"/>

(in that order, with the in-template class first, and the passed classes after)

the issue is that if you have a nested DOM element that you want to apply it to, you cannot do that and you will have to use a prop

ie:

using <CustomImageComponent class="class-name another-class" />

will render<template><div><img src="imageUrl" class="my-default-class"/></div></template>

as <div class="class-name another-class"><img src="imageUrl" class="my-default-class"/></div>

and there's nothing you can do about that, other than use custom props.

Upvotes: 3

codeMonkey
codeMonkey

Reputation: 4845

A couple things you could do, both with this.$el.classList.

On mounted:

mounted() {
    this.$el.classList.add('default-class');
}

Computed property:

computed: {
    classListWithDefault() { 
        return `${this.$el.classList.toString()} default-class`;
    }
}

Upvotes: -1

Alizadeh118
Alizadeh118

Reputation: 1064

You just need to use v-bind: or only colon(:) before the attributes to pass data as a value and that's it, Vue automatically merge classes, see link below:

https://codesandbox.io/s/30oly1z326

Upvotes: 0

Related Questions