Vue v-for changing from version 1 to version 2 - vue.js

I'm learning Vue.js and found this fiddle that does exactly what I want to do.
Here is the fiddle: https://jsfiddle.net/os7hp1cy/48/
I integrated this and am getting this error:
invalid expression: v-for="user in users | filterBy searchKey | paginate"
So I've done some digging and I see it has changed from version 1 to 2. However, I don't know how to fix this.
<li v-for="user in users | filterBy searchKey | paginate">{{ user.name }}</li>
I would like to replace this with something that Vue 2 will support and will work the same way.

As of Vue version 2, filters can only be used inside text interpolations ({{ }} tags). See the documentation for migrating from Vue version 1.
You can use a computed property to filter the users and use that computed property in the v-for directive instead:
computed: {
filteredUsers: function() {
let key = this.searchKey.toUpperCase();
return this.users.filter((user) => {
return user.name.toUpperCase().indexOf(key) !== -1
})
},
paginatedUsers: function() {
var list = this.filteredUsers;
this.resultCount = list.length
if (this.currentPage >= this.totalPages) {
this.currentPage = this.totalPages
}
var index = this.currentPage * this.itemsPerPage
return list.slice(index - 1, index - 1 + this.itemsPerPage)
}
}
<li v-for="user in paginatedUsers">{{ user.name }}</li>
Also, when using v-for to generate a range of numbers like you do for your page numbers, Vue version to starts the index at 1 instead of 0. So, you'll need to update the logic depending on a starting index of 0 as well.
Here's a working fiddle.

Related

Correct way to implement drill-down tags in vue with vuetify

