VueJS 2 Directives insert event - vue.js

I'm new VueJS Student, and i was trying to do a directive, and i stopped because i dont know why i can't get the element. What's am i doing wrong? I just want to get the el element.
Here is a JSFiddle or the code below.
HTML
<div class="container" id="vue-app">
<ul>
<li v-test v-for="item in itens">
{{item.name}}
</li>
</ul>
<span id="directiveLog"></span>
</div>
JS
Vue.directive('test', {
inserted: function (el) {
document.querySelector('#directiveLog').innerHTML = el;
}
})
new Vue({
el: '#vue-app',
data: {
itens:[
{name: "hello"},
{name: "fun"},
{name: "world"}
]
}
});

Parameter 'el' in function inserted(el) {...} - it is your element where v-test is inserted.
Vue.directive('test', {
inserted: function (el) {
document.querySelector('#directiveLog').innerHTML = el.innerHTML;
}
})
new Vue({
el: '#vue-app',
data: {
itens:[
{name: "hello"},
{name: "fun"},
{name: "world"}
]
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div v-test class="container" id="vue-app">
<ul>
<li v-for="item in itens">
{{item.name}}
</li>
</ul>
<span id="directiveLog"></span>
</div>

Related

VueJS renders variable twice in loop

When I press some key in editable <li> it shows this content twice. Out of this v-for loop, it shows only once. So in this array is for example ['a'] but in <li> it shows 'aa'
new Vue({
el: '#app',
data: {
component: {
items: ['']
}
},
methods: {
onKeydown(e, index) {
if(e.key === 'Enter') {
e.preventDefault()
this.component.items.push('')
}
},
onInput(e, index, item) {
this.component.items.splice(index, 1, e.target.innerHTML)
}
}
});
<div id="app">
<ul>
<li contenteditable="true"
v-for="(item, index) in component.items"
:key="index"
#keydown="onKeydown($event, index)"
#input="onInput($event, index, item)"
>{{ item }}</li>
</ul>
{{ component.items }}
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Apply v-focus to the first input field on a page

I've a Vue component in which I'm trying to autofocus the first field using v-focus. But my problem is, I've dynamic components that will be included at the top of the page. So in that case how can I apply autofocus to dynamically included component?
They key is to set ref on all your inputs to the same string like this:
<input type="text" ref="myInputs"/>
Then you will have access to an array called this.$refs.myInputs inside an event handler.
So you just need to do
this.$refs.myInputs[0].focus();
new Vue({
el: "#app",
mounted() {
this.$refs.myInputs[0].focus();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="index in 3" :key="index">
<input ref="myInputs" type="text" />
</div>
</div>
</div>
It's hard to tell how you're adding the input(s) to the DOM, without any pseudo code from you, but this is one way to do it..
[CodePen mirror]
new Vue({
el: "#app",
data: {
inputs: ["firstName", "lastName"]
},
watch: {
inputs() {
this.$nextTick(() => {
this.focusFirstInput();
});
}
},
methods: {
focusFirstInput() {
let first = this.inputs[0];
let firstInput = this.$refs[first][0];
firstInput.focus();
},
handleClick() {
this.inputs.push("newInput");
}
},
mounted() {
this.focusFirstInput();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="(input, index) in inputs" :key="index">
<input :ref="input" type="text" />
</div>
<div>
<button type="button" #click="handleClick">Click to add input</button>
</div>
</div>
</div>
I found this answer on Laracast and it worked for me. All I did was insert the code below in my dynamic form field.
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
HTML
<div id="app">
<ul v-for="item in items">
<li>
<input :ref="'title'" v-model="item.title">
</li>
</ul>
<button v-on:click="addItem">Add Item</button>
</div>
JS
let app = new Vue({
el: '#app',
data: {
items: [
{title: 'Apple'},
{title: 'Orange'},
]
},
methods: {
addItem(){
this.items.push({title: "Pineapple"});
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
}
}
});
Note: make sure to add :ref="'title'" into your dynamic form field.
Credits to the original author of the solution.

How to get data from component

I am new to Vue.js.
list.vue:
<template>
<div class="m-products-list">
<ul #mouseover="over">
<Item
v-for="(item,idx) in parentList"
location="item.location"
:key="idx"
:meta="item"/>
</ul>
</div>
</template>
<script>
export default {
...
methods: {
over: function (e) {
let dom = e.target;
let tag = dom.tagName.toLowerCase();
if (tag === 'dd') {
console.log(dom.getAttribute('location'))
}
}
}
}
</script>
The Item is from its parent component. And I want to get item.location in over() when I mouseover an item, but console.log always returns null. Anyone have an idea?
This is technically possible (but there may be a better alternative shown in the next section) by setting a data-* attribute in the Item.
// Item.vue
<li :data-location="location" class="item" ... >
new Vue({
el: '#app',
data() {
return {
items: [
{id: 1, location: 'New York'},
{id: 2, location: 'Los Angeles'},
{id: 3, location: 'Chicago'},
]
}
},
components: {
Item: {
props: ['location'],
template: `<li :data-location="location" class="item">{{location}}</li>`,
}
},
methods: {
over(e) {
console.log(e.target.dataset.location)
}
}
})
<script src="https://unpkg.com/vue#2.6.7/dist/vue.min.js"></script>
<div id="app">
<ul #mouseover="over">
<Item v-for="item in items"
:key="item.id"
:location="item.location" />
</ul>
</div>
A better solution that doesn't require DOM manipulation would be to use the data model in Vue and to move the mouseover event listener to the Item:
Change the argument of over() to the location name (previously the event object):
methods: {
over(location) {
/* ... */
}
}
Move the #mouseover event-listener annotation from ul to the Item in the template, and pass the item.location as an argument:
<ul>
<Item v-for="item in items" #mouseover="over(item.location)" ... />
</ul>
Edit the Item's template to forward its mouseover event to the parent:
// Item.vue
<li #mouseover="$emit('mouseover', $event)" ... >
new Vue({
el: '#app',
data() {
return {
items: [
{id: 1, location: 'New York'},
{id: 2, location: 'Los Angeles'},
{id: 3, location: 'Chicago'},
]
}
},
components: {
Item: {
props: ['location'],
template: `<li #mouseover="$emit('mouseover', $event)" class="item">{{location}}</li>`,
}
},
methods: {
over(location) {
console.log(location)
}
}
})
<script src="https://unpkg.com/vue#2.6.7/dist/vue.min.js"></script>
<div id="app">
<ul>
<Item v-for="item in items"
:key="item.id"
:location="item.location"
#mouseover="over(item.location)" />
</ul>
</div>

vuejs auto-filter out an array marked "Bar"

I am new vuejs but learning a lot. I Have an array of items that renders to a list perfectly fine. I do want to not display anything marked Bar? I have tried !Bar but it does not work. Whats the correct way to do this?
var app = new Vue({
el: "#demo",
data: {
items: [{
childMsg: 'Foo'
}, {
childMsg: 'Bar'
}]
}
});
<script src="https://unpkg.com/vue"></script>
<div id="demo">
<ul v-for="item in items">
<li>{{item.childMsg}}</li>
</ul>
</div>
As usual, there are several approaches. One most straightforward is to exclude the item directly within v-for element template, like this:
<li v-if="item.childMsg !== 'Bar'">{{item.childMsg}}</li>
An alternative would be creating a computed property: array of items that do not match the pattern. Then you can rebase your v-for onto that property. Here's how it can be done:
var app = new Vue({
el: "#demo",
data: {
exclude: '',
items: [{
childMsg: 'Foo'
}, {
childMsg: 'Bar'
}]
},
computed: {
filteredItems() {
return this.items.filter(x => x.childMsg !== this.exclude);
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="demo">
<label>Exclude word... <input type="text" v-model="exclude" /></label>
<ul v-for="item in filteredItems">
<li>{{item.childMsg}}</li>
</ul>
</div>

run loop backward in template in vuejs

<template>
<li v-for="r in recent">
{{r}}
</li>
</template>
recent is an array .
I don't want to list contents of recent from 0 to n-1 .
Instead i want to list it from n-1 to 0.
I tried ->
<template>
<li v-for="r=recent.length-1 ; r>=0 ; r--">
{{r}}
</li>
</template>
But, it didn't work.
Solution 1: Reverse array directly in template
You can actually reverse the array directly in your template before iterating through it, using recent.slice().reverse(). Using .slice() is necessary because this.recent is actually not an array per se. However, I do not prefer this method because it means placing logic in your template.
<template>
<li v-for="r in recent.slice().reverse()">
{{r}}
</li>
</template>
new Vue({
el: '#list',
data: {
recent: [
'Lorem',
'ipsum',
'dolor',
'sit',
'amet'
]
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<ul id="list">
<li v-for="r in recent.slice().reverse()">
{{ r }}
</li>
</ul>
Solution 2: Use a computed property
As what #vbranden has said, you can simply reverse an array in a method and use it in v-for. This is my preferred method.
new Vue({
el: '#list',
data: {
recent: [
'Lorem',
'ipsum',
'dolor',
'sit',
'amet'
]
},
computed: {
recentReversed: function() {
return this.recent.reverse();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<ul id="list">
<li v-for="r in recentReversed">
{{ r }}
</li>
</ul>
You should use Array.prototype.reverse()
Source : link
In your computed
export default Vue.extend({
name: 'mainActivity',
data (){
return{
stuff: ['a', 'b', 'c', 'd']
}
},
computed:{
reverseArray(){return this.stuff.reverse()}
},
created(){}
})
HTML
<template>
<li v-for="r in reverseArray">
{{r}}
</li>
</template>
There is no for-loop form of v-for. There is a range form, which you can use to get the effect you want (with a little simple math).
You could do the same thing with a computed, as vbranden noted in his comment.
new Vue({
el: '#app',
data: {
stuff: ['a', 'b', 'c', 'd']
},
computed: {
stuffIndexCountdown() {
return this.stuff.map((_, i) => i).reverse();
}
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
<div v-for="i in stuff.length">
{{stuff.length - i}}
</div>
<hr>
<div v-for="i in stuffIndexCountdown">
{{i}}
</div>
</div>