vue-r / vueR

vue.js for R
https://vue-r.github.io/vueR
Other
140 stars 14 forks source link

Shiny and vuex #7

Open timelyportfolio opened 4 years ago

timelyportfolio commented 4 years ago

I often wonder why does Shiny manage UI state from R instead of UI state (especially state that does not affect/require R) being managed in JavaScript. If we pursue this approach with vuex then JavaScript would manage all UI/frontend state and call R only when necessary allowing JS/HTML/CSS to do what it does best and more directly. This decision does not require vuex, but in a vue context I think vuex is a good established solution.

Code

# quick example of Vuex and Shiny
# reference: https://vuex.vuejs.org/guide/

library(htmltools)
library(shiny)
library(vueR)

vuex <- tags$script(src="https://unpkg.com/vuex@2.5.0/dist/vuex.js")

ui <- tagList(
  html_dependency_vue(offline = FALSE, minified = FALSE),
  tags$head(vuex),
  tags$h1("Shiny with Vue and Vuex"),
  tags$h3('Vue App'),
  tags$div(
    id = "app",
    tags$button(`@click`="increment", "+"),
    tags$button(`@click`="decrement", "-"),
    "{{ count }}"
  ),
  tags$h3("Shiny controls"),
  # add Shiny buttons to demonstrate Shiny to update Vuex state
  actionButton(inputId = "btnIncrement", label="+"),
  actionButton(inputId = "btnDecrement", label="-"),
  tags$script(HTML(
"
// first example in Vuex documentation
Vue.use(Vuex)

// make sure to call Vue.use(Vuex) if using a module system

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => state.count++,
    decrement: state => state.count--
  }
})

const app = new Vue({
  el: '#app',
  store: store,
  computed: {
    count () {
      return this.$store.state.count
    }
  },
  methods: {
    increment () {
      this.$store.commit('increment')
    },
    decrement () {
        this.$store.commit('decrement')
    }
  }
})

$(document).on('shiny:sessioninitialized', function() {
  // increment from Shiny custom message
  //   chose 'increment' but does not have to match the store mutation name
  Shiny.addCustomMessageHandler('increment', function(msg) {
    app.$store.commit('increment')
  });
  Shiny.addCustomMessageHandler('decrement', function(msg) {
    app.$store.commit('decrement')
  });
})
"
  ))
)

#browsable(ui)

server <- function(input, output, session) {
  observeEvent(input$btnIncrement, {
    session$sendCustomMessage("increment", list())
  })
  observeEvent(input$btnDecrement, {
    session$sendCustomMessage("decrement", list())
  })
}

shinyApp(
  ui = ui,
  server = server,
  options = list(launch.browser = rstudioapi::viewer)
)

vuer_7