在闪亮的应用程序中自定义搜索框

时间:2015-11-27 06:07:20

标签: r shiny

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

})();

正如你所看到的,这是一个可耻的复制和粘贴。

1 个答案:

答案 0 :(得分:1)

我认为这有点作用

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) 

选择器-bindings.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");

})();