Skip to content

Instantly share code, notes, and snippets.

@benoitongit
Last active May 8, 2021 10:24
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save benoitongit/68deff5b3abba809e5aca7e410a93112 to your computer and use it in GitHub Desktop.
Save benoitongit/68deff5b3abba809e5aca7e410a93112 to your computer and use it in GitHub Desktop.
Make Vue.js works with Ruby on Rails and Turbolinks 5. Gem is available here: https://github.com/benoitongit/vue-on-rails
@MyComponent =
props: ['myProp']
template: '<div>A custom component with {{myProp}}</div>'
# Vue adapter to mount/destroy components
VueAdapter = do ->
vueModels = []
init = ->
vueModels = []
vueComponents = document.querySelectorAll('[data-vue-component]')
return if vueComponents.length <= 0
for i in [0...vueComponents.length]
mountComponent(vueComponents[i])
mountComponent = (component) ->
name = component.getAttribute('data-vue-component')
props = JSON.parse(component.getAttribute('data-vue-props'))
if typeof window[name] == 'object'
el = document.createElement('element-to-be-mounted')
component.innerHTML = ''
component.appendChild(el)
vm = newVueInstance(name, props, el)
vueModels.push(vm)
newVueInstance = (name, props, el) ->
nameFormatted = camelCaseToHyphen(name)
element = document.createElement(nameFormatted)
setElementProps(element, props)
component = window[name]
new Vue({
el: el
template: element.outerHTML
components: { "#{nameFormatted}": component }
})
setElementProps = (element, props) ->
for key, value of props
if typeof value == 'object'
element.setAttribute(key, JSON.stringify(value))
else
element.setAttribute(key, value)
camelCaseToHyphen = (string) ->
string.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
destroy = ->
for vm in vueModels
vm.$destroy()
vueModels = []
{ init, destroy }
document.addEventListener 'turbolinks:load', -> VueAdapter.init()
document.addEventListener 'turbolinks:before-render', -> VueAdapter.destroy()
<%= vue_component 'MyComponent', my_prop: 'awesome prop!' %>
<!--
Final Outpout:
<div data-vue-component="MyComponent" data-vue-props="{&quot;my-prop&quot;:&quot;awesome prop!&quot;}">
<div>A custom component with awesome prop!</div>
</div>
-->
module VueHelper
def vue_component(component_name, props = {}, html_options = {})
html_options = html_options.reverse_merge(data: {})
props = VueHelper.format_props(props)
html_options[:data].tap do |data|
data[:vue_component] = component_name
data[:vue_props] = props.to_json
end
html_tag = html_options[:tag] || :div
html_options.except!(:tag)
content_tag(html_tag, '', html_options)
end
def self.format_props(props)
case props
when Hash
props.each_with_object({}) do |(key, value), new_props|
new_key = key.to_s.dasherize
new_props[new_key] = VueHelper.camelize_props(value)
end
else
props
end
end
def self.camelize_props(props)
case props
when Hash
props.each_with_object({}) do |(key, value), new_props|
new_key = key.to_s.camelize(:lower)
new_props[new_key] = VueHelper.camelize_props(value)
end
else
props
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment