Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: allow Shiny.onInputChange() to send a message to R immediately #1476

Closed
daattali opened this issue Nov 22, 2016 · 3 comments

Comments

@daattali
Copy link
Contributor

daattali commented Nov 22, 2016

Loosely related to #928

If I update the same input multiple times within the same function call in JavaScript, then Shiny (on the R side) is only aware of the last value.

I saw there was an immediate param in some of the JS code that deals with setting inputs, but I couldn't figure out how to make it work, or if it's even relevant.

Example: whenever you click on the button, an input called "testr" should be updated thrice in succession. But only the last value gets printed in Shiny using the input$testr variable

library(shiny)

ui <- fluidPage(
  tags$script("
    Shiny.addCustomMessageHandler('testjs', function(num) {
      Shiny.onInputChange('testr', num);
      Shiny.onInputChange('testr', num+1);
      Shiny.onInputChange('testr', num+10);
    });"
  ),
  actionButton("btn", "Click me")
)

server <- function(input, output, session) {
  observeEvent(input$btn, {
    session$sendCustomMessage('testjs', input$btn)
  })
  
  observe({
    cat(input$testr, "\n")
  })
}

shinyApp(ui = ui, server = server)
@jcheng5
Copy link
Member

jcheng5 commented Nov 22, 2016

Yeah, there's no workaround today that I'm aware of. This is one reason why the method name is onInputChange instead of setValue or sendValue, because there's all this "smart" deduping and batching and other stuff going on, that only really makes perfect sense for traditional inputs.

Well, if you really have to work around this today, you could keep a queue of name/value pairs, and pop them one by one on each tick. That would delay the sending by a few milliseconds but at least they would not be coalesced.

function SenderQueue() {
  this.readyToSend = true;
  this.queue = [];
  this.timer = null;
}
SenderQueue.prototype.send = function(name, value) {
  var self = this;
  function go() {
    self.timer = null;
    if (self.queue.length) {
      var msg = self.queue.shift();
      Shiny.onInputChange(msg.name, msg.value);
      self.timer = setTimeout(go, 0);
    } else {
      self.readyToSend = true;
    }
  }
  if (this.readyToSend) {
    this.readyToSend = false;
    Shiny.onInputChange(name, value);
    this.timer = setTimeout(go, 0);
  } else {
    this.queue.push({name: name, value: value});
    if (!this.timer) {
      this.timer = setTimeout(go, 0);
    }
  }
};

Example:

// Somewhere global-ish
var queue = new SenderQueue();

// Later...
queue.send("testr", 0);
queue.send("testr", 1);
queue.send("testr", 2);
queue.send("testr", 3);

@daattali
Copy link
Contributor Author

Thanks Joe, I thought about doing all this setTimeout(0) business but was
hoping to instead fiddle with shiny to figure out a proper solution. Thanks
for taking the time to provide the perfectly functional implementation


R-Shiny Consultant
http://deanattali.com/shiny

On 21 November 2016 at 19:28, Joe Cheng notifications@github.com wrote:

Yeah, there's no workaround today that I'm aware of. This is one reason
why the method name is onInputChange instead of setValue or sendValue,
because there's all this "smart" deduping and batching and other stuff
going on, that only really makes perfect sense for traditional inputs.

Well, if you really have to work around this today, you could keep a queue
of name/value pairs, and pop them one by one on each tick. That would delay
the sending by a few milliseconds but at least they would not be coalesced.

function SenderQueue() {
this.readyToSend = true;
this.queue = [];
this.timer = null;
}SenderQueue.prototype.send = function(name, value) {
var self = this;
function go() {
self.timer = null;
if (self.queue.length) {
var msg = self.queue.shift();
Shiny.onInputChange(msg.name, msg.value);
self.timer = setTimeout(go, 0);
} else {
self.readyToSend = true;
}
}
if (this.readyToSend) {
this.readyToSend = false;
Shiny.onInputChange(name, value);
this.timer = setTimeout(go, 0);
} else {
this.queue.push({name: name, value: value});
if (!this.timer) {
this.timer = setTimeout(go, 0);
}
}
};

Example:

// Somewhere global-ishvar queue = new SenderQueue();
// Later...queue.send("testr", 0);queue.send("testr", 1);queue.send("testr", 2);queue.send("testr", 3);


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
#1476 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AA6IFAIe3-RzACYbezDWwBBlxSTJ0ePtks5rAjcWgaJpZM4K422a
.

@alandipert
Copy link
Contributor

AFAIK we don't have plans to add this, but Joe provided an acceptable workaround.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants