How to increment property in loop Vue 2? - for-loop

Template : have function in curly braces setSr() for increment previous property
<ul v-for="i in 5" :key="i">
<li>{{setSr()}} {{i}} {{ preivous }} </li>
</ul>
data property previous initial 1
data(){
return{
preivous:1,
}
}
Method
methods:{
setSr(){
this.preivous =(this.preivous+1);
}
}
Output
1 2022 2 2023 3 2024 4 2025 5 2026
Expected output next want increment previous by condition wise
1 0
2 1
3 2
4 3
5 4

Avoid on-the-fly stuff like this in the template loop and do stuff like this and restructure what's needed before you output.
For example, make a range method to compute your range, if your use it ever again, abstract it into a mixin etc, and then use a computed value to call it, setting a default in data.
const utilsMixin = {
methods: {
range(start, end, step) {
// ascii a-z
if (typeof start === 'string' && typeof end === 'string') {
const result = []
for (let idx = start.charCodeAt(0), e = end.charCodeAt(0); idx <= e; ++idx) {
result.push(String.fromCharCode(idx))
}
return result
}
// number or date
const range = []
typeof step === 'undefined' && (step = 1)
if (end < start) {
step = -step
}
while (step > 0 ? end >= start : end <= start) {
range.push(start)
start += step
}
return range
}
}
}
var app = new Vue({
el: '#app',
mixins: [
utilsMixin
],
data: () => ({
limit: 5
}),
computed: {
years() {
return this.range(new Date().getFullYear(), new Date().getFullYear() + this.limit)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="(year, i) in years" :key="i">{{i}} {{ year }} </li>
</ul>
</div>

Related

How not to get NAN error in x-text of alpinejs

Here I want to get the value of the gap property, but it shows the NAN error!
<div x-data="tamrin()">
<h1 x-text="gap"></h1>
<script>
function tamrin() {
return {
countTo: new Date("May 30, 2021 00:00:00").getTime(),
now: new Date().getTime(),
gap: this.countTo - this.now,
}
}
</script>
</div>
You are returning an object literal, so the this there refers to the global window, not to that object literal.
One option would be to first create the countTo and now variables and then the object with the gap property:
window.MyComponent = function() {
console.log(`this === window = ${ this === window }`);
const countTo = new Date('May 30, 2021 00:00:00').getTime();
const now = new Date().getTime();
return {
countTo,
now,
gap: countTo - now,
};
};
<div x-data="MyComponent()">
<span x-text="gap"></span>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
Another option would be to use a getter and turn gap into a computed property so that this inside that getter will refer to your component instance:
window.MyComponent = () => ({
countTo: new Date('May 30, 2021 00:00:00').getTime(),
now: new Date().getTime(),
get gap() {
console.log(`this === window = ${ this === window }`);
return this.countTo - this.now;
},
});
<div x-data="MyComponent()">
<span x-text="gap"></span>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>
Edit:
If you want to continuously refresh the displayed value, use the second solution (getter) and update the now property using setInterval:
window.MyComponent = () => ({
countTo: new Date('May 30, 2021 00:00:00').getTime(),
now: Date.now(),
init() {
setInterval(() => {
requestAnimationFrame(() => {
this.now = Date.now();
});
}, 200);
},
get gap() {
return `${ Math.round(this.countTo - this.now / 1000) } seconds left.`;
},
});
<div x-data="MyComponent()" x-init="init">
<span x-text="gap"></span>
</div>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine#v2.x.x/dist/alpine.min.js"></script>

How to setState of array of Object value and toggle the count to either 1 or 2 on click

I want to update the state of value in an array of objects on click, the value will toggle between 1 and 2, once clicked if the existing value is 1 it will update it 2 on click, and if 2 it will update it 1. The value must change for the clicked object only and not all objects.
import React, {useRef, useState} from 'react'
import {BsThreeDots, BsBookmark, BsBookmarkFill} from 'react-icons/bs'
export const TextQuoteCard = () => {
const [textQuote, setTextQuote] = useState([
{
userId: '123',
userName: 'sample',
userImageUrl: 'https://qph.fs.quoracdn.net/main-thumb-ti-406-100-gtkoukgmmuzegaytlmtaybgrsoihzyii.jpeg',
quoteId: 'TQ119',
postDateTime: '28 June at 8:20',
quoteAuthorId: '123',
quoteAuthorName: 'john len',
quoteCategory: 'Motivational',
quoteType: 'textQuote',
quoteText: 'If there’s no market, then it may not be the best thing to do. Entrepreneurship is about finding market opportunities, or creating opportunities. If there’s no market, then you need to grow one',
quoteImageUrl: '',
// 1 = yes, 2 = no
bookmarkStatus: 1,
likesCount: 3300,
commentsCount: 123,
overallShareCount: 1203,
fbShareCount: 423,
twtShareCount: 1232,
waShareCount: 1023,
viewCount: 1923,
isSelected: null
}
])
const handleBookmark = i => {
let bookmarkStatus = [...textQuote]
let bookmark = bookmarkStatus[i].bookmarkStatus
console.log('before update' , bookmark)
if(bookmark === 1) {
bookmark = 2
} else if(bookmark === 2){
bookmark = 1
}
setTextQuote(bookmarkStatus)
console.log('after update', bookmark)
}
return(
<div>
{
textQuote.map((quote, index) => (
<div className="QuoteCardPrimaryContainer" key={quote.quoteId}>
className="QuoteCardAuthorFollowButtonActionContainer">
<span className="QuoteCardAuthorFollowButtonActionSpan"
onClick={() => handleBookmark(index)}>
<span className={quote.bookmarkStatus === 1 ?
'bookmarkButtonContainer activeBookmark':
'bookmarkButtonContainer'}>
{quote.bookmarkStatus === 1 ? <BsBookmarkFill/> :
<BsBookmark/>}
</span>
</span>
</div>
))
}
</div>
)
}
First get the object at that index where the bookmarkStatus has to be updated. Then using splice method you can replace with the updated object.
const handleBookmark = i => {
let quoteObj = {...textQuote[i]};
let bookmark = quoteObj.bookmarkStatus;
console.log('before update', bookmark);
if (bookmark === 1) {
quoteObj.bookmarkStatus = 2;
} else if (bookmark === 2) {
quoteObj.bookmarkStatus = 1;
}
textQuote.splice(i, 1, quoteObj)
console.log(textQuote);
setTextQuote([...textQuote]);
console.log('after update', textQuote[i].bookmarkStatus);
};
Hope this helps.

(Vue) Impact on performance of local scope variables in computed properties

Does defining variables inside of a computed property have any impact on the perfomance of Vue components?
Background: I built a table component which generates a HTML table generically from the passed data and has different filters per column, filter for the whole table, sort keys, etc., so I'm defining a lot of local variables inside the computed property.
Imagine having an array of objects:
let data = [
{ id: "y", a: 1, b: 2, c: 3 },
{ id: "z", a: 11, b: 22, c: 33 }
]
..which is used by a Vue component to display the data:
<template>
<div>
<input type="text" v-model="filterKey" />
</div>
<table>
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
<tbody>
<tr v-for="item in filteredData" :key="item.id">
<td v-for="(value, key) in item" :key="key">
{{ value }}
</td>
</tr>
</tbody>
</table>
</template>
The data gets filtered via input:
<script>
export default {
props: {
passedData: Array,
},
data() {
return {
filterKey: null,
};
},
computed: {
filteredData() {
// defining local scope variables
let data = this.passedData;
let filterKey = this.filterKey;
data = data.filter((e) => {
// filter by filterKey or this.filterKey
});
return data;
},
},
};
</script>
My question refers to let data = .. and let filterKey = .. as filteredData() gets triggered from any change of the filterKey (defined in data()) so the local variable gets updated too, although they're not "reactive" in a Vue way.
Is there any impact on the performance when defining local variables inside a computed property? Should you use the reactive variables from data() (e. g. this.filterKey) directly inside of the computed property?
The best way to test if something affects performance, is to actually test it.
According to my tests below, it is consistency more than 1000% slower to use this.passedData instead of adding a variable on top of the function. (869ms vs 29ms)
Make sure you run your benchmarks on the target browsers you write your application for the best results.
function time(name, cb) {
var t0 = performance.now();
const res = cb();
if(res !== 20000000) {
throw new Error('wrong result: ' + res);
}
var t1 = performance.now();
document.write("Call to "+name+" took " + (t1 - t0) + " milliseconds.<br>")
}
function withoutLocalVar() {
const vue = new Vue({
computed: {
hi() {
return 1;
},
hi2() {
return 1;
},
test() {
let sum = 0;
for(let i = 0; i < 10000000; i++) { // 10 000 000
sum += this.hi + this.hi2;
}
return sum;
},
}
})
return vue.test;
}
function withLocalVar() {
const vue = new Vue({
computed: {
hi() {
return 1;
},
hi2() {
return 1;
},
test() {
let sum = 0;
const hi = this.hi;
const hi2 = this.hi2;
for(let i = 0; i < 10000000; i++) { // 10 000 000
sum += hi + hi2;
}
return sum;
},
}
})
return vue.test;
}
function benchmark() {
const vue = new Vue({
computed: {
hi() {
return 1;
},
hi2() {
return 1;
},
test() {
let sum = 0;
const hi = 1;
const hi2 = 1;
for(let i = 0; i < 10000000; i++) { // 10 000 000
sum += hi + hi2;
}
return sum;
},
}
})
return vue.test;
}
time('withoutLocalVar - init', withoutLocalVar);
time('withLocalVar - init', withLocalVar);
time('benchmark - init', benchmark);
time('withoutLocalVar - run1', withoutLocalVar);
time('withLocalVar - run1', withLocalVar);
time('benchmark - run1', benchmark);
time('withoutLocalVar - run2', withoutLocalVar);
time('withLocalVar - run2', withLocalVar);
time('benchmark - run2', benchmark);
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>

VueJS: how can i use two computed properties inside one v-for?

I have this computed property:
computed: {
filteredCars: function() {
var self = this
return self.carros.filter(function(carro) {
return carro.nome.indexOf(self.busca) !== -1
})
},
},
and i'm using v-for like this:
<tr v-for="carro in filteredCars">
<td>{{carro.nome}}</td>
<td>{{carro.marca}}</td>
<td>{{carro.categoria}}</td>
<td>{{carro.motor}}</td>
<td>{{carro.cambio}}</td>
<td>{{carro.preco}}</td>
</tr>
but I need to create another computed property to limit my data quantity, how i call it inside the same v-for?
I'm trying to use filteredCars + another filter, in this case something like 'limit' filter from vue 1.x. I've done an example using Vue 1.x but i need to do using Vue 2.x.
Vue.filter('limit', function (value, amount) {
return value.filter(function(val, index, arr){
return index < amount;
});
<tr v-for="carro in carros | limit upperLimit>
...
</tr>
Just use Array.prototype.slice (Array.prototype.splice should work too) in the computed property.
data: {
carros: [...],
upperLimit: 30
},
computed: {
filteredCars: function() {
const arr = this.carros.filter(function(carro) {
return carro.nome.indexOf(self.busca) !== -1
});
if (arr.length > this.upperLimit) {
return arr.slice(0, this.upperLimit + 1);
}
return arr;
},
}

dropdown list with an extend option

I'm using the DropdownList component from react-widget. In my code, there are a couple of dropdowns that get their values from an Ajax call. Some of them, like a list of languages, are too big and it's very slow to get the list from Ajax and render it (takes 4 to 5 seconds!). I would like to provide to the dropdwon a small list of languages and an 'Extend' or 'Load Full List' option; if clicking on Extend the dropdown would be refreshed with the full list of languages.
Here is my solution: the code of the parent component:
const languages = ajaxCalls.getLanguages();
const langs = {"languages": [["eng", "English"], ["swe", "Swedish"], ["deu", "German"], ["...", "Load Full List"]]};
const common_langs = langs.languages.map(([id, name]) => ({id, name}));
<SelectBig data={common_langs} extend={languages} onSelect={x=>this.setValue(schema, path, x)} value={this.getValue(path)} />;
And here is the code for SelectBig component:
import React from 'react/lib/ReactWithAddons';
import { DropdownList } from 'react-widgets';
const PT = React.PropTypes;
export const SelectBig = React.createClass({
propTypes: {
data: PT.array,
value: PT.string,
onSelect: PT.func.isRequired,
},
maxResults: 50,
shouldComponentUpdate(nextProps, nextState) {
console.log("nextProps = " , nextProps, " , nextState = ", nextState);
const len = x => (x && x.length !== undefined) ? x.length : 0;
// fast check, not exact, but should work for our use case
return nextProps.value !== this.props.value
|| len(nextProps.data) !== len(this.props.data);
},
getInitialState(){
return {
lastSearch: '',
results: 0,
dataList: [],
};
},
select(val) {
if(val.id === "..."){
this.setState({dataList: this.props.extend})
}
this.props.onSelect(val.id);
},
filter(item, search) { .... },
renderField(item) { .... },
render() {
const busy = !this.props.data;
let data;
if(!this.props.extend){
data = this.props.data || [];
} else {
data = this.state.dataList;
}
return (
<DropdownList
data={data}
valueField='id'
textField={this.renderField}
value={this.props.value}
onChange={this.select}
filter={this.filter}
caseSensitive={false}
minLength={2}
busy={busy} />
);
}
});
But it doesn't look good: When the user chooses 'Load Full List', the dropdown list will be closed and user need to click again to see the updated list. Does anyone have a better solution or a suggestion to improve my code?
The picture shows how it looks like right now!
https://www.npmjs.com/package/react-select-2
Better go with this link, it will work

Resources