Losing connection with HtmlDialog

I’m finalizing work on a new version of my SketchUp IFC Manager extension.
Internationalising it a bit, but most of the work went into a new HtmlDialog.

I tried making the connection between SketchUp and JavaScript as simple as possible.
I don’t use a html template, but generate html with Ruby, because I felt it took too long to wait on the dom to be ready and fill it up afterwards using JavaScript. Re-generating the html on every selection change seems to have a better performance, and I also figured it would be more reliable than keeping the Ruby and JavaScript in sync.
Most of the time it works perfectly, but sometimes the dialog stops sending messages back to Ruby and I get this JavaScript error:

Uncaught TypeError: sketchup.Name is not a function

Somehow the “sketchup” handle in JavaScript is lost.

Screenshot:
dialog

Code:

I hope anyone can give me some tips on reliably using HtmlDialog for such a simple window.

Thanks!
Jan

It is actually a JavaScript object.

I’ll have a look at the files.


BTW … “BimTools” is not really a properly unique toplevel module name.
(Ie, it is very likely that other author(s) or companies could pick the same name.)

Hi Dan,

Thanks for looking at it.
You are right that “sketchup” is an object, handle is probably a wrong term here…
I’ve been using “BimTools” for quite a while, my “company” is called BIM-Tools, I even have “bim-tools.com” as a domain name :wink:
What would you suggest as a proper toplevel module?

Re the object access and “not a function” error:
The UI::HtmlDialog class can loose it’s callback attachments (especially if you close the window and reopen it with the same Ruby object.)

So what I do (usually) is put all the callback definitions within a method I (usually) name “add_callbacks()”. This way you can call the method and reattach the callbacks anytime.

ADD: I also pass the dialog object into this method.

It’s likely that reloading of the html document may break the attachments of the Ruby callbacks. You might add a JavaScript error handler function to detect the “not a function” error, and send a request over to Ruby (hopefully by a still functioning callback) to reattach the callbacks.
Other than that perhaps a Ruby timer that sends “pings” to the dialog every so often and if the dialog does not respond back in a certain amount of time, then the Rubyside goes ahead and reattaches the callbacks.

The only I’ve seen that error myself is when I tried to call a callback from JS that did not have any parameters, and I omitted the parenthesis, ie …

sketchup.close_dialog;

… instead of …

sketchup.close_dialog();
1 Like

Well you cannot use a dash within an identifier, nor a dot (period). They’d have to be omitted or replaced with an underscore.

Perhaps …

module BimToolsDotCom

… or …

module BrouwerBimTools

There are other companies that have software products using the name “bimtools” or “bim tools”, so I think it important to difference your product(s).

Thank you!
I was also thinking along these lines, good to have some confirmation. Seems there are still some issues around the Web/HtmlDialogs.
I also thought about “pinging” it to see if it’s still alive, but wondered if that wasn’t more of a workaround instead of a neat solution.
My idea was to re-attach them on every selection-change(re-draw), and see if that has any performance implications.
Or would you say the pinging solution is preferable?

I don’t know. It is a bit of a hack. It might be the redraws causing it.
We would prefer it didn’t happen. Perhaps open an issue in the API tracker ?

1 Like

For my 2011 bim-tools plugin https://sketchucation.com/pluginstore?pln=bim-tools (more privately developed, but the reason I picked that name) I used “Brewsky::BimTools” but I didn’t want to use “Brewsky” here, but maybe that’s still best…

I keep a variable reference to my dialog in ruby.
I add a block to execute when the dialog is closed.
Then I have a method to show the dialog. If it’s not initialized it does that and adds the callbacks.
Using this method I have had zero issues with JavaScript to Ruby communication and vise versa.

#inside your module namespace
def self.init_spec_dlg
  unless @spec_dlg.nil? 
    @spec_dlg.set_url 'about:blank' 
    @spec_dlg.close
  end
  @spec_dlg = UI::HtmlDialog.new( 
    #initilization stuff
  )
  @spec_dlg.set_on_closed do
    @spec_dlg.close
    @spec_dlg = nil
  end

  #add call backs here...

  @spec_dlg.set_file(@dlg_path_spec)
end


def self.show_specs_dialog
  init_spec_dlg unless @spec_dlg
  return if @spec_dlg.visible?

  @spec_dlg.show
end

Yes I don’t myself have many issues either. For me the “lost callbacks” only occurs if the dialog window is closed and callbacks need to get triggered. Ie, a timer or something doesn’t know the window was closed.

With the old UI::WebDialog we were used to just having the dialog remain closed for awhile, and then have it get opened again and the callbacks would work again. But the newer UI::HtmlDialog class will not do this. (The callbacks must be reattached if the same dialog object is to be reopened., so it is smart to put the callback definitions in a method of their own.)

All that said, … I’ve myself come to the conclusion that building dialog HTML is tedious and slow.
So I’ve switched to using template and template fragments with %{hashkey} replacement parameters. I’ve found it is also quite easy to replace the innerHTML using these template fragments when a list of data needs to be changed. It is much faster and looks better from the end users point of view, than rebuilding and rerendering the entire dialog page.

I also need to say, that I’ve seen (recently) this occur with other devs extensions, when the callback name begins with a capital character. (I myself have always named callbacks using ruby method name conventions. [lower case with underscores as spacing.])

1 Like

I also need to say, that I’ve seen (recently) this occur with other devs extensions, when the callback name begins with a capital character.

Ha! That’s one I can change anyway, if it doesn’t work, it doesn’t hurt…

@Neil_Burkholder Do you recommend to recreate the complete dialog on every change? I update the dialog content with the current selection…

I use JavaScript to update/modify the page. It’s very fast that way. Completely regenerating the html might cause flickering?

1 Like

Looks good!
I used JavaScript to load all the content at first but my experience with waiting for the DOM to be ready before pushing the content wasn’t that good.
How long does it take to appear when opening the dialog for the first time?

Looks like about 425ms from the time the window shows until the html is first rendered. It’s not really a big deal in my case because normally the window stays open all the time.

I load the html from file.

I simply show/hide divs as necessary to show the desired content.

function element(id) { return document.getElementById(id); }

function select_page(page) {
  element('building_specs').style.display = 'none';
  element('shed_specs').style.display = 'none';
  element('window_specs').style.display = 'none';
  element('door_specs').style.display = 'none';
  element('barn_door_specs').style.display = 'none';
  element('ohd_specs').style.display = 'none';
  element('cupola_specs').style.display = 'none';

  switch (page) {
    case 'building':
      element('building_specs').style.display = 'block';
      break;
    case 'shed':
      element('shed_specs').style.display = 'block';
      break;
    case 'window':
      element('window_specs').style.display = 'block';
      break;
    case 'door':
      element('door_specs').style.display = 'block';
      break;
    case 'barn_door':
      element('barn_door_specs').style.display = 'block';
      break;
    case 'ohd':
      element('ohd_specs').style.display = 'block';
      break;
    case 'cupola':
      element('cupola_specs').style.display = 'block';
      break;
  }
}

You also might need to take a look at some javascript state handling mechanisms.
I have used knockout.js in the past when my UI was built in vanilla javascript.

I nowadays use react.js with redux and send state updates from sketchup to javascript.

Anyhow, my workflow has always been: attach a callback to signal the page has been loaded and then execute a global state update.

Sorry for the late post. I had similar select elements in a few html dialogs, and sending an array from SU to JS seemed to work very quickly in terms of loading the array into a select…

I simply show/hide divs as necessary to show the desired content.

That’s a good idea for when you know all options beforehand, but a little more difficult for mine because the content is dependant on changes in the model.