I'm trying to use VBA to select a dropdown item from an HTML website that uses ReactJS. For this example, we can use the following website:
https://jedwatson.github.io/react-select/
<span class="Select-value-label" role="option" aria-selected="true" id="react-select-2--value-item">New South Wales</span>
If an HTML page lists all the select options on the dropdown, I can easily set the elementID to one of the dropdown values.
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = True
.Navigate "about:blank"
'with for page load
ieWaitForURL
.Navigate "https://jedwatson.github.io/react-select/"
ie.Document.getelementbyid("react-select-2--value-item").Value = "Victoria"
But the HTML of the ReactJS website doesn't list all the options of the dropdown, and the value of the innertext changes as I make a different selection.
Is there a way to select from a ReactJS dropdown using VBA if all the options aren't listed in the HTML?
It was actually a lot easier than I thought. The following uses selenium basic. Install selenium basic, ensure latest chromedriver.exe is in selenium folder, vbe > tools > references > add reference to selenium type library
I show grabbing all the option values into a dictionary. Also, selecting an item from the dropdown.
The key here is that the option menu is not a traditional select element, with child options, but uses React Select. The range of possible values are pulled via Ajax from this script.
I show how you could also retrieve the possible values from that script direct, at the end, using python, but am happy to translate to vba if you really are interested. Once the dropdown is clicked the list of available values can be collected.
If you want to go down the IE route you can use the same approach but need to trigger the events that will open the dropdown. These are also detailed in the js
script I think.
Option Explicit
Public Sub MakeSelection()
Dim d As WebDriver, i As Long, dropDownOptions As Object
Const URL = "https://jedwatson.github.io/react-select/"
Set d = New ChromeDriver
Set dropDownOptions = CreateObject("Scripting.Dictionary")
With d
.Start "Chrome"
.get URL
.FindElementByCss("button:nth-of-type(2)").Click
.FindElementByCss(".Select-arrow-zone").ClickAndHold
Dim item As Object
For Each item In .FindElementsByCss(".Select-menu div") 'put list of options in dictionary
dropDownOptions(item.Text) = i
i = i + 1
Next
For Each item In .FindElementsByCss(".Select-menu div") 'loop to select an option
If item.Text = "Victoria" Then 'If item.Text = dropDownOptions.item(3) etc....
item.Click
Exit For
End If
Next
Stop
.Quit
End With
End Sub
Python script to parse possible dropdown values from json:
This shows the 3 different element parts which are updated through the dropdown (labels, classes and values)
import requests
import re
import json
r = requests.get('https://jedwatson.github.io/react-select/app.js')
s = str(r.content)
p1 = re.compile('t\.AU=(.*)')
p2 = re.compile('.+?(?=,\[\d+\])')
data1 = re.findall(p1, s)[0]
data2 = re.findall(p2, data1)[0].replace(',disabled:!0','')
replacements = ['value:','label:','className:']
for item in replacements:
data2 = re.sub(item, '"' + item[:-1] + '":' , data2)
finals = data2.split(',t.US=')
finalAus = json.loads(finals[0])
# finalUs = json.loads(finals[1])
d = {}
i = 0
for item in finalAus:
d[item['label']] = i
# item['label']
# item['value']
# item['className']
i+=1
print(d)
Related
I am trying to click a link within an unordered list. The unordered list is within frames and I am not exactly sure of the frame name, so I used a recursive search (code obtained from this forum),
Dim elem2 As Object
Set elem2 = FindInputByName(ie.document, "0/2")
If Not elem2 Is Nothing Then
elem2.Click 'THIS IS NOT WORKING
End If
Function FindInputByName(document As Object, name As String) As Object
Dim i As Integer, subdocument As Object, elem As Variant
Set FindInputByName = Nothing
For i = 0 To document.frames.Length - 1
Set subdocument = document.frames.Item(i).document
Set FindInputByName = FindInputByName(subdocument, name)
If Not FindInputByName Is Nothing Then Exit Function
Next i
For Each elem In document.getElementsByTagName("a")
If elem.ID = name Then
Set FindInputByName = elem
Exit Function
End If
Next elem
End Function
Using this code no 'click' is carried out.
Instead of click, I tried elem2.Focus elem2.FireEvent ("tree[i].onclick"), then the link gets selected but there is no click again.
the html snippet is,
<a id="0/2" style="padding-left: 13px;" href="#">GENERAL INFORMATION</a>
But the element has a click event 'tree[i].onclick' . So what should I do to click the link?
Thanks in advance.
After adding 'application.wait', on click is getting executed
I am trying to problematically change the dropdown selection on an InternetExplorer.Application then save that selection. The code I have so far is
dim myValue
myValue="3"
for j = 0 to obj.Options.length - 1
if(obj.Options(j).Value = myValue) then
obj.Options(j).selected = true
exit for
end if
next
This works on the current pages dropdown list, however when I click save, the value "3" isn't saved and it reverts back to its original value when I reload the page.
Another thing to mention is that when I manually click the dropdown and select a value then save, it does update to the new value when I reload the page. I have tried the obj.click function on it but I do not believe a programmatic mouse click works like a actual mouse click with the action listener.
My guess would be something to do with the databinding between the new value selection and the action listener for the page. I am fairly new to vbscript and have tried all sorts of different things.
Any help would be very much appreciated. Thank You!
Supposing you have the obj object set properly, e.g. something like
set obj = ie.document.getElementById("my_dropdown") then you should ensure that only one option is selected:
for j = 0 to obj.Options.length - 1
if (obj.Options(j).Value = myValue) then
obj.Options(j).selected = true ''' do not exit for
else
obj.Options(j).selected = false
end if
next
or
For Each opt In obj.Options
If opt.Value = myValue Then
opt.Selected = True
Else
opt.Selected = False
End If
Next
Caution: above code snippet could result to (undesired?) case that no option remains selected!
I have a column of hyperlinks in an Excel file and I want to convert them to their respective HTML code:
Link Name
I found ways to extract the link only (as text), but I need the whole HTML code as text to replace the hyperlink in the cell.
I've searched and searched but no one needed this answer, I guess. Can someone help?
It is actually a fairly straightforward method to yank the .Address and optional .SubAddress from the Hyperlinks collection object. The .TextToDisplay property is simply the value or text of the cell.
Sub html_anchors()
Dim a As Range, u As String, l As String
Dim sANCHOR As String: sANCHOR = "%L%"
For Each a In Selection
With a
If CBool(.Hyperlinks.Count) Then
l = .Text
u = .Hyperlinks(1).Address
If Right(u, 1) = Chr(47) Then u = Left(u, Len(u) - 1)
.Hyperlinks(1).Delete
.Value = Replace(Replace(sANCHOR, "%U%", u), "%L%", l)
End If
End With
Next a
End Sub
Select all of the cells you want to process and run the routine. If any cell in your selection does not contain a hyperlink, it will be ignored.
I am having issues trying to use an option box to change a font on a series of controls. Basically, I have a report (named Q_tblProject52Week) embedded in a form. I have embedded an option box within the form (called "cornice33) which aims to change the font on the two controls (testo107 and testo108) embedded in the report.
At the moment I am trying the following with no success:
If Cornice33 = 1 Then
testo107.FontName = "calibri"
testo108.FontName = "times"
ElseIf Cornice33 = 2 Then
testo107.FontName = "times"
testo108.FontName = "calibri"
End If
I am getting a missing object message (it is not recognising the controls testo107 and testo108). Also important to note, the report is embedded in a folder control.
You need to tell access that you refer to a control on the report which is a child of your form.
If Cornice33 = 1 Then
Me.Q_tblProject52Week.testo107.FontName = "calibri"
Me.Q_tblProject52Week.testo108.FontName = "times"
ElseIf Cornice33 = 2 Then
Me.Q_tblProject52Week.testo107.FontName = "times"
Me.Q_tblProject52Week.testo108.FontName = "calibri"
End If
hope this helps
Is there a way in arcobjects to get a unique id for a layer? If you do a search by layer name there could be possible duplicates.
If there isn't a property is there a way to generate an id?
I tried using the GetHash() but that didn't stay consistent.
There is an ArcObjects Interface present for setting or getting an Id for a layer.
You should look at ILayerDescriptor:ID,
http://resources.esri.com/help/9.3/ArcGISDesktop/ArcObjects/esriCarto/ILayerDescriptor_ID.htm
Here is a VBA Snippet which shows how it can be used:
Public Sub layerInfo()
Dim app As IApplication '
Set app = Application
Dim mxDoc As IMxDocument
Set mxDoc = app.Document
Dim myMap As IMap
Set myMap = mxDoc.ActiveView
Dim mapServer As IMxdServer
Set mapServer = New MxdServer
'''Point to your .mxd...
mapServer.Start ("D:\Test.mxd")
Dim myArray As IArray
Set myArray = mapServer.LayerDescriptors(myMap.Name)
MsgBox myArray.Count
Dim x As ILayerDescriptor
Dim intX As Integer
intX = 0
For intX = 0 To myArray.Count - 1
Set x = myArray.Element(intX)
MsgBox x.ID
MsgBox x.Name
Next
End Sub
It isn't pretty, but in the past I've appended a guid in the layer description. Something like this:
<LAYER guid='a9843c88-3caa-4953-ad96-ca9990b410e9' revision='1' />
I've got a DLL floating around that would slam these xml frags into each layer of an MXD (with enough cr/lf in front to scroll the xml fragment out of the layer description in ArcMap Layer Prop dialog) .
There's a help file in the 7z file (documentation is sparse because I'm doing other things):
http://code.google.com/p/umbriel/downloads/list
I like the idea of using a GUID. This can then be stored in the ModelName property which is a tool for developers of custom objects to use to guarantee the names of objects independent of the true name or alias name.
There are more details and sample code at http://geographika.co.uk/?p=58
Easy. A side effect of using COM and because how the vtables are laid out, is that you can use the memory address of the layer itself as your unique identifier. Inside the implementation of many ESRI GeoDatabase and Carto code itself, this trick is being used all over the place.