【问题标题】:Customizing search box in shiny app在闪亮的应用程序中自定义搜索框
【发布时间】:2015-11-27 06:07:19
【问题描述】:

我有一个搜索状态的搜索框,我的选择可以转移到另一个存储桶。我的目标是让用户能够在搜索他们感兴趣的状态时按 Enter 并将他们的结果推送到选择桶。例如,用户将 New Hampshire 放入搜索框中并按 Enter 键 - New 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",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) 

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

(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");

})();

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

【问题讨论】:

    标签: r shiny


    【解决方案1】:

    我认为这有点工作

    选择器.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);
    
        // Push back options to left select array
        if(source == '.right'){
          $.each(selected,function(i){
            var sel = selected[i];
            options.push({value: $(sel).val(), text: $(sel).text()});
          });
        }
    
        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);
    
        /*
          Create separate bindings for each chooser widget
        */
        $('.chooser').each(function(){
          var chooser   = $(this);
          var left_sel  = $(this).find('.left');
          var right_sel = $(this).find('.right');
          var search_b  = $(this).find('.chooser-input-search');
    
          // Search function
          $(function() {
            $(left_sel).filterByText(search_b, true);
          }); 
    
          //Enter binding
          // If element in focus
          $('.chooser-input-search').focus(function() {
            $(this).keypress(function(e){
              // If enter pressed
              if(e.which == 13) {
    
                if( $(search_b).val().length > 2){
                  // Save for debuging 
                  var sel_options = [];
                  $(left_sel).find('option').each(function() {
                    var curr_val = $(this).val();
                    var curr_txt = $(this).text();
    
                    // Push to debug array
                    sel_options.push({value: curr_val, text: curr_txt});
    
                    // Append to tight selection
                    $(right_sel).append(
                      $('<option>').text(curr_val).val(curr_txt)
                    );
    
                    // Remove option 
                    $(this).remove();
    
                  }); // end each
                  // Remove options from gloabl options array
                  $.each(options, function(i) {
                    var option = options[i];
    
                    $.each(sel_options,function(j){
                      var sel_option = sel_options[j];
                      if (option.value==sel_option.value && option.text==sel_option.text){
                         options.splice(i, 1);
                      }
                    });
                  });
                }
              }
            });
          });
        });  // End enter keybinding
    };
    
    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");
    
    })();
    

    【讨论】:

    • 谢谢奥斯卡。它工作得很好,除非用户在搜索框中按下回车键而不输入任何内容。发生这种情况时,将选择整个列表。如果用户没有写任何东西并按下回车,是否可以不选择任何人?
    • 我更新了答案,现在输入需要至少 2 个字符长改变 if( $(search_b).val().length &gt; 2) 部分来改变它。看起来这仍然需要一些微调,但至少可以解决。
    • 嗨,奥斯卡,希望一切顺利。当你从左边选择一个状态并push到selected bucket中,然后从selected bucked推回原来的list,然后搜索那个状态;有重复的状态。有没有一种简单的方法可以解决这个问题,这样就不会出现重复?
    猜你喜欢
    • 1970-01-01
    • 2016-02-24
    • 2018-06-09
    • 2020-07-12
    • 1970-01-01
    • 2013-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多