Using $refs with Element UI's input component - vue.js

Is it possible to use ref with el-input component from Element-UI? I am trying to use $refsto focus on the input when my Vue instance is mounted. Here is my code:
<div id="app">
<el-input type="text" ref="test" placeholder="enter text"></el-input>
</div>
And in my Vue instance:
new Vue({
el: "#app",
mounted(){
this.$refs.test.focus()
}
})
The focus method is not working at all, even if I move this.$refs.test.focus() into a method and try to trigger it through an event.

The $refs object stores Vue components and should be working fine. The problem is that you are attempting to invoke a focus method which doesn't exist in the component, but rather on an input somewhere inside the component's template.
So, to actually find the input you'd have to do something like this:
this.$refs.test.$el.getElementsByTagName('input')[0].focus();
Not the prettiest line of code ever made, right? So, instead of calling anything in your app's mounted method, if you want to autofocus on the input, just do this:
<el-input type="text" placeholder="enter text" autofocus></el-input>

This can be also solved your problem.
// Focus the component, but we have to wait
// so that it will be showing first.
this.$nextTick(() => {
this.$refs.inputText.focus();
});

Now you can use it like this
<div id="app">
<el-input type="text" ref="test" placeholder="enter text"></el-input>
</div>
this.$refs.test.focus();
https://element.eleme.io/#/en-US/component/input#input-methods

Related

Vuetify custom v-text-field component is not updating the v-model

I’m following this documentation: https://v2.vuejs.org/v2/guide/components.html
I created custom v-text-field component which looks like that:
<template>
<div>
<v-text-field
:label="label"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"></v-text-field>
</div>
</template>
<script>
export default {
name: "txtbox",
props: ['value', 'label'],
}
</script>
I implemented it in my main component almost succesfully:
<txtbox
label="Name"
v-model="eventName"
/>
I don’t think it is necessary to paste all the code, but if it is I will edit the post. Here is how it works:
I have a list, when i click on the list element the text-field displays content of it, this works fine. Sadly when I’m editing the content of text-field the v-model value is not updating. I can also add that it works fine with normal (like in the docs) instead of . Is there anything I can do to make it work, or should i use simple input ?
Thanks
When you want to $emit the new value, you just have to emit the $event, and not $event.target.value
<template>
<div>
<v-text-field
:label="label"
v-bind:value="value"
v-on:input="$emit('input', $event)"></v-text-field>
</div>
</template>
v-on:input can also be shortened to just #input

Vue, change native event not working, input native does

I have a question and maybe a Vue bugg.
I have a custom component that needs a #change.native event. But it does not trigger anything and I could not find anything about this issue myself.
So i tried some different stuff and like #click.native and #input.native does work. Even tho #input.native works and do the trick i want to, i still want to know why the change event does not work.
Anybody? Else I should report this.
Vue version: 2.5.2
<custom-input type="search"
placeholder="search"
v-model="search"
#input.native="change" />
If the <input /> inside the custom component is a child element of another element, then the event listener bound by the .native modifier will not be reached, since it is listening to the event of a different element.
custom-input.vue:
<template>
<div>
<input :value="someValue" />
</div>
</template>
<script>
export default {
props: ['value']
}
</script>
so if you have this scenario, then the #change.native will be bound on the <div> (the wrapper).
(sadly) You need to manually propagate the event manually.

Vue mounted jquery code not working on DOM re-render

