【问题标题】:Add a search box to custom input control in shiny在闪亮的自定义输入控件中添加搜索框
【发布时间】:2015-11-26 16:51:51
【问题描述】:

我的目标是在闪亮的自定义输入控件顶部添加一个搜索框。例如,当用户搜索 Hampshire 时,我想选择选择 New Hampshire,这目前是不可能的,因为它只搜索第一个字母。

server.R

shinyServer(function(input, output, session) {

output$main <- renderUI({
source("chooser.R")
chooserInput("mychooser","Available frobs","Selected frobs",
row.names(USArrests),c(),size=20,multiple=TRUE)})
})

ui.R

source("chooser.R")

shinyUI(fluidPage(
uiOutput("main")
))

chooser.R

chooserInput <- function(inputId, leftLabel, rightLabel, leftChoices, rightChoices,
size = 5, multiple = FALSE) {

leftChoices <- lapply(leftChoices, tags$option)
rightChoices <- lapply(rightChoices, tags$option)

if (multiple)
  multiple <- "multiple"
else
multiple <- NULL

tagList(
singleton(tags$head(
  tags$script(src="chooser-binding.js"),
  tags$style(type="text/css",
    HTML(".chooser-container { display: inline-block; }")
  )
)),
div(id=inputId, class="chooser",
  div(class="chooser-container chooser-left-container",
    tags$select(class="left", size=size, multiple=multiple, leftChoices)
  ),
  div(class="chooser-container chooser-center-container",
    icon("arrow-circle-o-right", "right-arrow fa-3x"),
    tags$br(),
    icon("arrow-circle-o-left", "left-arrow fa-3x")
    ),
    div(class="chooser-container chooser-right-container",
    tags$select(class="right", size=size, multiple=multiple, rightChoices)
  )
)
)
}

registerInputHandler("shinyjsexamples.chooser", function(data, ...) {
if (is.null(data))
NULL
else
list(left=as.character(data$left), right=as.character(data$right))
}, force = TRUE)  

chooser-binding.js(在 www 文件夹中)

