Tool of Thought

APL for the Practical Man

"Vibe coding since 1987"

Working With Components

August 29, 2025

Now that we have a few components, we need some convenient ways to manipulate them. Components are just nodes in the APL DOM with a Name property. Note the uppercase N, not to be confused with the HTML name attribute. Currently the existence of Name in an element is the only thing that identifies it as a component.

We can get a component by name, using the GetComponent function:

GetComponent←{
     ⍝ ⍺ ←→ APL DOM node
     ⍝ ⍵ ←→ Component Name (or already a component (pass through)
     9=⎕NC'⍵':⍵
     e←⍺ GetElementsWith'Name'
     i←e.Name⍳⊂⍵
     i=≢e:6 ⎕SIGNAL⍨'Component "',⍵,'" not found'
     i⊃e
 }

The left argument is a DOM node, perhaps the whole document, or more likely a <dialog> element. the right argument is the name of the component, or if a namespace, assumed to be a component already, and just passed though. This is a convenience for other functions that accept either a component or a component name as an argument. We simply find all the elements in the node with a Name property and pick the one we want.

We can get a namespace with all of the components in a node:

GetComponents←{
     ⍝ ⍵ ← APL DOM node
     ⍝ ⍺ ←→ 0 for namespace result, 1 for array
     ⍝ ← ←→ Namespace of components
     ⍺←0
     e←⍵ GetElementsWith'Name'
     ⍺:e
     0=≢⍵:()
     ()⎕VSET(↑e.Name)e
 }

This allows us to access all the components under a node by name with dot syntax (or get an array of components back).

We can set the value of a component using SetComponentValue:

SetComponentValue←{
     ⍝ ⍺ ←→ APL DOM Node
     ⍝ ⍵ ←→ (Name|Component) Value
     ⍺←⊢
     c←⍺ GetComponent 0⊃⍵
     c(⍎c.class).SetValue 1⊃⍵
 } 

Here the right argument is name or component itself, and the value. If the name is provided, then the left argument must be provided. If the component is provided, the left argument should be omitted. There is no need for a corresponding GetComponentValue function, as the value may be directly accessed with dot syntax.

We also have GetComponentValues and SetComponentValues, which we have introduced before but now renamed to reflect that they work on components. These functions take or return a namespace of values. Finally we have SetComponentDisabled for making a component, in ⎕WC terms, active or inactive:

SetComponentDisabled←{
     ⍝ ⍺ ←→ DOM node
     ⍝ ⍵ ←→ Name or Component name, Boolean 1/0
     ⍺←⊢
     c←⍺ GetComponent 0⊃⍵
     e←Elements c
     d←e/⍨e.Tag∊'button' 'fieldset' 'optgroup' 'option' 'select' 'textarea' 'input'
     0=≢d:0
     0⊣d{⍺ SetBoolean'disabled'⍵}¨1⊃⍵
 }

The explicit list of HTML elements are those that take the disabled attribute.

Let's play around with the functions. Consider a dialog box with a few input fields:

OnFileSettings←{
     d←⍵.Document
     s←A.FieldSet.New''
     i←s A.DropList.New'Separator' 'Separator:' ''('Comma' 'Pipe' 'Tab')
     i←s A.DropList.New'TextQualifier' 'Text qualifier:'('Options'('DoubleQuote' 'Quote')
     i←s A.NumberInput.New'MaxRows' 'Maximum Rows:' 256 'CI15'
     i←s A.CheckBox.New'AutoConvert' 'Auto convert'
     _←d.FileSettings A.InitValues s
     p←⎕NS''
     p.Title←'CSV Import Settings'
     p.Contents←s
     p.OnOK←A.FQP'OnFileSettingsOK'
     b←A.DialogBox.New p
     d A.ShowModal b
 }

When the OnFileSettingsOK callback fires we are given the <dialog> element as an argument. Let's call this d. Then we can grab a component and see its value:

      c←d A.GetComponent 'Separator'
      c.Value
Pipe

Then set its value, updating the APL DOM and the browser:

      A.SetComponentValue c 'Comma' 
0     
      c.Value
Comma

A namepsace of components:

      A.GetComponents d
      p←A.GetComponents d
      p.MaxRows.Value
1024
      p.AutoConvert.Value
1

Get a namespace of values, make some changes, refresh the screen:

      v←A.GetComponentValues d 
      ⎕JSON v
{"AutoConvert":1,"MaxRows":1024,"Separator":"Comma","TextQualifier":"DoubleQuote"}
      v.(AutoConvert MaxRows)←1 1234567
      v A.SetComponentValues d
0
      v←A.GetComponentValues d
      ⎕JSON v
{"AutoConvert":0,"MaxRows":1234567,"Separator":"Pipe","TextQualifier":"Quote"}

An array of components:

      a←1 A.GetComponents d
      a.Name
 Separator  TextQualifier  MaxRows  AutoConvert 
      a.Value
 Pipe  Quote  1234567 0

Deactivate all the components:

      A.SetComponentDisabled¨a,¨1 
0 0 0 0

We have used these verbose functions names, all including the word component, to distinguish them from the functions used to operate on plain elements, as everything sits all together in the #.Abacus.Main namespace. It would be nice to just use properties with real classes and dispense with exposing all these functions, and we may do that in the future. We are going to see how many functions we will need to work with components over the coming months.