ruby-hyperloop / hyper-react

The project has moved to Hyperstack!!
https://hyperstack.org/
MIT License
285 stars 14 forks source link

need way to dynamically mount components (i.e. after ajax calls) #252

Open catmando opened 6 years ago

catmando commented 6 years ago
require 'jquery'
require 'opal-jquery'

module Hyperloop
  def self.mount_all
    Element['[data-hyperloop-mount]'].each do |mount_point|
      component_name = mount_point.attr('data-hyperloop-mount')
      component = nil
      begin
        component = Object.const_get(component_name)
      rescue
        message = "Could not find Component class named #{component_name}"
        `console.error(message)`
        next
      end
      params = Hash[*mount_point.data.collect do |name, value|
        [name.underscore, value] unless name == 'hyperloopMount'
      end.compact.flatten(1)]
      React.render(React.create_element(component, params), mount_point)
    end
  end
  %x{
     window.hyperloop_mount_all = function() {
       #{Hyperloop.mount_all}
      }
    }
end

From opal you can call Hyperloop.mount_all or from js land its hyperloop_mount_all()

It will find all components with the data-hyperloop-mount data attribute, and the name of the component will be the attribute value. For example

<DIV data-hyperloop-mount='MyBigComponent' data-param1='some data'>
</DIV>

will mount MyBigComponent at the DIV and pass it 'some data' as param1

janbiedermann commented 6 years ago

already there:

React::Test::Utils.render_component_into_document(MyComponent, args)

maybe needs a better name

catmando commented 6 years ago

actually, its built into react-rails, and works with the <% react_component ...%> helper!

we can make it a bit nicer like this:

#jquery-mounter.rb
require 'jquery'
require 'opal-jquery'
%x{
  (function ( $ ) {
    $.fn.mount_components = function() {
      this.each(function(e) { ReactRailsUJS.mountComponents(e[0]) })
      return this;
    }
  }( jQuery ));
}
Element.expose :mount_components

Now we can add the components to the view using <%= react_component ... %> and mount them by doing a dom query, then calling mount_components.

Typical in JS:

// fetch.js.erb
$('#target_div').html("<%= escape_javascript(render partial: 'fetch') %>").mount_components();
// fetch.erb contains our html code including 1 or more component mount points

In Ruby

Element['body'].mount_components # mount everything!