I'm trying to use jqueryui datapicker with vue. The input for the datepicker is inside a template if-else condition.
The datepicker works just fine initially but after flipping back and forth using the template if-else it stops working.
It seems the jquery code is no longer mounted after a DOM re-render.
<template v-if="...">
<input name="startdate" id="startdate" v-model="date" type="text">
</template>
<template v-else>
<div>Some other display</div>
</template>
mounted: function() {
$('#startdate').datepicker({
onSelect:function(selectedDate, datePicker) {
this.date = selectedDate;
}
});
I placed the jquery code instead inside "updated" and it works now, but this code gets called every single time there is a DOM change.
I'm wondering it there is a better way to accomplish this?
updated: function () {
this.$nextTick(function () {
....
....
You might use v-show instead. This will only toggle the display value of the element, not remove from or add to the DOM, so any event handlers attached won't be affected. From the Vue docs: https://v2.vuejs.org/v2/guide/conditional.html#v-show
That said, #Imre_G is probably right about finding a Vue datepicker to use.
Try this: use your component with mouted() hook and just add to your component the v-once directive. Like this: <input name="start date" ... v-once>
But, as mentioned in Imre_G comment, consider to use some premaded Vue datepicker component. It is far more better idea as mixing Vue with jQuery.

How to avoid vue component redraw?

I have prepared tag input control in Vue with tag grouping. Templates includes:
<script type="text/x-template" id="tem_vtags">
<div class="v-tags">
<ul>
<li v-for="(item, index) in model.items" :key="index" :data-group="getGroupName(item)"><div :data-value="item"><span v-if="typeof model.tagRenderer != 'function'">{{ item }}</span><span v-if="typeof model.tagRenderer == 'function'" v-html="tagRender(item)"></span></div><div data-remove="" #click="remove(index)">x</div></li>
</ul>
<textarea v-model="input" placeholder="type value and hit enter" #keydown="inputKeydown($event,input)"></textarea>
<button v-on:click="add(input)">Apply</button>
</div>
</script>
I have defined component method called .getGroupName() which relays on other function called .qualifier() that can be set over props.
My problem: once I add any tags to collection (.items) when i type anything into textarea for each keydown .getGroupName() seems to be called. It looks like entering anything to textarea results all component rerender?
Do you know how to avoid this behavior? I expect .getGroupName to be called only when new tag is added.
Heres the full code:
https://codepen.io/anon/pen/bKOJjo?editors=1011 (i have placed debugger; to catch when runtime enters .qualifier().
Any help appriciated.
It Man
TL;DR;
You can't, what you can do is optimize to reduce function calls.
the redraws are dynamic, triggered by data change. because you have functions (v-model and #keydown) you will update the data. The issue is that when you call a function: :data-group="getGroupName(item)" it will execute every time, because it makes no assumptions on what data may have changed.
One way of dealing with is is setting groupName as a computed key-val object that you can look up without calling the function. Then you can use :data-group="getGroupName[item]" without calling the function on redraw. The same should be done for v-html="tagRender(item)"
Instead of trying to fight how the framework handles data input events and rendering, instead use it to your advantage:
new Vue({
el: '#app',
template: '#example',
data() {
return {
someInput: '',
someInputStore: []
};
},
methods: {
add() {
if (this.someInputStore.indexOf(this.someInput) === -1) {
this.someInputStore.push(this.someInput);
this.someInput = '';
}
},
}
});
<html>
<body>
<div id="app"></div>
<template id="example">
<div>
<textarea v-model="someInput" #keyup.enter.exact="add" #keyup.shift.enter=""></textarea>
<button #click="add">click to add new input</button>
<div>
{{ someInputStore }}
</div>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
</body>
</html>
Event modifiers modifying
In the example, you can see that I am using 4 different event modifiers in order to achieve the desired outcome, but I will focus on the combination of them here:
#keyup.enter.exact - allows control of the exact combination of system modifiers needed to trigger an event. In this case, we are looking for the enter button.
#keyup.shift.enter - this is the interesting bit. Instead of trying to hackily prevent the framework from firing on both events, we can use this (and a blank value) to prevent the event we added into #keyup.enter.exact from firing. I must note that ="" is critical to the whole setup working properly. Without it, you aren't giving vue an alternative to firing the add method, as shown by my example.
I hope this helps!

Keeping things DRY in Vue.js

I've done a couple of projects with React in the last year and I've switched to Vue for my current one, attracted by its greater simplicity, less verbose nature and the fact that you don't have to transpile your code to work, so it's easier to get going with and more flexible (well, to be accurate you don't have to transpile with React either, there's no need to use JSX, but it loses one of its great benefits if you don't).
Anyway, one of the things I'm missing from React (and I'm sure it's just ignorance of the Vue way which is my problem) is a way of reusing a fragment of code to avoid repeating myself in templates. The specific situation which prompted this question was a template where I have a custom input element like this:
<input ref="input" :id='name' :name='name' :type='fieldType' class='form-control' :value="value" :readonly="readonly" :disabled="disabled" #input="handleInput"/>
In certain situations I'd want to wrap it in a div, otherwise I'd want to use it as is. With React, I'd simply store it in a variable, something like this:
var inp=( <input ref="input" :id='name' :name='name' :type='fieldType' class='form-control' :value="value" :readonly="readonly" :disabled="disabled"
#input="handleInput"/>);
Then I could do something like the following:
var myInput;
if(divSituation){
myInput=(<div>{inp}</div>);
} else {
myInput=inp;
}
Then I could use the myInput var. The Vue logic doesn't seem to allow this, though. Unless, of course, using JSX within Vue would allow me to do the very same thing? I currently have for this in Vue something like the following, which offends me:
<template v-if="divSituation">
<div><input ref="input" :id='name' :name='name' :type='fieldType' class='form-control' :value="value" :readonly="readonly" :disabled="disabled" #input="handleInput"/></div>
</template>
<template v-else>
<input ref="input" :id='name' :name='name' :type='fieldType' class='form-control' :value="value" :readonly="readonly" :disabled="disabled" #input="handleInput"/
</template>
You can create vue components for re-usable components, which can be used as par requirement.
You can find an example of re-usable input component in vue docs:
<currency-input v-model="price"></currency-input>
and you can write that as re-usable component like following:
Vue.component('currency-input', {
template: '\
<span>\
$\
<input\
ref="input"\
v-bind:value="value"\
v-on:input="updateValue($event.target.value)"\
>\
</span>\
',
props: ['value'],
methods: {
// Instead of updating the value directly, this
// method is used to format and place constraints
// on the input's value
updateValue: function (value) {
var formattedValue = value
// Remove whitespace on either side
.trim()
// Shorten to 2 decimal places
.slice(0, value.indexOf('.') + 3)
// If the value was not already normalized,
// manually override it to conform
if (formattedValue !== value) {
this.$refs.input.value = formattedValue
}
// Emit the number value through the input event
this.$emit('input', Number(formattedValue))
}
}
})
You can add more props for readonly, disabled, etc.
You can also have a look at custom input elements of element-ui and it's code.
Given the example you have given, You can use v-html more efficiently. with v-html, you can pass a HTML string which will be rendered as HTML. However Note: the contents are inserted as plain HTML - they will not be compiled as Vue templates.
You can have a computed property, which will return HTML string as par your variable: divSituation, like following:
var data = {
templateInput: '<input ref="input" :id="name" :name="name" :type="fieldType" class="form-control" :value="value" :readonly="readonly" :disabled="disabled" #input="handleInput"/>',
divSituation: true,
myInput: ''
}
var demo = new Vue({
el: '#demo',
data: function(){
return data
},
computed: {
getMyInput: function(){
if(this.divSituation){
return this.templateInput
}
else{
return '<div>' + this.templateInput + '</div>'
}
}
}
})
Now you can just render myInput in HTML using v-html like this:
<div id="demo">
<div v-html="getMyInput">
</div>
</div>
check out working fiddle.