(function() {

function updateChooser(chooser) {
chooser = $(chooser);
var left = chooser.find("select.left");
var right = chooser.find("select.right");
var leftArrow = chooser.find(".left-arrow");
var rightArrow = chooser.find(".right-arrow");

var canMoveTo = (left.val() || []).length > 0;
var canMoveFrom = (right.val() || []).length > 0;

leftArrow.toggleClass("muted", !canMoveFrom);
rightArrow.toggleClass("muted", !canMoveTo);
}

function move(chooser, source, dest) {
chooser = $(chooser);
var selected = chooser.find(source).children("option:selected");
var dest = chooser.find(dest);
dest.children("option:selected").each(function(i, e) {e.selected = false;});
dest.append(selected);
updateChooser(chooser);
chooser.trigger("change");
}

$(document).on("change", ".chooser select", function() {
updateChooser($(this).parents(".chooser"));
});

$(document).on("click", ".chooser .right-arrow", function() {
move($(this).parents(".chooser"), ".left", ".right");
});

$(document).on("click", ".chooser .left-arrow", function() {
move($(this).parents(".chooser"), ".right", ".left");
});

$(document).on("dblclick", ".chooser select.left", function() {
move($(this).parents(".chooser"), ".left", ".right");
});

$(document).on("dblclick", ".chooser select.right", function() {
move($(this).parents(".chooser"), ".right", ".left");
});

var binding = new Shiny.InputBinding();

binding.find = function(scope) {
return $(scope).find(".chooser");
};

binding.initialize = function(el) {
updateChooser(el);
};

binding.getValue = function(el) {
return {
left: $.makeArray($(el).find("select.left option").map(function(i, e) { return      e.value; })),
right: $.makeArray($(el).find("select.right option").map(function(i, e) {   return e.value; }))
}
};

binding.setValue = function(el, value) {
// TODO: implement
};

binding.subscribe = function(el, callback) {
$(el).on("change.chooserBinding", function(e) {
callback();
});
};

binding.unsubscribe = function(el) {
$(el).off(".chooserBinding");
};

binding.getType = function() {
return "shinyjsexamples.chooser";
};

Shiny.inputBindings.register(binding, "shinyjsexamples.chooser");

})();

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    酷小部件(或任何术语)。这个问题实际上已经回答了here,所以如果它对你有帮助,请务必对这些人的答案进行投票。

    这是一个超级简单的实现(可能会更好):

    选择器.R

    chooserInput <- function(inputId, leftLabel, rightLabel, leftChoices, rightChoices,
                             size = 5, multiple = FALSE) {
    
      leftChoices <- lapply(leftChoices, tags$option)
      rightChoices <- lapply(rightChoices, tags$option)
    
      if (multiple)
        multiple <- "multiple"
      else
        multiple <- NULL
    
      tagList(
        singleton(tags$head(
          tags$script(src="chooser-binding.js"),
          tags$style(type="text/css",
                     HTML(".chooser-container { display: inline-block; }")
          )
        )),
        div(id=inputId, class="chooser",style="",
            div(
              div(style="min-width:100px;",
                  tags$input(type="text",class="chooser-input-search",style="width:100px;")
              )
            ),
            div(style="display:table",
                div(style="min-width:100px; display:table-cell;",
                    div(class="chooser-container chooser-left-container",
                        style="width:100%;",
                        tags$select(class="left", size=size, multiple=multiple, leftChoices,style="width:100%;min-width:100px")
                    )
                ),
                div(style="min-width:50px; display:table-cell;vertical-align: middle;",
                    div(class="chooser-container chooser-center-container",
                        style="padding:10px;",
                        icon("arrow-circle-o-right", "right-arrow fa-3x"),
                        tags$br(),
                        icon("arrow-circle-o-left", "left-arrow fa-3x")
                    )
                ),
                div(style="min-width:100px; display:table-cell;",
                    div(class="chooser-container chooser-right-container", style="width:100%;",
                        tags$select(class="right", size=size, multiple=multiple, rightChoices,style="width:100%;")
                    )
                )
            )
        )
      )
    }
    
    registerInputHandler("shinyjsexamples.chooser", function(data, ...) {
      if (is.null(data))
        NULL
      else
        list(left=as.character(data$left), right=as.character(data$right))
    }, force = TRUE) 
    

    选择器绑定.js

    (function() {
    
    var options = [];
    jQuery.fn.filterByText = function(textbox, selectSingleMatch) {
      return this.each(function() {
        var select = this;
        options = [];
        $(select).find('option').each(function() {
          options.push({value: $(this).val(), text: $(this).text()});
        });
        $(select).data('options', options);
        $(textbox).bind('change keyup', function() {
          options = $(select).empty().scrollTop(0).data('options');
          var search = $.trim($(this).val());
          var regex = new RegExp(search,'gi');
    
          $.each(options, function(i) {
            var option = options[i];
            if(option.text.match(regex) !== null) {
              $(select).append(
                 $('<option>').text(option.text).val(option.value)
              );
            }
          });
          if (selectSingleMatch === true && 
              $(select).children().length === 1) {
            $(select).children().get(0).selected = true;
          }
        });
      });
    };
    
    function updateChooser(chooser) {
        chooser = $(chooser);
        var left = chooser.find("select.left");
        var right = chooser.find("select.right");
        var leftArrow = chooser.find(".left-arrow");
        var rightArrow = chooser.find(".right-arrow");
    
        var canMoveTo = (left.val() || []).length > 0;
        var canMoveFrom = (right.val() || []).length > 0;
    
        leftArrow.toggleClass("muted", !canMoveFrom);
        rightArrow.toggleClass("muted", !canMoveTo);
    }
    
    function move(chooser, source, dest) {
        chooser = $(chooser);
        var selected = chooser.find(source).children("option:selected");
        var dest = chooser.find(dest);
        dest.children("option:selected").each(function(i, e) {e.selected = false;});
        dest.append(selected);
        updateChooser(chooser);
        chooser.trigger("change");
    }
    
    $(".chooser").change(function(){
    
    });
    
    $(document).on("change", ".chooser select", function() {
        updateChooser($(this).parents(".chooser"));
    });
    
    $(document).on("click", ".chooser .right-arrow", function() {
        move($(this).parents(".chooser"), ".left", ".right");
    });
    
    $(document).on("click", ".chooser .left-arrow", function() {
        move($(this).parents(".chooser"), ".right", ".left");
    });
    
    $(document).on("dblclick", ".chooser select.left", function() {
        move($(this).parents(".chooser"), ".left", ".right");
    });
    
    $(document).on("dblclick", ".chooser select.right", function() {
        move($(this).parents(".chooser"), ".right", ".left");
    });
    
    var binding = new Shiny.InputBinding();
    
    binding.find = function(scope) {
        return $(scope).find(".chooser");
    };
    
    binding.initialize = function(el) {
        updateChooser(el);
        $(function() {
          $('.left').filterByText($('.chooser-input-search'), true);
        }); 
    };
    
    binding.getValue = function(el) {
    return {
        left: $.makeArray($(el).find("select.left option").map(function(i, e) { return      e.value; })),
        right: $.makeArray($(el).find("select.right option").map(function(i, e) {   return e.value; }))
    }
    };
    
    binding.setValue = function(el, value) {
    // TODO: implement
    };
    
    binding.subscribe = function(el, callback) {
        $(el).on("change.chooserBinding", function(e) {
            callback();
        });
    };
    
    binding.unsubscribe = function(el) {
        $(el).off(".chooserBinding");
    };
    
    binding.getType = function() {
    return "shinyjsexamples.chooser";
    };
    
    Shiny.inputBindings.register(binding, "shinyjsexamples.chooser");
    
    })();
    

    如您所见,这几乎是可耻的复制和粘贴。

    【讨论】:

    • 非常感谢奥斯卡。最后一个问题。我的 server.R 和 ui.R 版本与我最初发布的有点不同。您发送的内容适用于我的初始帖子。我编辑以显示我的版本。你知道为什么我的版本禁用了搜索框吗?重要的是我要在 server.R 端编写代码并将其传递给 ui.R。
    • 这是因为我的代码真的很糟糕,我在 DOM 中绑定的元素在我绑定搜索功能时不存在所以我已经更改它以便搜索功能绑定在初始化上。此外,我刚刚为不同元素硬编码了一些宽度,但这可能不是要走的路。
    • 感谢您的回复。我不知道为什么,但是使用新代码后,搜索框不显示了。
    • 嘿奥斯卡。我终于让它工作了。我有最后一个问题。我刚刚将它作为一个新问题发布,所以我可以给予信任。如果它不占用您太多时间,如果您能看一下,我将不胜感激。我无法想象除了你之外的任何人会尝试解决它。​​
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 1970-01-01
    • 2020-12-28
    相关资源
    最近更新 更多