Javascript Integration¶
ToscaWidget2 was designed to work with any Javascript framework and integrate Python and Javascript as well as possible, leading to a seamless development experience when you have to provide Javascript callbacks or functions to your Python declared widgets and forms.
Javascript on Display¶
Frequently, when a widget is displayed, you might have to run some form of initialization in JavaScript to attach dynamic behaviours to it.
This can be done by using tw2.core.Widget.add_call()
to register a tw2.core.js.js_function that should be
called.
A simple example might be to display a widget that shows an “Hello World” alert every time it renders:
import tw2.core as twc
class HelloJSWidget(twc.Widget):
message = twc.Param(description="Message to display")
template = "<div></div>"
inline_engine_name = "kajiki"
def prepare(self):
super(HelloJSWidget, self).prepare()
alert = twc.js.js_function("alert")
if self.message:
self.add_call(alert(self.message))
As you can see we define a new tw2.core.js.js_function
named
“alert” and we assign it to the python “alert” variable.
If a message is provided, tw2.core.Widget.add_call()
is used to register alert(self.message)
as what should
be called every time the widget is rendered.
Displaying the widget in a web page:
HelloJSWidget(message="Hello World").display()
will in fact open an alert box with the “Hello World” text.
But you are not constrained to use pre-existing Javascript
functions (like alert
), you can in fact declare your
own function (or use one that was imported from a tw2.core.resources.JSLink
).
For example we can change the previous widget to accept
only the name of the person to greet instead of the whole
message and display "Hello SOMEONE"
always:
class HelloJSWidget(twc.Widget):
greeted = twc.Param(description="Who to greet")
template = "<div></div>"
inline_engine_name = "kajiki"
def prepare(self):
super(HelloJSWidget, self).prepare()
sayhello = twc.js.js_function('(function(target){ alert("Hello " + target); })')
if self.greeted:
self.add_call(sayhello(self.greeted))
As you could see, instead of having out tw2.core.js.js_function
point
to an already existing one, we declared a new one that accepts
a target
argument and displays an alert to greet the target.
The target of the greet message is then set in HelloJSWidget.prepare
through the greeted
param.
Displaying such widget will lead as expected to show an alert box with “Hello” plus the name of the greeted person:
HelloJSWidget(greeted="Mario").display()
It’s also for example possible to run javascript that will target
the widget itself by using the Widget.id
and Widget.compound_id
properties to know the unique identifier of the widget in the dom.
Using such tactic we could rewrite the previous widget to always read
the greeted person from the content of the div
instead of passing
it as an argument:
class HelloJSWidget(twc.Widget):
greeted = twc.Param(description="Who to greet")
template = """<div id="$w.id">${w.greeted}</div>"""
inline_engine_name = "kajiki"
def prepare(self):
super(HelloJSWidget, self).prepare()
sayhello = twc.js.js_function('(function(widget_id){ var target = document.getElementById(widget_id).innerText; alert("Hello " + target); })')
self.add_call(sayhello(self.id))
Note
compound_id
is safer to use, as it avoids collions
when widgets with the same id are used within different
parents. But is mostly only available in form fields.
On plain widgets you might need to use id
itself.
Javascript Callbacks¶
While being able to call javascript every time the widget is displayed is essential to be able to attach advanced javascript behaviours to widgets, sometimes you will need to trigger Javascript callbacks when something happens on the widgets.
This can usually be done with tw2.core.js.js_callback
to declare the
javascript callback you care about.
A possible example is to run some javascript when the selected option is changed in a single select field:
alertpicker = twf.SingleSelectField(
attrs={'onchange': twc.js.js_callback('alert("changed!")')},
options=[(1, 'First'), (2, 'Second')]
)
Builtin Javascript Helpers¶
Python-JS interface to dynamically create JS function calls from your widgets.
This moudle doesn’t aim to serve as a Python-JS “translator”. You should code your client-side code in JavaScript and make it available in static files which you include as JSLinks or inline using JSSources. This module is only intended as a “bridge” or interface between Python and JavaScript so JS function calls can be generated programatically.
-
class
tw2.core.js.
js_callback
(cb, *args)[source]¶ A js function that can be passed as a callback to be called by another JS function
Examples:
>>> str(js_callback("update_div")) 'update_div'
>>> str(js_callback("function (event) { .... }")) 'function (event) { .... }'
Can also create callbacks for deferred js calls
>>> str(js_callback(js_function('foo')(1,2,3))) 'function(){foo(1, 2, 3)}'
Or equivalently
>>> str(js_callback(js_function('foo'), 1,2,3)) 'function(){foo(1, 2, 3)}'
A more realistic example
>>> jQuery = js_function('jQuery') >>> my_cb = js_callback('function() { alert(this.text)}') >>> on_doc_load = jQuery('#foo').bind('click', my_cb) >>> call = jQuery(js_callback(on_doc_load)) >>> print call jQuery(function(){jQuery(\"#foo\").bind( \"click\", function() { alert(this.text)})})
-
class
tw2.core.js.
js_function
(name)[source]¶ A JS function that can be “called” from python and added to a widget by widget.add_call() so it get’s called every time the widget is rendered.
Used to create a callable object that can be called from your widgets to trigger actions in the browser. It’s used primarily to initialize JS code programatically. Calls can be chained and parameters are automatically json-encoded into something JavaScript undersrtands. Example:
>>> jQuery = js_function('jQuery') >>> call = jQuery('#foo').datePicker({'option1': 'value1'}) >>> str(call) 'jQuery("#foo").datePicker({"option1": "value1"})'
Calls are added to the widget call stack with the
add_call
method.If made at Widget initialization those calls will be placed in the template for every request that renders the widget:
>>> import tw2.core as twc >>> class SomeWidget(twc.Widget): ... pickerOptions = twc.Param(default={}) >>> SomeWidget.add_call( ... jQuery('#%s' % SomeWidget.id).datePicker(SomeWidget.pickerOptions) ... )
More likely, we will want to dynamically make calls on every request. Here we will call add_calls inside the
prepare
method:>>> class SomeWidget(Widget): ... pickerOptions = twc.Param(default={}) ... def prepare(self): ... super(SomeWidget, self).prepare() ... self.add_call( ... jQuery('#%s' % d.id).datePicker(d.pickerOptions) ... )
This would allow to pass different options to the datePicker on every display.
JS calls are rendered by the same mechanisms that render required css and js for a widget and places those calls at bodybottom so DOM elements which we might target are available.
Examples:
>>> call = js_function('jQuery')("a .async") >>> str(call) 'jQuery("a .async")'
js_function calls can be chained:
>>> call = js_function('jQuery')("a .async").foo().bar() >>> str(call) 'jQuery("a .async").foo().bar()'