diff --git a/lib/stimulus_reflex.rb b/lib/stimulus_reflex.rb index 6b39bc93..d94014cf 100644 --- a/lib/stimulus_reflex.rb +++ b/lib/stimulus_reflex.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "uri" require "open-uri" require "rack" @@ -15,6 +13,7 @@ require "stimulus_reflex/concern_enhancer" require "stimulus_reflex/configuration" require "stimulus_reflex/callbacks" +require "stimulus_reflex/fragment" require "stimulus_reflex/request_parameters" require "stimulus_reflex/reflex" require "stimulus_reflex/reflex_data" diff --git a/lib/stimulus_reflex/broadcasters/page_broadcaster.rb b/lib/stimulus_reflex/broadcasters/page_broadcaster.rb index ab182aaf..ad81e311 100644 --- a/lib/stimulus_reflex/broadcasters/page_broadcaster.rb +++ b/lib/stimulus_reflex/broadcasters/page_broadcaster.rb @@ -4,15 +4,14 @@ module StimulusReflex class PageBroadcaster < Broadcaster def broadcast(selectors, data) reflex.controller.process reflex.params[:action] - page_html = reflex.controller.response.body + fragment = StimulusReflex::Fragment.new(reflex.controller.response.body) - return unless page_html.present? + return if fragment.empty? - document = Nokogiri::HTML.parse(page_html) - selectors = selectors.select { |s| document.css(s).present? } + selectors = selectors.select { |s| fragment.match(s).present? } selectors.each do |selector| operations << [selector, :morph] - html = document.css(selector).inner_html(save_with: Broadcaster::DEFAULT_HTML_WITHOUT_FORMAT) + html = fragment.match(selector).to_html cable_ready.morph( selector: selector, html: html, diff --git a/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb b/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb index 3fd1769f..91cd172b 100644 --- a/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb +++ b/lib/stimulus_reflex/broadcasters/selector_broadcaster.rb @@ -7,13 +7,13 @@ def broadcast(_, data = {}) selectors, html = morph updates = create_update_collection(selectors, html) updates.each do |update| - fragment = Nokogiri::HTML.fragment(update.html.to_s) - match = fragment.at_css(update.selector) + fragment = StimulusReflex::Fragment.new(update.html) + match = fragment.match(update.selector) if match.present? operations << [update.selector, :morph] cable_ready.morph( selector: update.selector, - html: match.inner_html(save_with: Broadcaster::DEFAULT_HTML_WITHOUT_FORMAT), + html: match.to_html, payload: payload, children_only: true, permanent_attribute_name: permanent_attribute_name, diff --git a/lib/stimulus_reflex/fragment.rb b/lib/stimulus_reflex/fragment.rb new file mode 100644 index 00000000..bf4c606e --- /dev/null +++ b/lib/stimulus_reflex/fragment.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module StimulusReflex + class Fragment + delegate :to_html, to: :@fragment + + def initialize(html) + @fragment = Nokogiri::HTML.fragment(html.to_s) + @matches = { + "body" => Match.new(@fragment) + } + end + + def empty? + @fragment.content.empty? + end + + def match(selector) + @matches[selector] ||= Match.new(@fragment.at_css(selector)) + end + + Match = Struct.new(:element) do + delegate :present?, to: :element + + def to_html + element&.inner_html(save_with: Broadcaster::DEFAULT_HTML_WITHOUT_FORMAT) + end + end + end +end