# Question about htmldialog and pushing variables from Ruby to Javascript

**URL:** <https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593>\
**Category:** Ruby API\
**Tags:** javascript, ui::htmldialog\
**Created:** [May 6, 2020, 4:59pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593 "2020-05-06T16:59:17Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![july\_warm](https://avatars.discourse-cdn.com/v4/letter/j/c6cbf5/32.png) [@july\_warm](https://forums.sketchup.com/u/july_warm)\
**Post date:** [May 6, 2020, 4:59pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/1 "2020-05-06T16:59:17Z")

</div>

Just getting into Ruby for SketchUp, and the first tool I’m trying to develop is a mass view exporter, which would require user input to choose a list of scenes to export from, and a dynamic form reading the layer info inside the model. I got the scene exporting part figured out, but the UI is a bit confusing at first given it’s actually html+js, and it’s at its most confusing when I try to figure out how to push for example a list of model layer names as array into Javascript so I can dynamically generate a multi-select form in the html window.

The script below that I wrote kind of works, but it feels very hard coded and I’m posting here to see if I’m understanding `HtmlDialog.add_action_callback` and `HtmlDialog.execute_script` correctly, and if there are far more efficient and elegant ways to do this.

Thank you! ( ~~And also how do you post code in here? There is only blockquote and pre-formatted text~~ Thnx I got it!) And if you have better htmldialog example scripts, please share!

```ruby
module Example
    model = Sketchup.active_model
    model_layers = model.layers
    model_scenes = model.pages
    scene_names = []

    model_scenes.each {|scene| scene_names.push(scene.name)}

    html = <<-EOT
    <p>Choose (multiple) layers</p>
    <select id = "layer_select" name="ok" size="12" multiple>
    </select>

    <script>
    sketchup.views_to_form();
    </script>
    EOT

    options = {
      :dialog_title => "Choose Views to Export",
      :width => 300,
      :height => 400,
      :style => UI::HtmlDialog::STYLE_UTILITY
    }
    dialog = UI::HtmlDialog.new(options)

    dialog.add_action_callback("views_to_form") {|action_context|
      js_command = "var new_text = '';"
      scene_names.each do |name|
        js_command += "new_text += '<option>#{name}</option>';"
      end
      js_command += 'document.getElementById("layer_select").innerHTML = new_text;'
      dialog.execute_script(js_command)
    }

    dialog.set_html(html)
    dialog.center
    dialog.show
end

```

---

<div class="post-metadata">

**Author:** ![john\_mcclenahan](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/john_mcclenahan/32/135377_2.png) [@john\_mcclenahan](https://forums.sketchup.com/u/john_mcclenahan)\
**Post date:** [May 6, 2020, 5:14pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/2 "2020-05-06T17:14:31Z")

</div>

To post code, precede your code on a separate line by three backticks and the word `ruby`

End it, again on a separate line, by three backticks.

````auto
 ```ruby
 ... ruby code here... 

````

` ` `  
three backticks at the end, but I can’t format them here to display properly inside the ruby code block - I used the html code `#96;`to show them and that only works outside the block.

If I type two, they show, but when I type a third, they all disappear.

They are all wrapped in

````nohighlight
```text
...plain text here...

````

` ` `  
Again, I tried to show the three backticks at the end of the `text` wrapper, but only two ticks or none show.

There must be a way to ‘escape’ the backtick characters but I can’t see how. It isn’t either \ or /.

Searched the forum for ‘how to post ruby code’ but nothing relevant came up in the dozen or so results.

---

<div class="post-metadata">

**Author:** ![McGordon](https://avatars.discourse-cdn.com/v4/letter/m/e5b9ba/32.png) [@McGordon](https://forums.sketchup.com/u/McGordon)\
**Post date:** [May 6, 2020, 5:38pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/3 "2020-05-06T17:38:29Z")

</div>

> [@july\_warm](#):
>
> And also how do you post code in here?

```ruby  
module YourCode  
# Your code here  
end  
```  
Gives this:

```ruby
module YourCode
   # Your code here
end

```

So your (unchanged) code would look like:

```ruby
model = Sketchup.active_model
model_layers = model.layers
model_scenes = model.pages
scene_names = []

model_scenes.each {|scene| scene_names.push(scene.name)}

html = <<-EOT
<p>Choose (multiple) layers</p>
<select id = "layer_select" name="ok" size="12" multiple>
</select>

<script>
sketchup.views_to_form();
</script>
EOT

options = {
  :dialog_title => "Choose Views to Export",
  :width => 300,
  :height => 400,
  :style => UI::HtmlDialog::STYLE_UTILITY
}
dialog = UI::HtmlDialog.new(options)

dialog.add_action_callback("views_to_form") {|action_context|
  js_command = "var new_text = '';"
  scene_names.each do |name|
    js_command += "new_text += '<option>#{name}</option>';"
  end
  js_command += 'document.getElementById("layer_select").innerHTML = new_text;'
  dialog.execute_script(js_command)
}

dialog.set_html(html)
dialog.center
dialog.show

```

---

<div class="post-metadata">

**Author:** ![McGordon](https://avatars.discourse-cdn.com/v4/letter/m/e5b9ba/32.png) [@McGordon](https://forums.sketchup.com/u/McGordon)\
**Post date:** [May 6, 2020, 5:42pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/4 "2020-05-06T17:42:27Z")

</div>

> [@john\_mcclenahan](#):
>
> Again, I tried to show the three backticks at the end of the `text` wrapper, but only two ticks or none show.
> 
> There must be a way to ‘escape’ the backtick characters but I can’t see how. It isn’t either \ or /.

You have to escape each backtick individually  
\`\`\`ruby  
module YourCode  
# Your code here  
end  
\`\`\`

To show this  
```ruby  
module YourCode  
# Your code here  
end  
```

to show the first one here you have to escape the backslashes too.

---

<div class="post-metadata">

**Author:** ![july\_warm](https://avatars.discourse-cdn.com/v4/letter/j/c6cbf5/32.png) [@july\_warm](https://forums.sketchup.com/u/july_warm)\
**Post date:** [May 6, 2020, 6:00pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/5 "2020-05-06T18:00:53Z")

</div>

Thank you and where did you find information like this? Also trying to figure out how to strike through texts.  
Also do you have any insight on the main question of my post?

---

<div class="post-metadata">

**Author:** ![McGordon](https://avatars.discourse-cdn.com/v4/letter/m/e5b9ba/32.png) [@McGordon](https://forums.sketchup.com/u/McGordon)\
**Post date:** [May 6, 2020, 6:04pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/6 "2020-05-06T18:04:01Z")

</div>

> [@july\_warm](#):
>
> Thank you and where did you find information like this? Also trying to figure out how to strike through texts.  
> Also do you have any insight on the main question of my post?

> [@\[How to\] Post correctly formatted and colorized code on the forum?](https://forums.sketchup.com/t/how-to-colourize-code-on-the-forum-wiki/46189):
>
> Continuing the discussion from [Ruby backtick returns nothing!](https://forums.sketchup.com/t/ruby-backtick-returns-nothing/46120/17): Triple backtick delimiter lines. [What is a backquote ?](https://www.computerhope.com/jargon/b/backquot.htm) [image] Backquote character key on US keyboard. If it is error message text (or some other plain text that contains \<, \>, #, or other characters that can confuse Discourse markdown,) then specify “text” as a lexing language: ```text paste error message here ``` If it is Ruby, then: ```ruby # code here ``` If it is Javascript, then: ```javascript /\* js…

The escaping was trial and error.

I’m no expert on using HtmlDialog.

~~Strikethrough~~ with two tildes before and after like this:  
~~Strikethrough~~

---

<div class="post-metadata">

**Author:** ![DanRathbun](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/danrathbun/32/500_2.png) [@DanRathbun](https://forums.sketchup.com/u/DanRathbun)\
**Post date:** [May 6, 2020, 10:26pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/7 "2020-05-06T22:26:22Z")

</div>

> [@july\_warm](#):
>
> Also do you have any insight on the main question of my post?

There are 2 options for getting the arrays over to the JS.

Use JSON (which describes as JavaScript object and can therefore be used to define a JS object.)  
It is likely that SketchUp’s native extensions have already loaded Ruby’s JSON library, but just to be sure you should add a dependency require ta the top of your file …

```ruby
require 'json'

```

- [Module: JSON (Ruby 2.5.5)](https://ruby-doc.org/stdlib-2.5.5/libdoc/json/rdoc/JSON.html)

**a.** Send it when the page is ready via a callback …

```ruby
dialog.add_action_callback("views_to_form") {|action_context|
  json_text = Sketchup.active_model.pages.map(&:name).to_json
  dialog.execute_script("var scenes = #{json_text}; load_scene_select();")
}

```

… or …

```ruby
dialog.add_action_callback("layers_to_form") {|action_context|
  json_text = Sketchup.active_model.layers.map(&:name).to_json
  dialog.execute_script("var layers = #{json_text}; load_layer_select();")
}

```

The examples assume a couple of JS functions to load the array values once on the JS side into the appropriate `<select>` HMTL element.

**ADD** : Also note that these examples are using the [`#map`](https://ruby-doc.org/core-2.5.5/Enumerable.html#method-i-map) method which comes from most of the API collection classes mixing in the Ruby core [`Enumerable`](https://ruby-doc.org/core-2.5.5/Enumerable.html) module. (At the top of the API class doc pages will be a “ **Includes** ” box listing any libraries mixed into the class.)

* * *

**b.** But, as you are actually creating the HTML for the dialog dynamically on the Ruby side, you can just stuff the values into the text, before setting the HTML for the `dialog` object.

This way leverages Ruby String interpolation. _See primer:_  
[https://ruby-doc.org/core-2.5.5/doc/syntax/literals\_rdoc.html#label-Strings](https://ruby-doc.org/core-2.5.5/doc/syntax/literals_rdoc.html#label-Strings)

```ruby
    # Map the active model's layer collection to an array of HTML option elements:
    layers_options = Sketchup.active_model.layers.map do |layer| # << -- fixed
      name = layer.name # << -- fixed
      %[<option value="#{name}">#{name}</option>]
    end
    # Join the array of options into a newline separated text string:
    layers_html = layers_options.join("\n")

    html = <<-EOT
    <!DOCTYPE html>
    <html>
    <body>
        <p>Choose (multiple) layers</p>
        <select id = "layer_select" name="ok" size="12" multiple>
            #{layers_html}
        </select>

        <script>
            sketchup.views_to_form();
        </script>
    </body>
    </html>
    EOT

```

Yes, that is right. HereDocs can use String interpolation.

* * *

All this aside … your example module is written in a sequential script-like way.

SketchUp embedded Ruby is event-driven. So you’d have a mneu item or toolbar button that will fire off a Ruby method that will build and display your dialog.

You do not use local variables inside modules or classes. If you need persistence, use @vars.  
Otherwise you pass values between methods as arguemnts.

The references for the active model and it’s collections are **never** set in stone as you have done.  
They are only referenced at the last second after the user has chosen a command. You need to realize that on the Mac it’s a multi-document interface where the user can switch back and forth between numerous open models.

---

<div class="post-metadata">

**Author:** ![McGordon](https://avatars.discourse-cdn.com/v4/letter/m/e5b9ba/32.png) [@McGordon](https://forums.sketchup.com/u/McGordon)\
**Post date:** [May 6, 2020, 10:36pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/8 "2020-05-06T22:36:54Z")

</div>

> [@DanRathbun](#):
>
> Yes, that is right. HereDocs can use String interpolation.

Nice, I didn’t know that.

---

<div class="post-metadata">

**Author:** ![DanRathbun](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/danrathbun/32/500_2.png) [@DanRathbun](https://forums.sketchup.com/u/DanRathbun)\
**Post date:** [May 6, 2020, 10:43pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/9 "2020-05-06T22:43:22Z")

</div>

Yea, they are basically a big multiline double quoted string.

But I don’t like the delimiter format myself, (forget how to use it usually) … so I just use a multiline % delimited string like …

```ruby
    html = %[
    <!DOCTYPE html>
    <html>
    <body>
        <p>Choose (multiple) layers</p>
        <select id = "layer_select" name="ok" size="12" multiple>
            #{layers_html}
        </select>

        <script>
            sketchup.views_to_form();
        </script>
    </body>
    </html>
    ]

```

---

<div class="post-metadata">

**Author:** ![july\_warm](https://avatars.discourse-cdn.com/v4/letter/j/c6cbf5/32.png) [@july\_warm](https://forums.sketchup.com/u/july_warm)\
**Post date:** [May 6, 2020, 10:56pm UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/10 "2020-05-06T22:56:46Z")

</div>

these are very clear and helpful, especially the string interpolation used in HereDocs, that does feels much better.  
Your last comment is very valid, I imagine this script will not work well if the user creates a new layer after the script has already started running.  
Not sure how the Mac system affect all of these yet, will have to do some research on that.  
Thank you!

---

<div class="post-metadata">

**Author:** ![slbaumgartner](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/slbaumgartner/32/7302_2.png) [@slbaumgartner](https://forums.sketchup.com/u/slbaumgartner)\
**Post date:** [May 7, 2020, 12:01am UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/11 "2020-05-07T00:01:04Z")

</div>

> [@july\_warm](#):
>
> Not sure how the Mac system affect all of these yet, will have to do some research on that.

I didn’t notice anything in @DanRathbun’s suggestions that should be OS dependent or sensitive. Of course, those are famous last words so it should be checked.

---

<div class="post-metadata">

**Author:** ![july\_warm](https://avatars.discourse-cdn.com/v4/letter/j/c6cbf5/32.png) [@july\_warm](https://forums.sketchup.com/u/july_warm)\
**Post date:** [May 7, 2020, 12:20am UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/12 "2020-05-07T00:20:42Z")

</div>

> [@DanRathbun](#):
>
> ```ruby
> layers_options = Sketchup.active_model.layers.map do |name| 
> %[<option value="#{name}">#{name}</option>]
> end
> 
> ```

am I understanding correctly that this here should actually be like below, or does the above also work?

```ruby
layers_options = Sketchup.active_model.layers.map do |layer|
  %[<option value="#{layer.name}">#{layer.name}</option>]
end

```

---

<div class="post-metadata">

**Author:** ![MSP\_Greg](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/msp_greg/32/206066_2.png) [@MSP\_Greg](https://forums.sketchup.com/u/MSP_Greg)\
**Post date:** [May 7, 2020, 1:32am UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/13 "2020-05-07T01:32:00Z")

</div>

> [@july\_warm](#):
>
> I imagine this script will not work well if the user creates a new layer after the script has already started running.

Dialogs are messy, and what is done on the Ruby side vs what is done on the JS side depends on what you’re familiar with. You can use `dialog.execute_script` to call a JS function that loads whatever multi-select element you’re using in the html. Typically, this would be using DOM JS functions.

I’ve used arrays of arrays (using `#inspect`) as the JS function argument to generate tables with checkboxes, color spans, and the text/name. Hence, you may not even have to pass as JSON.

The advantage to using DOM is that a non-modal dialog can be updated. You can also pass a doc html fragment and parse it in JS, but that seems tedious.

Also, not sure about current SU, but `dialog.execute_script` may have limits on the string length, and passing an html fragment adds to the string length.

---

<div class="post-metadata">

**Author:** ![DanRathbun](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/danrathbun/32/500_2.png) [@DanRathbun](https://forums.sketchup.com/u/DanRathbun)\
**Post date:** [May 7, 2020, 2:01am UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/14 "2020-05-07T02:01:20Z")

</div>

> [@july\_warm](#):
>
> am I understanding correctly that this here should actually be like below, or does the above also work?
> 
> ```auto
> layers_options = Sketchup.active_model.layers.map do |layer|
> %[<option value="#{layer.name}">#{layer.name}</option>]
> end
> 
> ```

YES! 😊 (Brain fartitis) And no, I made a mistake.

I was thinking at first to map to an array of layer names, then do a loop stuffing the names into the HTML strings. (That’s twice today I was pondering two different ways, and wound up expressing a mixture that doesn’t work. Must be the meds I’m on. 🙄)

You’ll find that Ruby is multi-paradigm. There’s many ways to do a task. For example …

```ruby
layers_options = Sketchup.active_model.layers.map(&:name).map do |name| 
  %[<option value="#{name}">#{name}</option>]
end

```

… or …

```ruby
layers_options = Sketchup.active_model.layers.map do |layer|
  name = layer.name
  %[<option value="#{name}">#{name}</option>]
end

```

Ie, the value used in the mapped output array, is the last evaluated value from the block.

---

<div class="post-metadata">

**Author:** ![DanRathbun](https://sea2.discourse-cdn.com/sketchup/user_avatar/forums.sketchup.com/danrathbun/32/500_2.png) [@DanRathbun](https://forums.sketchup.com/u/DanRathbun)\
**Post date:** [May 7, 2020, 2:24am UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/15 "2020-05-07T02:24:23Z")

</div>

> [@MSP\_Greg](#):
>
> You can use `dialog.execute_script` to call a JS function that loads whatever multi-select element you’re using in the html. Typically, this would be using DOM JS functions.

This what I was referring to in **(a) above** …

> [@DanRathbun](#):
>
> ```auto
> dialog.add_action_callback("layers_to_form") {|action_context|
> json_text = Sketchup.active_model.layers.map(&:name).to_json
> dialog.execute_script("var layers = #{json_text}; load_layer_select();")
> }
> 
> ```
> 
> **The examples assume a couple of JS functions to load the array values once on the JS side into the appropriate `<select>` HMTL element.**

The JS function `"load_layer_select()"` would be the function using DOM to populate the selection elements.

* * *

> [@july\_warm](#):
>
> these are very clear and helpful, especially the string interpolation used in HereDocs, that does feels much better.

You may find some JS framework that builds a nice interface for you, which might require the selection values to be in a JS array. Although I showed stuffing the values into the HTML `<option>` elements, you could also stuff the array as JSON into the `<script>` element in prep for use by some JS GUI framework.  
Ex …

```ruby
json_layers = Sketchup.active_model.layers.map(&:name).to_json

# Down in the HEREDOC ...

    html = <<-EOT
    <!DOCTYPE html>
    <html>
    <body>
        <p>Choose (multiple) layers</p>
        <select id = "layer_select" name="ok" size="12" multiple>
        </select>

        <script>
            var layer_names = #{json_layers};
            // Then call some JS framework to bind this array to the
            // #layer_select element as child option elements.
        </script>
    </body>
    </html>
    EOT

```

Some frameworks require the data array(s) to be wrapped within an object (instead of at the top level as shown.) But this is not any more difficult.

---

<div class="post-metadata">

**Author:** ![july\_warm](https://avatars.discourse-cdn.com/v4/letter/j/c6cbf5/32.png) [@july\_warm](https://forums.sketchup.com/u/july_warm)\
**Post date:** [May 7, 2020, 4:14am UTC](https://forums.sketchup.com/t/question-about-htmldialog-and-pushing-variables-from-ruby-to-javascript/124593/16 "2020-05-07T04:14:33Z")

</div>

I was just reading about & shorthand, great to look at a SU example here. I did not know shorthand can be further chained, this is really cool!
