Convert the amount to 2 decimals - nunjucks

I am using nunjucks template:
<td class="alignright">{{ item.amount / 100 }}</td>
Using 10050 / 100, I am getting 100.5, I want to have it like 100.50.
Question:
How do I convert the amount to 2 decimals, after divided by 100?

env = nunjucks.configure( ... );
...
env.addFilter('fixed', function(num, length) {
return num.toFixed(length || 2);
});
<td class="alignright">{{ item.amount / 100 | fixed }}</td> <= need parenthesis!
Worked example
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
env.addFilter('fixed', function(num, length) {
return num.toFixed(2 || length);
});
env.addGlobal('fixed', function(num, length) {
return num.toFixed(2 || length);
})
var html = env.renderString(
'Filter: {{ (totalAmt / 100) | fixed }}, GlobalFunc: {{ fixed(totalAmt / 100) }}',
{ totalAmt: 500030 });
console.log(html);

Related

How to increment property in loop Vue 2?

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>

Can I use #for loop in emotion-js, similarly to #for in sass?

In sass if I write:
#for $i from 1 through 3
li:nth-child(#{$i})
transition-delay: #{$i * 0.3}s
, I can get a nice progressive transition delay for each list element.
Is it possible to do this with emotion-js ?
Okay I have figured it.
First I create a JS function, which does my loop and then returns the styles as an object
const menuListTrans = () => {
let styles = {};
for (let $i = 0; $i < 10; $i++) {
styles["&:nth-child(" + $i + ")"] = {
transitionDelay: "1s," + $i * 0.08 + "s",
};
}
return styles;
};
and then interpolate it in the styled component
const MenuList = styled.ul`
&.expanded > li {
transform: translateY(0);
${menuListTrans}
}
`;
here is the same approach but with variables.
export const nthChildDelay = ({ count = 10, delay = 100, multiplier = 80 }) => {
const styles = {};
[...Array(count).keys()].forEach((_, index) => {
if (index !== 0) {
styles[`&:nth-child(${index})`] = {
transitionDelay: `${delay + (index - 1) * multiplier}ms`,
};
}
});
return styles;
};
And then use it as
${nthChildDelay({ count: 10, delay: 100, multiplier: 100 })};

How to show refreshed value from an array

My problem is in showing the values ​​of the object.
The table shows the tasks and the remaining time to finish. But it does not update the countdown timer ...
The values ​​are in an Object in Task ID, for example:
Object {1: "4017 D 13 H 2 M 49 S", 2: "0 D 0 H 0 M 0 S", ...}
This object should always be updated to show the remaining time, but in the table where I show the object the same is only with the first value, without updating the timer.
<template>
...
<table class="table" style="text-align: center">
<thead>
<tr>
<th scope="col">...</th>
<th scope="col">...</th>
<th scope="col">...</th>
<th scope="col">...</th>
<th scope="col">Time Left</th>
</tr>
</thead>
<tbody>
<tr class="table-warning" v-for="task in tasks" :key="task.id">
<th scope="row">{{task.id}}</th>
<th>{{task.description}}</th>
<th>{{task.created_at}}</th>
<th>{{task.redline}}</th>
<th>{{showLeft[task.id]}}</th>
</tbody>
</table>
...
</template>
<script>
export default {
// variables declaration
data() {
return {
user: [],
tasks: [],
numTasks: "",
currentTime: [],
showLeft: {}
};
},
created() {
// ProgressBar animation starts
this.$Progress.start();
// Get data from server
axios.get("/user/name/data").then(response => {
this.user = response.data;
axios.get(`/user/missingTaskNum/data`).then(response => {
this.numTasks = response.data;
axios.get(`/user/missingTask/data`).then(response => {
this.tasks = response.data;
// Call function that will calculate time left
this.updateCurrentTime();
// ProgressBar animation stops
this.$Progress.finish();
});
});
});
},
mounted() {
// start timer to refresh function every 1 sec
this.interval = setInterval(() => {
this.updateCurrentTime();
}, 1000);
},
methods: {
updateCurrentTime: function() {
var now = new Date().getTime();
for (let i = 0; i < this.tasks.length; i++) {
this.currentTime[this.tasks[i].id] = new Date(this.tasks[i].redline) - now;
if (this.currentTime[this.tasks[i].id] < 0) {
this.$Progress.start();
this.$Progress.finish();
console.log("EXPIROU ID: " + this.tasks[i].id);
} else {
var days = Math.floor(
this.currentTime[this.tasks[i].id] / (1000 * 60 * 60 * 24)
);
var hours = Math.floor(
(this.currentTime[this.tasks[i].id] % (1000 * 60 * 60 * 24)) /
(1000 * 60 * 60)
);
var minutes = Math.floor(
(this.currentTime[this.tasks[i].id] % (1000 * 60 * 60)) /
(1000 * 60)
);
var seconds = Math.floor(
(this.currentTime[this.tasks[i].id] % (1000 * 60)) / 1000
);
this.showLeft[this.tasks[i].id] =
days + " D " + hours + " H " + minutes + " M " + seconds + " S ";
}
}
console.log(this.showLeft);
}
},
beforeDestroy() {
clearInterval(this.interval);
}
};
</script>
You're running into one of the change detection caveats, outlined here:
https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
Specifically on this line:
this.showLeft[this.tasks[i].id] =
days + " D " + hours + " H " + minutes + " M " + seconds + " S ";
The object this.showLeft is empty when it is created within data, so it won't have any reactive properties. It works fine if you change it to this:
this.$set(
this.showLeft,
this.tasks[i].id,
days + " D " + hours + " H " + minutes + " M " + seconds + " S "
)
This tells Vue to add a reactive property to showLeft, such that the UI will update when it changes.
An alternative would be to create a new object each time:
// Somewhere near the top of updateCurrentTime
const showLeft = {};
// ... set values on showLeft instead of this.showLeft
this.showLeft = showLeft
Personally I think I would make greater use of computed properties for this, rather than trying to do everything inside updateCurrentTime. I can't be more specific given the code is incomplete.

(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>

how to add multiple column sum total in datatable

I have issue in showing sum total of some columns in data table. One issue is my table is dynamically created i.e user select columns to show so that column index are not fixed!
Secondly footerCallback on adding code throws ncaught TypeError: Cannot read property 'nTf' of undefined error
<tfoot>
<tr>
<td colspan='2'> <span style="float:right;"id ='totalcol1'></span> </td>
</tr>
</tfoot>
FooterCAllback is defined inside var table=('#mytable').Datatable()
"footerCallback": function ( row, data, start, end, display ) {
var api = this.api(), data;
$(api.column(11).footer()).html(
api.column(11).data().reduce( function ( a, b ) { return a + b;})
);
/* For second column
$(api.column(12).footer()).append(
api.column(12).data().reduce( function ( a, b ) { return a + b;})
);*/
},
I found an example that looks like exactly what you want to do
$(document).ready(function() {
$('#example').DataTable( {
"footerCallback": function ( row, data, start, end, display ) {
var api = this.api(), data;
// Remove the formatting to get integer data for summation
var intVal = function ( i ) {
return typeof i === 'string' ?
i.replace(/[\$,]/g, '')*1 :
typeof i === 'number' ?
i : 0;
};
// Total over all pages
total = api
.column( 4 )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
// Total over this page
pageTotal = api
.column( 4, { page: 'current'} )
.data()
.reduce( function (a, b) {
return intVal(a) + intVal(b);
}, 0 );
// Update footer
$( api.column( 4 ).footer() ).html(
'$'+pageTotal +' ( $'+ total +' total)'
);
}
} );
} );

Resources