Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
262 views
in Technique[技术] by (71.8m points)

javascript - Store column total and using that value to update the same column R Shiny+DT

I am working on a Shiny app that uses the DT package and some JavaScript to create a nice nested table. In the child table the user is allowed to edit the 2nd column (Mix (%)). Upon editing that column the 6th column (GRPs/TRPs) will also update. When updating the mix (%) I am attempting to store the total GRPs/TRPs (or points) then use that total to update the GRPs/TRPs column. See the equation below. However, the total points does not update correctly when editing the mix %.

The main issue is in CosterTableJS in the footerCallback

totalGRPs/TRPs = sum(GRPs/TRPs column)

GRPs/TRPs = (mix%/100)*totalGRPs/TRPs

The below will also use https://github.com/ejbeaty/CellEdit (MIT License)

# parent table 
parent_table  <- data.frame(structure(list(Market = "ABILENE-SWEETWATER", `Total GRPs/TRPs` = 500L, 
    Cost = 82500, Impressions = 877215, Population = 175443), .Names = c("Market", 
"Total GRPs/TRPs", "Cost", "Impressions", "Population"), row.names = c(NA, 
-1L), class = "data.frame"))


# child table
child_table <- structure(list(Market = c("ABILENE-SWEETWATER", "ABILENE-SWEETWATER", 
"ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", 
"ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", 
"ABILENE-SWEETWATER"), Daypart = c("Early Morning", "Daytime", 
"Early Fringe", "Early News", "Prime Access", "Prime Time", "Late News", 
"Late Fringe", "Non-Linear"), `Mix (%)` = c(10, 10, 10, 10, 10, 
10, 10, 10, 20), Week = c(1, 1, 1, 1, 1, 1, 1, 1, 1), Weeks = c(5L, 
5L, 5L, 5L, 5L, 5L, 5L, 5L, 5L), `Override CPP` = c(16, 18, 17, 
24, 27, 55, 43, 22, 58), `GRPs/TRPs` = c(10, 10, 10, 10, 10, 
10, 10, 10, 20), Cost = c(160, 180, 170, 240, 270, 550, 430, 
220, 1160), Impressions = c(17544.3, 17544.3, 17544.3, 17544.3, 
17544.3, 17544.3, 17544.3, 17544.3, 35088.6), Population = c(175443, 
175443, 175443, 175443, 175443, 175443, 175443, 175443, 175443
), `Full Flight Cost` = c(800, 900, 850, 1200, 1350, 2750, 2150, 
1100, 5800), `Full Flight GRPs/TRPs` = c(50, 50, 50, 50, 50, 
50, 50, 50, 100), `Full Flight Impressions` = c(87721.5, 87721.5, 
87721.5, 87721.5, 87721.5, 87721.5, 87721.5, 87721.5, 175443)), .Names = c("Market", 
"Daypart", "Mix (%)", "Week", "Weeks", "Override CPP", "GRPs/TRPs", 
"Cost", "Impressions", "Population", "Full Flight Cost", "Full Flight GRPs/TRPs", 
"Full Flight Impressions"), row.names = c(NA, -9L), class = "data.frame")


# Module to create the nested structure of the table
NestedData <- function(dat, children) {
  stopifnot(length(children) == nrow(dat))
  g <- function(d){
    if(is.data.frame(d)){
      purrr::transpose(d)
    }else{
      purrr::transpose(NestedData(d[[1]], children = d$children))
    }
  }
  subdats <- lapply(children, g)
  oplus <- sapply(subdats, function(x) if(length(x)) "<img src='https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_open.png'/>" else "")
  cbind(" " = oplus, dat, "_details" = I(subdats), stringsAsFactors = FALSE)
}


table <- NestedData(
        dat = parent_table,
        children = split(child_table, child_table$Market)
)
# Whether to show row names (set TRUE or FALSE)
rowNames <- FALSE
colIdx <- as.integer(rowNames)
parentRows <- which(table[,1] != "")
# Some JS for the table
coster_callback_js <- CosterTableJS(
  parentRows,
  colIdx
)
# Table
dtable <- datatable(
  table,
  callback = coster_callback_js,
  rownames = rowNames,
  escape = -colIdx-1,
  style = "bootstrap4",
  extensions = 'Buttons',
    options = list(
      dom = "Bt",
      columnDefs = list(
        list(width = '30px', targets = 0),
        list(width = '300px', targets = 1),
        list(width = '80px', targets = 3),
        list(visible = FALSE, targets = c(2,3,4,5)),
        list(visible = FALSE, targets = ncol(table)-1+colIdx),
        list(orderable = FALSE, className = 'details-control', targets = colIdx),
        list(className = "dt-center", targets = "_all")
    )
  )
)
# Call the html tools deps (js & css files in this directory)
path <- "/Users/timmcwilliams/Desktop" # folder containing the file dataTables.cellEdit.js
dep <- htmltools::htmlDependency(
  "CellEdit", "1.0.19", 
  path, script = "dataTables.cellEdit.js")
dtable$dependencies <- c(dtable$dependencies, list(dep))

dtable

