在闪亮的自定义输入控件中添加一个搜索框

时间:2015-11-26 16:51:52

标签: r shiny

我的目标是在自定义输入控件的顶部添加一个闪亮的搜索框。我想当用户搜索汉普郡时,选择新罕布什尔州的选择目前不可能,因为它只搜索第一个字母。

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

})();

1 个答案:

答案 0 :(得分:3)

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

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

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

})();

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