I am using a v-chip-group with v-chips to represent a tag cloud for records in my database. I have an object array with records that look something like { key:'device', count:100}. A record could have multiple tags, so as you click on a tag, a new query is made that filters on that tag, the result will then have a new tag cloud with a subset of the previous.
It looks something like this:
tag1 (1000), tag2 (100), tag3 (100)
When you click on tag1 you end up with:
tag1 (1000), tag3 (15) (no tag2 because there is no overlap between tag1 and tag2).
Here is the relevant template code:
<v-chip-group v-model="selectedTag" multiple #change="refresh">
<v-chip v-for="tag in tags" :key="tag.key" active-class="primary">
<v-avatar left class="grey">{{ tag.count }}</v-avatar>
{{ tag.key }}
</v-chip>
</v-chip-group>
The problem I have is that in the typescript I do something like this:
refresh() {
// get simple array of tag strings
const selectedTags = this.selectedTag.map((value: any) => {
if (this.tags && this.tags[value]) {
return this.tags[value].key
} else {
return null
}
}).filter((value: any) => {
return value != null
})
Promise.all([
...
ApiCall('GET', 'tags', {limit: 1000, tags: selectedTags}),
...
).then((values) => {
// decode response from server into new tags
this.tags = values[2].series['0'].values.map((item: any) => {
return {key: item.bucket, count: item.doc_count}
})
const newTags: number[] = []
this.tags.forEach((tag, index) => {
// find the new index of the previously selected tags and save them
if (selectedTags.find(st => {
return st === tag.key
})) {
newTags.push(index)
}
})
// update selectedTag with the new value
this.$set(this, 'selectedTag', newTags)
// did not work this.selectedTag = newTags
})
}
What I'm seeing is that when I click a chip, it correctly fires the #change event and calls refresh, but then when the refresh finishes, I see an additional refresh get called with an empty selectedTag, which clears my filters and recalls the above functionality.
Is there a way to get #change to fire when a chip is changed, but not fire (or filter it out) when the event is generated by changing the data referenced by v-model?

Dynamically Tally Child Elements by Classname in Vue

I have a page that allows a user to drag/drop images into pre-defined DIVs, then I tally up the total value of the images based on their class name. What I am trying to do is get vue to read the values from each outer div.answer and get the class names of the child images.
My source code is:
<div
is="box-answers"
v-for="box in boxes.slice().reverse()"
v-bind:key="box.id"
v-bind:level="box.level"
v-bind:hint="box.hint"
></div>
<script>
Vue.component('box-answers', {
props: ['level','hint'],
template: '<div class="droppable answer :id="level" :title="hint"></div>'
});
new Vue({
el: '#mainapp',
data: {
boxes: [
{ id: 1, level: 'baselevel-1', hint: 'x 1' },
{ id: 2, level: 'baselevel-2', hint: 'x 20' },
{ id: 3, level: 'baselevel-3', hint: 'x 400' },
{ id: 4, level: 'baselevel-4', hint: 'x 8,000' },
{ id: 5, level: 'baselevel-5', hint: 'x 160,000' }
]
}
</script>
This converts to the follow HTML (the nested DIVs and SPANs are user-possible entries by dragging):
<div id="baselevel-5" class="droppable answer" title="x 160,000">
<div><img src="images/line.gif" alt="Five" class="imgfive"></div>
<span><img src="images/dot.gif" alt="One" class="imgone"></span>
</div>
...
<div id="baselevel-1" class="droppable answer" title="x 1">
<span><img src="images/line.gif" alt="One" class="imgone"></span>
</div>
Currently, I have jQuery/JavaScript calculating the point values using the following:
$(function(j) {
var arAnswers = Array(1);
count = 0; //
j("div.answer").each(function( idx ) {
currentId = j(this).attr('id');
ones = 0;
fives = 0;
if ( j("#" + currentId).children().length > 0 ) {
ones = j("#" + currentId).children().find("img.imgone").length * 1;
fives = j("#" + currentId).children().find("img.imgfive").length * 5;
arAnswers[count] = ones + fives; //Tally box value
count++;
}
});
});
I would like Vue to perform similar iteration and addition to return total value of ones and fives found based on the image classname.
Currently, you are approaching this problem as a pure-play DOM operation. If that is what you need then you can simply use $refs:
<!-- NOTICE ref -->
<div ref="boxAnswers"
is="box-answers"
v-for="box in boxes.slice().reverse()"
v-bind:key="box.id"
v-bind:level="box.level"
v-bind:hint="box.hint">
</div>
Inside your high-level component, you will have a function like:
function calculate() {
// NOTICE $refs
const arAnswers = this.$refs.boxAnswers.map((x) => {
// $el is the DOM element
const once = x.$el.querySelectorAll('img.imgone').length * 1;
const fives = x.$el.querySelectorAll('img.imgfive').length * 5;
return once + fives
});
return arAnswers;
}
But this is not the correct Vue way of doing things. You have to think in terms of events and data model (MVVM - don't touch DOM. DOM is just a representation of your data model). Since, you have a drag-n-drop based application, you have to listen for drag, dragstart, dragend and other drag events. For example:
<!-- NOTICE drop event -->
<div #drop="onDropEnd(box, $event)"
is="box-answers"
v-for="box in boxes.slice().reverse()"
v-bind:key="box.id"
v-bind:level="box.level"
v-bind:hint="box.hint">
</div>
Your onDropEnd event handler will look like:
function onDrop(box, $event) {
// box - on which box drop is happening
// $event.data - which image is being dropped
// Verify $event.data is actually the image you are intending
if ($event.data === 'some-type-image') {
// Do the counting manipulations here
// ... remaining code
}
}
This is not a complete code as I don't know other components. But it should help you with the required direction.

How to use the concatenate names of the elements/options in one function in vue-select

I have a project where I use many vue-select components.
My components:
...
<v-select ref="select_1" :options="options_1" #search:focus="maybeLoad('1')">
</v-select>
<v-select ref="select_3" :options="options_2" #search:focus="maybeLoad('2')">
</v-select>
<v-select ref="select_3" :options="options_3" #search:focus="maybeLoad('3')">
</v-select>
...
My method:
...
maybeLoad(name) {
// Vue.prototype.$options_select = 'options_' + name;
// const options_select = 'options_' + name;
return this.$options_select.length <= 0 ? this.load(name) : null
},
...
I was trying with Vue.prototype.$options_select or const options_select, but not working.
Error with Vue.prototype:
vue-select is empty.
Error with const options_select
TypeError: "this.$options_select is undefined; can't access its "length" property"
If I am using dedicated functions for every vue-select ... is working, every vue-select is filled with data from axios (with load() method).
Any ideas?
Found the solution:
...
maybeLoad(name) {
const options_select = 'options_' + name;
return this[options_select].length <= 0 ? this.load(name) : null
},
...

how can get index & count in vuejs

I have code like this (JSFiddle)
<li v-for="(itemObjKey, catalog) in catalogs">this index : {{itemObjKey}}</li>
Output:
this index: 0
this index: 1
My question is:
How can I get value index first begin: 1 for example I want
output like this: this index: 1 this index: 2
How can I get count from index, i.e. output like this: this index: 1 this index: 2 this count: 2 field
you can just add 1
<li v-for="(catalog, itemObjKey) in catalogs">this index : {{itemObjKey + 1}}</li>
to get the length of an array/objects
{{ catalogs.length }}
In case, your data is in the following structure, you get string as an index
items = {
am:"Amharic",
ar:"Arabic",
az:"Azerbaijani",
ba:"Bashkir",
be:"Belarusian"
}
In this case, you can use extra variable to get the index in number:
<ul>
<li v-for="(item, key, index) in items">
{{ item }} - {{ key }} - {{ index }}
</li>
</ul>
Source: https://alligator.io/vuejs/iterating-v-for/
Alternatively, you can just use,
<li v-for="catalog, key in catalogs">this is index {{++key}}</li>
This is working just fine.
The optional SECOND argument is the index, starting at 0. So to output the index and total length of an array called 'some_list':
<div>Total Length: {{some_list.length}}</div>
<div v-for="(each, i) in some_list">
{{i + 1}} : {{each}}
</div>
If instead of a list, you were looping through an object, then the second argument is key of the key/value pair. So for the object 'my_object':
var an_id = new Vue({
el: '#an_id',
data: {
my_object: {
one: 'valueA',
two: 'valueB'
}
}
})
The following would print out the key : value pairs. (you can name 'each' and 'i' whatever you want)
<div id="an_id">
<span v-for="(each, i) in my_object">
{{i}} : {{each}}<br/>
</span>
</div>
For more info on Vue list rendering: https://v2.vuejs.org/v2/guide/list.html
Using Vue 1.x, use the special variable $index like so:
<li v-for="catalog in catalogs">this index : {{$index + 1}}</li>
alternatively, you can specify an alias as a first argument for v-for directive like so:
<li v-for="(itemObjKey, catalog) in catalogs">
this index : {{itemObjKey + 1}}
</li>
See : Vue 1.x guide
Using Vue 2.x, v-for provides a second optional argument referencing the index of the current item, you can add 1 to it in your mustache template as seen before:
<li v-for="(catalog, itemObjKey) in catalogs">
this index : {{itemObjKey + 1}}
</li>
See: Vue 2.x guide
Eliminating the parentheses in the v-for syntax also works fine hence:
<li v-for="catalog, itemObjKey in catalogs">
this index : {{itemObjKey + 1}}
</li>
Hope that helps.
Why its printing 0,1,2...?
Because those are indexes of the items in array, and index always starts from 0 to array.length-1.
To print the item count instead of index, use index+1. Like this:
<li v-for="(catalog, index) in catalogs">this index : {{index + 1}}</li>
And to show the total count use array.length, Like this:
<p>Total Count: {{ catalogs.length }}</p>
As per DOC:
v-for also supports an optional second argument (not first) for
the index of the current item.
this might be a dirty code but i think it can suffice
<div v-for="(counter in counters">
{{ counter }}) {{ userlist[counter-1].name }}
</div>
on your script add this one
data(){return {userlist: [],user_id: '',counters: 0,edit: false,}},

Check if value exists in vuejs

I have data : 1,2,3,4,4,5 & my code like this:
<div id="looping" v-for="display in editlistAssesments">
{{display.test_id}}
</div>
My code if in php such as like this
$temp_id = array();
foreach($data as $data){
if(in_array($data ->test_id,$temp_id)){
echo" 1: no";
echo" 2: no";
echo" 3: no";
echo" 4: yes"; //because he have same value
echo" 5: no";
$temp_id[] = $data ->test_id;
}
}
how I can do that in loop vueJs..??
From my point of view, the best way is:
<div id="looping"
v-for="display in editlistAssesments">
<span v-if="typeof display.test_id !== 'undefined'">
{{display.test_id}}
</span>
</div>
Because if you use v-if="display.test_id" and the test_id value is 0 (boolean comparation) you will never see the display.text_id.
You can use also this another condition to check if is null or undefined: display.test_id != null (loose equality operator)
As far as I understand, you want to check if value is in array and then render it accordingly?
If so, you need a Vue custom filter. Something like this will do the trick:
var vm = new Vue({
el: 'body',
data: {
editlistAssesments: [1,2,3,4,4,5]
},
filters: {
ifInArray: function (value) {
return this.editlistAssesments.indexOf(value) > -1 ? 'Yes' : 'No';
}
},
});
And then use it like this:
<div id="looping" v-for="display in editlistAssesments">
<span v-text="display.test_id | ifInArray"></span>
<!-- bind Vue value to html element is better practice -->
</div>
Check docs for more information:
http://vuejs.org/guide/custom-filter.html