Re: multiple input with same html element name

572 views
Skip to first unread message
Message has been deleted
Message has been deleted

ZJ

unread,
Sep 19, 2013, 12:14:49 AM9/19/13
to shiny-...@googlegroups.com
Hi,

I don't have a solution yet but there are a few things I noticed.

I see that you have used the name = in your example. My interpretation of the Shiny tutorial (see getId(el) section of http://rstudio.github.io/shiny/tutorial/#building-inputs) is that Shiny looks for id = "some_name" or inputId = "some_name" and then input$some_name can be accessed.

Actually if you have multiple components with the same id then all the component will display in the webbrowser then it might cause "erratic" behaviour. Shiny uses JavaScript to find elements in the HTML when you have two tags/components with the same Id the default behaviour is that it will return the first one only. So for example if you 10 inputs with the same id then it may read the value in the first one only. The Id in html is meant to be unique even though the browser will not force you to do that. 

I think you wanted to have an array of inputs. In that cases each id needs to be unique so that you have param1, param2 ...paramn etc. But since the number is variable so n is determined dynamically. I can think of a way to wrap input arrays into a shiny component, let's call that inputArrayComponent. But that requires some javascript so I might wip an example later.

Also I believe [[]] is actually interpreted as a function in R, meaning input$param[[]] actually means apply the "[[" function to input$param, so behind the scene R is trying to run some code like "[["(input$param), if I am not wrong. Maybe can define '[[' to your advantage though.




On Thursday, September 19, 2013 10:21:28 AM UTC+8, Massi wrote:
oups, In the last line, I mean input$params[[]]. 

Le mercredi 18 septembre 2013 15:21:22 UTC-4, Massi a écrit :
Hi,

I'm trying to get multiple inputs into the same variable but it seems something is wrong with my approach!

It has to be noted that the number of inputs is dynamic. The user may add an undefined number of params. 

Here is my code: 
# html client:
...
<input type="text" name="params[]" class="shiny-bound-input">
<input type="text" name="params[]" class="shiny-bound-input">
<input type="text" name="params[]" class="shiny-bound-input">
...

In the server side I'm using input$params[] to get an array of the values submitted by the user. But it does not seem to work. I also tried using input$lsub2[[]]


Any help?

Thanks!



Message has been deleted

ZJ

unread,
Sep 19, 2013, 11:02:42 AM9/19/13
to shiny-...@googlegroups.com
I see. Very interesting. I did some experimentation if I am not wrong id takes precedent and then Shiny looks for name!
So both of us are right but if you have id = "id" and name = "name" within the same tag's attributes then only input$id will work and input$name wil not. Interesting! I didn't know this


On Thursday, September 19, 2013 10:00:56 PM UTC+8, Massi wrote:
Thank you for the replay,

My understanding is that input elements are bound using their names (source: http://rstudio.github.io/shiny/tutorial/#html-ui):
"HTML form elements (in this case a select list and a number input) are bound to input slots using their name attribute."

ZJ

unread,
Sep 19, 2013, 11:03:00 AM9/19/13
to shiny-...@googlegroups.com
I see. Very interesting. I did some experimentation if I am not wrong id takes precedent and then Shiny looks for name!
So both of us are right but if you have id = "id" and name = "name" within the same tag's attributes then only input$id will work and input$name wil not. Interesting! I didn't know this


On Thursday, September 19, 2013 10:00:56 PM UTC+8, Massi wrote:
Thank you for the replay,

My understanding is that input elements are bound using their names (source: http://rstudio.github.io/shiny/tutorial/#html-ui):
"HTML form elements (in this case a select list and a number input) are bound to input slots using their name attribute."


Le jeudi 19 septembre 2013 00:14:49 UTC-4, ZJ a écrit :
Message has been deleted

ZJ

unread,
Sep 19, 2013, 10:48:05 PM9/19/13
to shiny-...@googlegroups.com
Just in case you actually wanted to have a grid I have an example set up

Joe Cheng

unread,
Sep 20, 2013, 2:54:48 AM9/20/13
to shiny-...@googlegroups.com
I'm impressed you got so close!

    subscribe: function(el, callback) {
      $(el).on('change.textInputArrayBinding', function(event) {
        callback();
      });
    },

Unless I'm missing something, the implementation of subscribe needs to listen for events on the child inputs, not the containing div. So something like

$(el).on('change.textInputArrayBinding, keyup.textInputArrayBinding', 'input', function(event) { callback() });

Also, I don't think your <input type="text" ...> elements should have names, as (I believe) these will still be bound by Shiny and since they have the same names as the containing textInputArrayBinding they will collide (I forget which one will win, I believe whichever one had the higher priority--nonetheless, confusing and best avoided).


On Thu, Sep 19, 2013 at 5:38 PM, Massi <mehdi...@gmail.com> wrote:
Here is my first attempt to implement a custom component to deal with input arrays:

# input-array.js:

// inspiration: textInputArrayBinding and dateRangeInputBinding : https://github.com/rstudio/shiny/blob/master/inst/www/shared/shiny.js#L105
var textInputArrayBinding = {};
$.extend(textInputArrayBinding, {
    // how to find the component
    find: function(scope) {
      return $(scope).find('.shiny-array-input');
    },
    
    // Return the values in an array
    getValue: function(el) {
      var $objs  = $('input:text[name=' + el.id + ']');
      var values = $inputs.map(function(){return $(this).val();}).get();
      return values;
    },
    
    // value must be an array of values    
    setValue: function(el, value) {
      // Clear all inputs
      $('input:text[name=' + el.id + ']').val('');

      // Accept array
      if (value instanceof Array) {
        for (var i = 0; i < value.length; i++) {
          $('input:text[name=' + el.id + ']')[i].val(value[i]); 
        }
      // Else assume it's a single value
      } else {
        $('input:text[name=' + el.id + ']').val(value);
      }

    },
    
    // the values are in the "values" variable
    getState: function(el) {
      var $objs = $('input:text[name=' + el.id + ']');

      // Store values in an array of objects, each with value and label
      var values = new Array($objs.length);
      for (var i = 0; i < values.length; i++) {
        values[i] = { 
          value: $objs[i].value,
          label: $objs[i].parent().parent().find('label[for=' + el.id + ']').text()
          label:   this._getLabel($objs[i])
        };
      }

      return { label:    $(el).find('label[for=' + el.id + ']').text(),
               value:    this.getValue(el),
               values:  values
             };
    },
    
    receiveMessage: function(el, data) {
      var $el = $(el);

      // This will replace all the values
      if (data.hasOwnProperty('values')) {
        for (var i = 0; i < data.values.length; i++) {
          $('input:text[name=' + el.id + ']')[i].val(data.values[i]);
        }
      }

      if (data.hasOwnProperty('value')) this.setValue(el, data.value);

      if (data.hasOwnProperty('label')) $el.find('label[for=' + el.id + ']').text(data.label);

      $(el).trigger('change');
    },
    
    subscribe: function(el, callback) {
      $(el).on('change.textInputArrayBinding', function(event) {
        callback();
      });
    },
    
    unsubscribe: function(el) {
      $(el).off('.textInputArrayBinding');
    },
    
    // Given an input DOM object, get the associated label. Handles labels
    // that wrap the input as well as labels associated with 'for' attribute.
    _getLabel: function(obj) {
      // If <input id='myid'><label for='myid'>label text</label>
      var $label_for = $('label[for=' + obj.id + ']');
      if ($label_for.length > 0) {
        return $.trim($label_for.text());
      }

      // If <label><input /><span>label text</span></label>
      if (obj.parentNode.tagName === "LABEL") {
        return $.trim($(obj.parentNode).find('span').text());
      }

      return null;
    },
    // Given an input DOM object, set the associated label. Handles labels
    // that wrap the input as well as labels associated with 'for' attribute.
    _setLabel: function(obj, value) {
      // If <input id='myid'><label for='myid'>label text</label>
      var $label_for = $('label[for=' + obj.id + ']');
      if ($label_for.length > 0) {
        $label_for.text(value);
      }

      // If <label><input /><span>label text</span></label>
      if (obj.parentNode.tagName === "LABEL") {
        $(obj.parentNode).find('span').text(value);
      }

      return null;
    }

  });
  
  Shiny.inputBindings.register(textInputArrayBinding, 'shiny.textInputArray');
  Shiny.inputBindings.setPriority("shiny.textInputArray", 10);
  
  


# HTML sample:
...
<script src="assets/js/input-array.js" type="text/javascript"></script>
...
<div class="shiny-array-input" id="array1">
    <div class="control-group">
  <label class="control-label span3" for="array1">Array1:</label>
<div class="controls">
            <span id="addVar" class="btn btn-primary">
          <i class="icon-plus icon-white"></i> Add parameter
</span>
</div>
</div>
<div class="control-group">
<label class="control-label span3" ></label>
<div class="controls"><input type="text" name="array1" > 
<span class=" btn btn-danger removeVar">
<i class="icon-minus icon-white"></i> Remove parameter
</span>
</div>
</div>
<div class="control-group">
<label class="control-label span3"></label>
<div class="controls"><input type="text" name="array1" > 
<span class=" btn btn-danger removeVar">
<i class="icon-minus icon-white"></i> Remove parameter
</span>
</div>
</div>
</div>

However, it does not seem to work.

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
0 new messages