Имеет любимый пользовательский тег Grails?

Предполагая, что res является просто вектором, вы можете просто объединить ваши данные и преобразовать их в широкоформатную матрицу, а затем построить график с помощью чего-то вроде пакета lattice

prob <- seq(0,1,by=0.01)
n <- seq(999,9999, by = 1000)
n <- c(9,99,n)
combis <- expand.grid(prob,n)

res <- runif(n=nrow(combis), 0, 0.67) #generate sample data for res
dat <- cbind(combis, res)

library(reshape2)
datm <- acast(data = dat, Var1~Var2, value.var = "res") #cast it into wide format

library(lattice)
library(latticeExtra)

cloud(datm, panel.3d.cloud = panel.3dbars, xlab="n", ylab="res", zlab="prob")

[113 ] 3d plot

6
задан 20 October 2008 в 03:16
поделиться

3 ответа

Я считаю класс DecimalFormat (и тег Grails formatNumber по расширению) немного непрозрачен для определенных случаев использования, и я до сих пор не нашел разумного способа сделать с ним довольно простое форматирование без некоторой уродливой предварительной обработки для генерации соответствующей строки формата.Несколько месяцев назад я собрал простой тег форматирования числа, который, по сути, создает строку формата и выполняет некоторую минимальную обработку самого числа.

Это не так универсально и элегантно, как хотелось бы (это все, что нам было нужно в то время - он супер простой, но он по-прежнему сохраняет некоторую некрасивую обработку в GSP), но его должно быть легко читать, и очевидно, что его можно тривиально улучшить (например, сделать масштабирование итеративным, а не наивным условием if-elseif, позволяющим пользователю для передачи настраиваемых маркеров масштабирования, позволяющих использовать настраиваемый валидатор числа в качестве параметра и т. д.).


// Formats a number to 3 significant digits, appending appropriate scale marker
// (k, m, b, t, etc.). Defining var allows you to use a string representation
// of the formatted number anywhere you need it within the tag body, and 
// provides the scale as well (in case highlighting or other special formatting
// based upon scale is desired).
def formatNumberScaled = {attrs, body -> // number, prefix, suffix, invalid, var
    Double number
    String numberString
    String scale

    try {
        number = attrs.'number'.toDouble()
    } catch (Exception e) {
        number = Double.NaN
    }

    if (number.isNaN() || number.isInfinite()) {
        numberString = scale = attrs.'invalid' ?: "N/A"
    } else {
        Boolean negative = number < 0d
        number = negative ? -number : number

        if (number < 1000d) {
            scale = ''
        } else if (number < 1000000d) {
            scale = 'k'
            number /= 1000d
        } else if (number < 1000000000d) {
            scale = 'm'
            number /= 1000000d
        } else if (number < 1000000000000d) {
            scale = 'b'
            number /= 1000000000d
        } else if (number < 1000000000000000d) {
            scale = 't'
            number /= 1000000000000d
        }

        String format
        if (number < 10d) {
            format = '#.00'
        } else if (number < 100d) {
            format = '##.0'
        } else {
            format = '###'
        }
        format = "'${attrs.'prefix' ?: ''}'${format}'${scale} ${attrs.'suffix' ?: ''}'"

        numberString = g.formatNumber('number': negative ? -number : number, 'format': format)
    }

    // Now, either print the number or output the tag body with
    // the appropriate variables set
    if (attrs.'var') {
        out << body((attrs.'var'): numberString, 'scale': scale)
    } else {
        out << numberString
    }
}
1
ответ дан 17 December 2019 в 18:21
поделиться

У меня есть тег "fmt:relDate", который дает Вам подобные Твиттеру относительные даты "3 дня назад", "меньше чем 30 секунд назад", и т.д., с реальным временем как подсказка.

Текущая реализация является в основном гигантской цепочкой условных операторов с границами, которые я люблю. Основанный на двоичном поиске алгоритм был бы лучше (в смысле "более эффективного"), и текущей реализации закодировали мои персональные предпочтения в него, таким образом, я отказываюсь совместно использовать тег.

1
ответ дан 17 December 2019 в 18:21
поделиться

У меня есть вкладка для удаленной разбивки на страницы, которая помогает мне разбивать результаты на страницы с помощью ajax. Это улучшение по сравнению с вкладкой по умолчанию и включает настраиваемые аргументы.

Вот код:

class CustomRemotePaginateTagLib {

