I want to create a component with Vue.js containing a label
and an input
. For example :
<label for="inputId">Label text</label>
<input id="inputId" type="text" />
How can I set a unique ID for each component instance?
Each component has a unique id which can be accessed as this._uid
.
<template>
<div>
<label :for="id">Label text for {{id}}</label>
<input :id="id" type="text" />
</div>
</template>
<script>
export default {
data () {
return {
id: null
}
},
mounted () {
this.id = this._uid
}
}
</script>
If you want more control over the ids you can for example, generate them inside a parent component.
To Nihat's point (above): Evan You has advised against using _uid: "The vm _uid is reserved for internal use and it's important to keep it private (and not rely on it in user code) so that we keep the flexibility to change its behavior for potential future use cases. ... I'd suggest generating UIDs yourself [using a module, a global mixin, etc.]"
Using the suggested mixin in this GitHub issue to generate the UID seems like a better approach:
let uuid = 0;
export default {
beforeCreate() {
this.uuid = uuid.toString();
uuid += 1;
},
};
Update: Code will throw an error if ._uid
property does not exist in the instance so that you can update it to use something custom or new unique id property if provided by Vue.
Although zxzak's answer is great; _uid
is not a published api property. To save a headache in case it changes in the future, you can update your code with just one change with a plugin solution like below.
Vue.use({
install: function(Vue, options) {
Object.defineProperty(Vue.prototype, "uniqId", {
get: function uniqId() {
if ('_uid' in this) {
return this._uid;
}
throw new Error("_uid property does not exist");
}
});
}
});
_uid
property does not exist anymore.
Update
I published the vue-unique-id Vue plugin for this on npm.
Answer
None of the other solutions address the requirement of having more than one form element in your component. Here's my take on a plugin that builds on previously given answers:
Vue.use((Vue) => {
// Assign a unique id to each component
let uidCounter = 0;
Vue.mixin({
beforeCreate: function() {
this.uidCounter = uidCounter.toString();
uidCounter += 1;
},
});
// Generate a component-scoped id
Vue.prototype.$id = function(id) {
return "uid-" + this.uidCounter + "-" + id;
};
});
This doesn't rely on the internal _uid
property which is reserved for internal use.
Use it like this in your component:
<label :for="$id('field1')">Field 1</label>
<input :id="$id('field1')" type="text" />
<label :for="$id('field2')">Field 2</label>
<input :id="$id('field2')" type="text" />
To produce something like this:
<label for="uid-42-field1">Field 1</label>
<input id="uid-42-field1" type="text" />
<label for="uid-42-field2">Field 2</label>
<input id="uid-42-field2" type="text" />
uuid
was an unfortunate name choice so I've renamed it to uidCounter
. Javascript is single threaded so I don't see which race conditions could possibly creep in the code above. uidCounter
is read and updated atomically because no there are no other threads and execution isn't preempted. Imo, an actual UUID doesn't offer any benefit over a simple incrementing counter for this use-case, but yes it works.
npm i -S lodash.uniqueid
Then in your code...
<script>
const uniqueId = require('lodash.uniqueid')
export default {
data () {
return {
id: ''
}
},
mounted () {
this.id = uniqueId()
}
}
</script>
This way you're not loading the entire lodash library, or even saving the entire library to node_modules
.
id
properties on elements. Also, -S
(or --save
) has been the default on npm
since 2017 and doesn't actually do anything any more.
This seem to work for me using in nuxtjs
https://www.npmjs.com/package/uuid
example of generated output: element: 47bfe557-d75f-455c-9a37-85b7935b297b
package.json
"dependencies": {
"uuid": "^8.3.2"
},
on child component, might not be the best way but seem to work
...
<ComponentName v-if="element" />
...
import { v4 as uuidv4 } from 'uuid';
...
data() {
return {
element: null,
}
}
...
mounted() {
this.element = uuidv4();
}
The simplest way I found was to create a UUID (uuid package
) manually through a global mixin. That way you won't rely on anything that can potentially change or become deprecated in the future like this._uid
.
You first have to install the uuid
package:
npm i uuid
Then, in your main.js
file create a global mixin:
// rest of imports
import { v4 as uuidv4 } from 'uuid';
const app = Vue.createApp(App);
app.mixin({
data() {
return {
componentId: uuidv4()
}
},
});
app.use(store).use(router).mount('#app');
And here is how you can us it in a component:
<template>
<div>
<h1>{{ componentId }}</h1>
<button @click="printId()">click me for componentId.</button>
</div>
</template>
<script>
export default {
methods: {
printId: function() {
console.log(this.componentId);
}
}
}
</script>
If you're using TypeScript, without any plugin, you could simply add a static id in your class component and increment it in the created() method. Each component will have a unique id (add a string prefix to avoid collision with another components which use the same tip)
<template>
<div>
<label :for="id">Label text for {{id}}</label>
<input :id="id" type="text" />
</div>
</template>
<script lang="ts">
...
@Component
export default class MyComponent extends Vue {
private id!: string;
private static componentId = 0;
...
created() {
MyComponent.componentId += 1;
this.id = `my-component-${MyComponent.componentId}`;
}
</script>
class
-based syntax for defining Vue components? For example using `export default defineComponent({ created() { ... }, ... });
A simple approach that I haven't seen in the replies is:
<template>
<div>
<label :for="id">Label text for {{id}}</label>
<input :id="id" type="text" />
</div>
</template>
<script>
import uniqueId from 'lodash-es/uniqueId'
export default {
computed: {
id () {
# return this._uid
return uniqueId('id')
}
}
}
</script>
In Vue2, use v-bind
.
Say I have an object for a poll
<div class="options" v-for="option in poll.body.options">
<div class="poll-item">
<label v-bind:for="option._id" v-bind:style="{color: option.color}">
{{option.text}}
</label>
<input type="radio" v-model="picked" v-bind:value="option._id" v-bind:id="option._id">
</div>
</div>
v-for="(option, index) in poll.body.options"
, and use index
in you v-bind.
According to MDN, you can just make an implicit label binding.
<label>
Label text
<input type="text" />
</label>
This way you don't even need to assign an id.
id
property, but this doesn't really answer the question. There are cases in which you can't avoid needing to use an id
. Also, MDN has a Vue tutorial (here) where they give an non-working example of how to set unique IDs on components... That's what brought me here.
This package seems to be a good solution for the underlying issue of having non-unique IDs in your DOM across multiple components:
It is a trend to use components. Components are cool, they are small, obvious, easy to use and modular. Untill it comes to the id property. Some HTML tag attributes requires using an id property, like label[for], input[form] and many of aria-* attributes. And the problem with the id is that it is not modular. If several id properties on the page will has the same value they can affect each other. VueUniqIds helps you to get rid of this problem. It provides the set of id-related directives which value is automatically modified by adding unique string while keeping the attrbitue easy to read.
This seem to work for me using https://www.npmjs.com/package/uuid
example of generated output: element: 47bfe557-d75f-455c-9a37-85b7935b297b
package.json
"dependencies": {
"uuid": "^8.3.2"
},
component.vue
v-if="element"
...
import { v4 as uuidv4 } from 'uuid';
...
data() {
return {
element: null,
}
}
...
mounted() {
this.element = uuidv4();
}
For Vue.js v3 you can get id like this:
In template: {{ $.uid }}
In script: this.$.uid
Or use your own function or mix them:
this.componentUid = ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
This will return e.g:
aa174375-5b75-4919-acd0-980fcd54003c
If your uid is not used by other compoment, I have an idea.
uid: Math.random()
Simple and enough.
It can also be achieved using this pattern (Vue 2.0 v-bind) , so let say you have a list of items to iterate over and you want to give some dom element uninque id's.
new Vue({
el:body,
data: {
myElementIds : [1,2,3,4,5,6,8]
}
})
Html
<div v-for="id in myElementIds">
<label v-bind:for="id">Label text for {{id}}</label>
<input v-bind:id="id" type="text" />
<div>
Hope it helps
Success story sharing
ready
method was removed in Vue 2.0 and above. I was very confused when theready
method wasn't executing. stackoverflow.com/a/40209796/126751data
must be a function that returns an object: vuejs.org/v2/guide/components.html#data-Must-Be-a-Functionthis._uid
is not valid. Instead, generate your id yourself, e.g.public id = uuid4();
, see uuid4._uid
, it "is reserved for internal use and it's important to keep it private (and not rely on it in user code) so that we keep the flexibility to change its behavior for potential future use cases".