My conclusion is that you cannot add 2 formatters to the same column with DT
, although of course I could be wrong.
Note that, even this is not explicitly stated in the documentation of DT
, only one formatter can be added per column table. Note also that on the examples in the link you provide, or when you type ?formatCurrency
, when they include two pipes %>%
, they always affect to two different columns.
In your example, when you do
datatable(m) %>% formatRound("A", digits=1) %>% formatCurrency("A", currency="£")
the result is round for 1 digit with no currency, and if you do
datatable(m) %>% formatCurrency("A", currency="£") %>% formatRound("A", digits=1)
the result is the currency added with no rounding.
My knowledge on how R integrates with js is very limited, but looking at the R sources for the package in cran, it looks like every format command in the pipe appends a formatter, but for some reason only one formatter comes into play:
formatCurrency = function(table, columns, currency = '$', interval = 3, mark = ',') {
formatColumns(table, columns, tplCurrency, currency, interval, mark)
}
formatRound = function(table, columns, digits = 2) {
formatColumns(table, columns, tplRound, digits)
}
formatColumns = function(table, columns, template, ...) {
...
x$options$rowCallback = appendFormatter(
x$options$rowCallback, columns, colnames, rownames, template, ...
)
...
}
appendFormatter = function(js, name, names, rownames = TRUE, template, ...) {
...
JS(append(
js, after = 1,
template(i, ...)
))
}
where every formatter ends up calling formatColumns
with a different template
, and i
resolves an id for the column. As I said, I have no idea if this is because the append operations overwrites the formatter, or if it has to do with the execution.
EDIT: Sorry I accidentally pressed the post button and got interruped. I actually implemented a formatter that takes more arguments. The solution is a bit convoluted but it works. This is a formatter that accepts both currency and digits:
tplRound2 = function(cols, currency, digits) {
sprintf(
"var d = parseFloat(data[%d]); $(this.api().cell(row, %s).node()).html(isNaN(d) ? '' : '%s' + d.toFixed(%d).toString());",
cols, cols, currency, digits
)
}
You need to add all these functions to your session:
formatRound2 = function(table, columns, currency, digits = 2) {
formatColumns2(table, columns, tplRound2, currency, digits)
}
formatColumns2 = function(table, columns, template, ...) {
if (inherits(columns, 'formula')) columns = all.vars(columns)
x = table$x
colnames = base::attr(x, 'colnames', exact = TRUE)
rownames = base::attr(x, 'rownames', exact = TRUE)
x$options$rowCallback = appendFormatter2(
x$options$rowCallback, columns, colnames, rownames, template, ...
)
table$x = x
table
}
name2int = function(name, names) {
if (is.numeric(name)) {
return(if (all(name > 0)) name else seq_along(names)[name])
}
names = setNames(seq_along(names), names)
unname(names[name])
}
appendFormatter2 = function(js, name, names, rownames = TRUE, template, ...) {
js = if (length(js) == 0) c('function(row, data) {', '}') else {
unlist(strsplit(as.character(js), '\n'))
}
i = name2int(name, names)
if (is.character(name) || (is.numeric(name) && !rownames)) i = i - 1
if (any(is.na(i))) stop(
'You specified the columns: ', paste(name, collapse = ', '), ', ',
'but the column names of the data are ', paste(names, collapse = ', ')
)
JS(append(
js, after = 1,
template(i, ...)
))
}
And then you can run with the new formatter to get the desired result:
datatable(m) %>% formatRound2("A", "£", digits=1)
(However this does not add the ,
every 3 digits, if you really need it I could add it to the formatter...)
EDIT2 after comments:
This would be the formatter function for using both currency and number of digits, plus the ',' marks:
tplRound3 = function(cols, currency, digits, interval, mark) {
sprintf(
"var d = parseFloat(data[%d]); $(this.api().cell(row, %s).node()).html(isNaN(d) ? '' : '%s' + d.toFixed(%d).toString().replace(/\\B(?=(\\d{%d})+(?!\\d))/g, '%s'));",
cols, cols, currency, digits, interval, mark
)
}
formatRound3 = function(table, columns, currency, digits = 2, interval=3, mark=',') {
formatColumns2(table, columns, tplRound3, currency, digits, interval, mark)
}
In order to use it just type
datatable(m) %>% formatRound3("A", "£", digits=1)
round
first on that column and then feed todatatable
? – Opinion