Alternative to get_element_value method for HtmlDialog

2017
ui::webdialog
ui::htmldialog

#1

Has a best practices alternative method been set for transferring values from a dialog to an extension? I was storing values in hidden inputs and moving those to the extension with the get_element_value method on the WebDialog.


#2

Yes.

In the UI::HtmlDialog class, the add_action_callback() instance method will convert between Js and Ruby types automatically.

In the old UI::WebDialog class, your webpages could only send String data via a protocol handler.

The UI::HtmlDialog class does not use the protocol handler to send data to Ruby.


That said, you could still likely do it, thus defining a singleton method upon your HtmlDialog instance:

EDIT: Proven not to work! The UI::HtmlDialog class’ execute_script() method returns nil.

Non-working attempt ...
@dlg.define_singleton_method(:get_element_value) {|id|
  self.execute_script("document.getElementById('#{id}').value")
}

Or, you can create your own subclass of UI::HtmlDialog, thus:

module Archetris
  class NiftyDialog < UI::HtmlDialog

    def get_element_value( id )
      self.execute_script(
        "document.getElementById('#{id}').value"
      )
    end

  end
end

EDIT: For a working example see


#3

Thanks, Dan. That’s incredibly helpful.


#4

@Dan: question is though: is it best practice to continue storing values in hidden elements OR just use the callback to send data from JS to Ruby instead?


#5

The storing of values, and whether they are hidden or visible is not really the issue anymore. (You can still have hidden values, stored in hidden html elements, or even better stored in Javascript objects.)

It is the passing of values to the Ruby side is the more important concept.

So yes, best practice going forward will be to use the new sketchup Javascript object, which will convert on the Ruby side to the correct class of Ruby object.
No more chopping up big strings into little strings and then convert the strings to arrays and numbers, etc.

I just showed the above code as a workaround for getting old code to work without a large overhaul.


#6

the method #execute_script is defined to return nil


#7

Yes, and the API docs are never wrong … :rolling_eyes:

If the above does not work,… then try something like this:

EDIT: This does not work either, See below.

(Collapsed as this example will also not work.)
module AuthorTest

  @@loaded ||= false

  class NiftyDialog < UI::HtmlDialog

    def initialize(*args)
      super(*args)
      @ready ||= {}
      @returned_values ||= {}
      add_action_callback('returned_value') {|ac,id,value|
        @returned_values[id]= value
      }
      add_action_callback('element_value_ready') {|ac,id|
        @ready[id]= true
      }
    end

    def get_element_value( id )
      @ready[id]= false
      self.execute_script(
        "sketchup.returned_value('#{id}',document.getElementById('#{id}').value,
          { onCompleted: function() {
              sketchup.element_value_ready('#{id}');
            }
          });"
      )
      until @ready[id]
        sleep(0.1)
      end
      @returned_values[id]
    end

  end # custom HtmlDialog subclass
  
  def self::open
    self::reset()
    
    @dlg = NiftyDialog::new(dialog_title: "Test")
    
    @dlg.set_html('
      <!DOCTYPE html>
      <html>
        <head>
        </head>
        <body>
          <div id="DataField">
            Testing 1, 2, 3, ...
          </div>
        </body>
      </html>
    '
    )
    @dlg.show
    sleep(1.0)
  end

  def self::test
    msg = "Getting the 'DataField' element's value ..."
    msg<< "  value is: \"#{@dlg.get_element_value('DataField')}\""
    puts msg
    UI.messagebox(msg,MB_OK)
  end

  def self::reset
    @dlg.close if @dlg && @dlg.visible?
    @dlg = nil
  end

  if !@@loaded
    sm = UI.menu("Plugins").add_submenu("Get Element Value Test")
    sm.add_item('Open Dialog') { self::open }
    sm.add_item('Test') { self::test }
    sm.add_item('Reset') { self::reset }
    @@loaded = true
  end

end

EDIT: For a working example see


#8

Yep. This is true for the UI::HtmlDialog class. Seems like it used to work for the old UI::WebDialog class.

I just tested and it seems now (with the UI::HtmlDialog class,) there is no way to get an immediate synchronous value of a HtmlDialog element. Control must return to the SketchUp engine before the JS to Ruby callbacks will fire. So then there is no way, that a custom get_element_value() method could ever itself return the value, if it is the instigator of the JS call to send the value to Ruby.

get_element_value_test.rb (2.6 KB)

This is unfortunate as that ol’ get_element_value() method came in very handy.

What’s the workaround ?

It seems that you’ll need to make a call to fire a method on the JS side to collect ALL values that you might wish to test on the Ruby-side, and pass them en masse to Ruby as a array, etc. Or,… you move this kind of immediate comparison expression to the JS side of your code.


#9

EDIT: For an asynchronous working example see