  static namespace = 'myTagLib'

  /** * Creates next/previous links to support pagination for the current controller * * <g:paginate total="$ { Account.count() } " />               */
  def remotePaginate = {attrs ->
    def writer = out
    if (attrs.total == null) throwTagError("Tag [remotePaginate] is missing required attribute [total]")

    if (attrs.update == null) throwTagError("Tag [remotePaginate] is missing required attribute [update]")

    def locale = RequestContextUtils.getLocale(request)

    def total = attrs.total.toInteger()

    def update = attrs.update

    def action = (attrs.action ? attrs.action : (params.action ? params.action : "list"))
    def controller = (attrs.controller ? attrs.controller : params.controller)
    def offset = params.offset?.toInteger()
    def max = params.max?.toInteger()
    def maxsteps = (attrs.maxsteps ? attrs.maxsteps.toInteger() : 10)

    if (!offset) offset = (attrs.offset ? attrs.offset.toInteger() : 0)
    if (!max) max = (attrs.max ? attrs.max.toInteger() : 10)

    def linkParams = [offset: offset - max, max: max]
    if (params.sort) linkParams.sort = params.sort
    if (params.order) linkParams.order = params.order
    if (attrs.params) linkParams.putAll(attrs.params)
    linkParams['action'] = action
    linkParams['controller'] = controller

    def linkTagAttrs = [url: "#"]
    if (attrs.controller) { linkTagAttrs.controller = attrs.controller }
    if (attrs.id != null) { linkTagAttrs.id = attrs.id }

    // determine paging variables
    def steps = maxsteps > 0
    int currentstep = (offset / max) + 1
    int firststep = 1
    int laststep = Math.round(Math.ceil(total / max))

    // display previous link when not on firststep
    if (currentstep > firststep) {
      linkTagAttrs.class = 'prevLink'
      def prevOffset = linkParams.offset

      def params = attrs.params ?: []
      params.'offset' = prevOffset

      linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params)
      writer << link(linkTagAttrs.clone()) {
        (attrs.prev ? attrs.prev : g.message(code: 'default.paginate.prev', default: 'Previous'))
      }
    }

    // display steps when steps are enabled and laststep is not firststep
    if (steps && laststep > firststep) {
      linkTagAttrs.class = 'step'

      // determine begin and endstep paging variables
      int beginstep = currentstep - Math.round(maxsteps / 2) + (maxsteps % 2)
      int endstep = currentstep + Math.round(maxsteps / 2) - 1

      if (beginstep < firststep) {
        beginstep = firststep
        endstep = maxsteps
      }
      if (endstep > laststep) {
        beginstep = laststep - maxsteps + 1
        if (beginstep < firststep) {
          beginstep = firststep
        }
        endstep = laststep
      }

      // display firststep link when beginstep is not firststep
      if (beginstep > firststep) {
        linkParams.offset = 0

        def params = attrs.params ?: []
        params['offset'] = linkParams.offset

        linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params)
        writer << link(linkTagAttrs.clone()) { firststep.toString() }
        writer << '<span class="step">..</span>'
      }

      // display paginate steps
      (beginstep..endstep).each {i ->
        if (currentstep == i) {
          writer << "<span class=\"currentStep\">${i}</span>"
        } else {
          linkParams.offset = (i - 1) * max

          def params = attrs.params ?: []
          params['offset'] = linkParams.offset

          linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params)
          writer << link(linkTagAttrs.clone()) { i.toString() }
        }
      }

      // display laststep link when endstep is not laststep
      if (endstep < laststep) {
        writer << '<span class="step">..</span>'
        linkParams.offset = (laststep - 1) * max

        def params = attrs.params ?: []
        params['offset'] = linkParams.offset

        linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params)
        writer << link(linkTagAttrs.clone()) { laststep.toString() }
      }
    }

    // display next link when not on laststep
    if (currentstep < laststep) {
      linkTagAttrs.class = 'nextLink'
      linkParams.offset = offset + max

      def params = attrs.params ?: []
      params['offset'] = linkParams.offset

      linkTagAttrs.onclick = g.remoteFunction(update: update, action: linkParams.action, controller: linkParams.controller, params: params)
      writer << link(linkTagAttrs.clone()) {
        (attrs.next ? attrs.next : g.message(code: 'default.paginate.next', default: 'Next'))
      }
    }

  }
1
ответ дан 17 December 2019 в 18:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: