Accessing Javascript
Interaction with Javascript objects in Anvil is primarily done through the anvil.js
module.
This reference provides some details about working with Javascript objects from Python code.
Check out the Quickstart guide for an introduction applied to an external library.
HTML Forms also have a unique way to interact with their HTML Template Javascript functions described here.
Accessing the window
You can access any Javascript function or variable by importing it from the window
object. The window
object can be thought of as the Javascript global namespace in the browser where your app runs.
from anvil.js.window import Foo
# Foo is a Javascript object defined in the Javascript namespace
Accessing a DOM Node
You can access the DOM node for any Anvil Component in Python
anvil.js.get_dom_node(component)
# access any anvil component's DOM node
Return values from anvil.js
When using anvil.js
, Javascript objects will be returned as Python objects. Javascript strings, numbers, arrays, booleans, null
, and undefined
are mapped to their Python equivalents.
All other Javascript objects (including dictionary-like objects) will be returned as proxyobjects. Proxyobjects are Python objects that wrap a Javascript object and allow you to interact with the underlying Javascript object from Python code.
from anvil.js.window import Foo
print(type(Foo)) # <class 'Proxy'>
Return values from anvil.js.get_dom_node
are also proxyobjects.
dom_node = anvil.js.get_dom_node(self.content_panel)
print(dom_node) # <HTMLDivElement proxyobject>
As is the window
object!
print(anvil.js.window) # <Window proxyobject>
Working with proxyobjects
Attributes
You can access the attributes on the underlying Javascript object in Python as you would in Javascript.
Foo.bar # use . notation
Foo['bar'] # use [] subscript notation
Javascript attributes will be returned to Python in the same way as described above i.e. Javascript strings, numbers, arrays, booleans, null
, and undefined
will be returned as their Python equivalents. All other Javascript objects will be returned as proxyobjects.
Calling dir()
on a proxyobject, e.g. dir(Foo)
, will give an indication of the available attributes accessible in Python. Only Javascript enumerable properties of the underlying Javascript object will be returned when calling dir()
.
All proxyobjects have a keys()
method e.g. Foo.keys()
. Calling keys()
returns a list of strings, which represents the underlying Javascript object’s own property names. It is the equivalent in Javascript of doing Object.keys(js_object)
All proxyobjects have a get()
method e.g. Foo.get('bar', None)
. The default value will be returned if the attribute is not found. If no default value is provided then None
will be used as the default value.
Iteration
If an object is iterable in Javascript then it will be iterable in Python.
A Javascript object is only iterable if it has a Symbol.iterator
symbol.
from anvil.js.window import jQuery
for element in jQuery('div'):
# jQuery objects are iterable in Javascript
# and therefore iterable in Python
print(element)
Typically proxyobjects are not iterable - but you can iterate over the keys.
for key in proxy_obj.keys():
val = proxy_obj[key]
__class__
Accessing .__class__
of a proxyobject will return the constructor of the underlying Javascript object.
x = Foo()
print(x) # <Foo proxyobject>
print(x.__class__) # <proxyclass 'Foo'> (the Javascript constructor)
print(type(x)) # <class 'Proxy'>
True
or False
All proxyobjects are considered ‘Truthy’ (i.e. bool(proxy_obj)
will be True
) unless:
- the underlying Javascript object is empty:
{}
- the proxyobject can be called with
len
andlen(proxy_obj) == 0
len()
You can call len()
on a proxyobject if the underlying Javascript object is not a function and has a .length
property.
from anvil.js.window import jQuery
print(len(jQuery('div')))
# jQuery objects have .length property and can be called with len()
Calling proxyobjects
If the underlying Javascript object is callable, then the proxyobject is callable in Python.
Kwargs will not work when calling proxyobjects since kwargs are not supported in Javascript.
When calling a proxyobject:
- the arguments will be converted to Javascript,
- the underlying Javascript function will be called with those arguments
- the return value will be converted to Python.
If you send a Python function to a proxyobject as a callable, the reverse is true. See the Quickstart guide for an example.
*Implementation Detail: If a proxyobject is called and the underlying Javascript function returns a Promise, then the Python execution will pause until the Promise resolves before continuing the execution. See additional notes on asynchronous Javascript.*
You can know if a proxyobject is callable by calling print()
on the proxyobject or by using callable()
.
from anvil.js.window import Foo
print(Foo)
# <proxyclass 'Foo'>, or
# <proxyfunction 'Foo'>, or
# <bound proxymethod 'Foo' of <Window proxyobject>>
print(callable(Foo))
# True
Calling with new
Some libraries will include constructors that require the use of the new operator.
When calling a proxyobject, Python will infer whether new
should be used based on the context and the underlying Javascript object being called.
If you want to control when the new
operator is used, you can use anvil.js.new
or anvil.js.call
.
from anvil.js.window import Foo
import anvil.js
anvil.js.new(Foo, 1, 'a') # Foo will be called with the new keyword
anvil.js.call(Foo, 1, 'a') # Foo will be called without the new keyword
anvil.js.call('Foo')
anvil.js.call(anvil.js.window['Foo'])
anvil.js.window.Foo()
Are three ways of calling a Javascript function from Python
Converting to Python
A proxyobject, which is a dictionary-like, is easily converted into a Python dictionary.
print(proxy_obj) # proxyobject({'a': 1, 'b': 2})
dict(proxy_obj) # {'a': 1, 'b': 2}
{**proxy_obj} # {'a': 1, 'b': 2}
The above code works for all proxyobjects regardless of the underlying Javascript object. However, only the keys returned from the .keys()
method will be included in the dictionary object. Thus it is unlikely to be suitable for most proxyobjects.
You can determine if the proxyobject is dictionary-like by inspecting it with print()
.
print(proxy_obj) # proxyobject({'a': 1, 'b': 2}) (dictionary-like)
print(Foo()) # <Foo proxyobject> (not dictionary-like)
More specifically, a proxyobject is considered dictionary-like when the underlying Javascript object is an object literal.
Proxyobjects cannot be sent to the server and must be converted to a dictionary or mapped to another portable type.