El nuevo control de navegador de internet que Microsoft ha incluido en Access actualiza el anterior y está basado en Edge. Incorpora un par de métodos muy útiles e interesantes para interactuar con su contenido mediante VBA que son ExecuteJavascript y RetrieveJavascriptValue (parece un control derivado y limitado respecto al WebView2 en Visual Studio).
Existe bastante información en internet, por ejemplo, muy completa en https://www.devhut.net/everything-you-never-wanted-to-know-about-the-access-modern-web-browser-control/.
Sin embargo los métodos para ejecutar Javascript (ExecuteJavascript) en el navegador y poder disponer en Access de resultados del navegador (RetrieveJavascriptValue) tienen alguna peculiaridad relacionada con la ejecución asíncrona y la carga o actualización de la página web del navegador en el momento que se están automatizando tareas desde Access. En particular con la espera a que cargue la página para que los resultados al ejecutar Javascript u obtener valores sea el esperado.
No es tan sencillo como con el control anterior usando las propiedades Busy y ReadyState del control, pues la primera no existe y la segunda no se comporta como se espera. Hace falta además usar el evento DocumentComplete.
El problema que ocurre es que cuando se trata de automatizar una navegación web desde Access con VBA, por ejemplo pulsando clicks (ExecuteJavascript) y una vez que se ejecuta ese click obtener ciertos resultados (RetrieveJavascriptValue), resulta que puede ocurrir (y ocurre) que la instrucción RetrieveJavascriptValue se ejecuta antes de que la web se haya actualizado y no se obtienen los resultados esperados. No basta en este caso con usar una espera basada en la propiedad ReadyState, no funciona…
Un ejemplo de caso práctico podría ser una web de este estilo, tipo tabla con diferentes botones o enlaces, que al pulsarlos abren otra página o bien cargan algo en la página. Se trata de automatizar los clicks y obtener ciertos valores deseados una vez se haya ejecutado la acción de cada click (por ejemplo obtener todo el contenido html que se haya generado al hacer click y tratarlo de alguna manera)
Un posible código que uno esperaría que funcionase sería el siguiente (pero no funciona):
'EdgeBrowser es el nombre del control navegador Dim js As String Dim contenidoHtml as String For i=1 To 4 js="document.getElementById('click_" & i & "').click();" EdgeBrowser.ExecuteJavascript js 'esperar a que el navegador acabe Do While EdgeBrowser.ReadyState <> 4 DoEvents Loop 'obtener todo el Html para hacer algo contenidoHtml = EdgeBrowser.RetrieveJavascriptValue("document.getElementsByTagName('html')[0].outerHTML") ' usar contenidoHtml.... Next i
Pero no va a funcionar como uno espera, pues de esa manera no se espera a que se complete el Javascript ejecutado y lo que obtendremos con RetrieveJavascriptValue no es lo que queremos.
Solución
Tras muchas, muchas pruebas, parece que lo que hay que usar es el evento DocumentComplete del navegador y esperar a que este evento se produzca para poder asegurarse de que el Javascript que hemos pedido que se ejecute ha finalizado. Por ello una posible forma es mediante el uso de una variable global (de tipo boolean) que indicará si este evento del navegador se ha producido o no. Por tanto el código anterior modificado para que funcione sería el siguiente:
Option Compare Database Dim gContinue As Boolean
'EdgeBrowser es el nombre del control navegador Dim js As String Dim contenidoHtml as String For i=1 To 4 js="document.getElementById('click_" & i & "').click();" gContinue = False EdgeBrowser.ExecuteJavascript js 'esperar a que el navegador nos diga que ha completado Do While gContinue = False DoEvents Loop 'obtener todo el Html para hacer algo contenidoHtml = EdgeBrowser.RetrieveJavascriptValue("document.getElementsByTagName('html')[0].outerHTML") ' usar contenidoHtml.... Next i
Private Sub EdgeBrowser_DocumentComplete(url As Variant) ' actualizar el valor de la variable global para que las esperas finalicen gContinue = True End Sub
De esa manera sí nos aseguramos que lo que la web hace al pulsar el click se ha ejecutado y entonces podemos obtener ya lo que queramos. Por tanto cada vez que queramos ejecutar Javascript en el navegador con ExecuteJavascript tendremos que inicializar la variable global de espera a False y hacer una espera hasta que sea True (puede simplificarse creando alguna función o rutina para ello)
Nota sobre el evento Navigate y la espera a que el navegador acabe
La verdad es que la documentación de este nuevo control no es muy detallada. En las pruebas que he realizado resulta que para el evento Navigate del control navegador, que se usa para navegar a una dirección determinada, la espera a que el navegador complete la carga sí hay que hacerla con la propiedad ReadyState para evitar comportamientos erráticos.
Por ejemplo:
url = "https://abrazalaweb.net" EdgeBrowser.Navigate url ' esperar al navegador Do While EdgeBrowser.ReadyState <> 4 DoEvents Loop
Cuando se navega en la página de PowerBi mediante el navegador (Me.EdgeBrowser0.Navigate «https://app.powerbi.com») Sale el siguiente error:
«No tiene permisos de acceso para ver esta página.
DETALLES TÉCNICOS
Power BI no ha podido leer los metadatos de la aplicación. Compruebe el estado del servicio Power BI en la página de soporte técnico siguiente y vuelva a intentarlo más tarde.
Fecha y hora: 2023-12-23 19:57:47Z
Id. de la actividad: 8f4a0d2d-51da-44e7-b126-f90fc4c1a0e8
Id. de la solicitud: 97f89348-ae92-4b57-8ec1-6d88dac8d42a»
Licencias utilizadas:
. Microsoft 365 Familia
. Power BI PRO (Pagada para un usuario).
SQL Server 2019 Express (la versión gratuita)
. Computador: ASUS ZenBook 13, RAM 16 GB.
Sistema operativo: Windows 10 Pro, 64 Bits, Versión 22H2
Eso parece un problema con los servidores de PowerBi, buscando en inglés hay muchas páginas en internet que lo reportan, p. ej. https://community.fabric.microsoft.com/t5/Issues/Power-BI-was-unable-to-read-the-application-metadata-Please/idi-p/3535586
Intenta desde otro navegador, o prueba de nuevo. Yo he hecho una prueba y funciona sin problemas:
Entendido, gracias Sr por su colaboración. Seguiré sus indicaciones.