CosterTableJS <- function(parentRows, colIdx) {
  return( 
    JS(
        "function onUpdate(updatedCell, updatedRow, oldValue) {",
        "}",
        "function renderInt(data, type, row) {",
        "  if(type === 'display') {",
        "    return parseFloat(data).toFixed(0);",
        "  } else {",
        "    return data;",
        "  }",
        "}",
        "function renderDollars(data, type, row) {",
        "  if(type === 'display') {",
        "    return '$' + parseFloat(data).toFixed(2);",
        "  } else {",
        "    return data;",
        "  }",
        "}",
        sprintf("var parentRows = [%s];", toString(parentRows-1)),
        sprintf("var j0 = %d;", colIdx),
        "var nrows = table.rows().count();",
        "for(var i=0; i < nrows; ++i){",
        "  if(parentRows.indexOf(i) > -1){",
        "    table.cell(i,j0).nodes().to$().css({cursor: 'pointer'});",
        "  }else{",
        "    table.cell(i,j0).nodes().to$().removeClass('details-control');",
        "  }",
        "}",
        "",
        "// make the table header of the nested table",
        "var format = function(d, childId){",
        "  if(d != null){",
        "    var html = ",
        "      '<table class="display compact hover" ' + ",
        "      'style="padding-left: 30px;" id="' + childId + '"><thead><tr>';",
        "    for(var key in d[d.length-1][0]){",
        "      html += '<th>' + key + '</th>';",
        "    }",
        "    html += '</tr></thead><tfoot><tr>'",
        "    for(var key in d[d.length-1][0]){",
        "      html += '<th></th>';",
        "    }",
        "    return html + '</tr></tfoot></table>';",
        "  } else {",
        "    return '';",
        "  }",
        "};",
        "",
        "// row callback to style the rows of the child tables",
        "var rowCallback = function(row, dat, displayNum, index){",
        "  if($(row).hasClass('odd')){",
        "    $(row).css('background-color', 'white');",
        "    $(row).hover(function(){",
        "      $(this).css('background-color', 'lightgreen');",
        "    }, function() {",
        "      $(this).css('background-color', 'white');",
        "    });",
        "  } else {",
        "    $(row).css('background-color', 'white');",
        "    $(row).hover(function(){",
        "      $(this).css('background-color', 'lightblue');",
        "    }, function() {",
        "      $(this).css('background-color', 'white');",
        "    });",
        "  }",
        "};",
        "",
        "// header callback to style the header of the child tables",
        "var headerCallback = function(thead, data, start, end, display){",
        "  $('th', thead).css({",
        "    'color': 'black',",
        "    'background-color': 'white'",
        "  });",
        "};",
        "",
        "// make the datatable",
        "var format_datatable = function(d, childId, rowIdx){",
        "  // footer callback to display the totals",
        "  var footerCallback = function(tfoot, data, start, end, display){",
        "    $('th', tfoot).css('background-color', '#F5F2F2');",
        "    var api = this.api();",
        "    var cpp = api.column(5).data();",
        "    var mix = api.column(2).data();",
        "    var points = api.column(6).data();",
        "    var population = api.column(9).data();",
        
        "    // HERE",
        "    // Grab the sum of the points to use later on",
        "    var pointsSumTotal = 0;",
        "    for(var i = 0; i < points.length; i++) {",
        "      pointsSumTotal += (parseFloat(points[i]));",
        "    }",
        "    console.log('points', pointsSumTotal)",
        
        "    // Update the Point column when the Mix column is edited",
        "    for (var z = 0; z < mix.length; z++) {",
        "      api.cell(z,6).data((mix[z]/100)*pointsSumTotal);",
        "    }",
        "",
        "    // Update the Cost and Impressions with the new Points values",
        "    var points = api.column(6).data();",
        "    for (var i = 0; i < points.length; i++) {",
        "      // The cost",
        "      api.cell(i,7).data(parseFloat(points[i]).toFixed(0)*parseFloat(cpp[i]));",
        "      // The Imps",
        "      api.cell(i,8).data((((parseFloat(points[i]).toFixed(0)/100)*parseFloat(population[i]))));",
        "    }",
        "    // Update the full flight cost, points, and imps",
        "    var weeks = api.column(4).data();",
        "    var cost = api.column(7).data();",
        "    var imps = api.column(8).data();",
        "    for (var i = 0; i < weeks.length; i++) {",
        "      // The full flight cost",
        "      api.cell(i,10).data(parseFloat(weeks[i])*parseFloat(cost[i]));",
        "      // The full flight points",
        "      api.cell(i,11).data(parseFloat(weeks[i])*parseFloat(points[i]).toFixed(0));",
        "      // The full flight imps",
        "      api.cell(i,12).data(parseFloat(weeks[i])*parseFloat(imps[i]).toFixed(0));",
        "    }",

        "// Make the footer sums",
        "    api.columns().eq(0).each(function(index){",
        "      if(index == 1) return $(api.column(index).footer()).html('Totals');",
        "      var coldata = api.column(index).data();",
        "      var total = coldata",
        "          .reduce(function(a, b){return parseInt(a) + parseInt(b)}, 0);",
        "   

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...