Bindings¶
In the Module and Component sections, it has been explained that
both can host a bindings declaration. These bindings are a link
between:
Attributes and Observables (see Observables)
The declaration looks like this.
from anpylar import Component, html
...
class PyroesComponent(Component):
bindinds = {
'pyroes': [],
}
...
This at first seems like a normal dict declaration containing a pyroes
key and a matching value of [] (an empty list), but there is more.
Because the declaration happens inside a subclass of Component the
following holds true:
PyroesComponentwill automatically have an attribute namedpyroeswhich will obviously have a default value of[]A 2nd attribute named
pyroes_will be available and this is an Observable
Note
Yes, the observable attribute receives the suffix _ (an
underscore).
In Python _ is usually doubled before and after a name to
indicate a Python reserved name, doubled before a name to indicate a
name-mangled component and used as single character before a name to
indicate a kind of reserved attribute.
AnPyLar has chosen to mark the bindings (or observable
attributes) by using a single _ as a suffix
See also
If you are eager, you can also go straight to Observables
Both attributes are linked together so that:
Setting the value of
pyroestriggers the observablepyroes_and therefore any operations subscriptions to itSetting (or calling) the observable sets the value of the attribute:
self.pyroes_([1, 2, 3]) # equivalent to self.pyroes = [1, 2, 3]
Let’s see it in code terms
from anpylar import Component, html
class Counter(Component):
bindinds = {
'count': 0,
}
def render(self, node):
html.h1('{}')._fmt(self.count_)
with html.button('Count up!') as b:
b._bindx('click', self.do_count)
def do_count(self):
# Alternative -> self.count_(self.count + 1)
# Alternative -> self.count_ = self.count + 1
self.count += 1
Note
You can test this simply script with anpylar-serve without creating a
complicated structure by placing the contents in a file index.py and
doing:
anpylar-serve --auto-serve index.py
With this basic example the powers of the binding (attribute <-> observable) could be explained:
html.h1('{}')._fmt(self.count_)
We are creating an <h1> with the formatting template {} as text. This
will be formatted to contain the value delivered by _fmt(self.count_)
Because self.count_ is an Observable, there will be a background
subscription to it. Whenever the value of self.count changes, this will be
reflected as a event through the observable and the value of our <h1> tag
will change.
with html.button('Count up!') as b:
b._bindx('click', self.do_count)
We are now adding a <button> for which we add an event binding. When
clicked, it will call our do_count method.
Note
Notice the name _bindx with the trailing x. This is to
separate it from the _bind method. With the x method the
generated click event is not delivered with the callback.
And finally
def do_count(self):
# Alternative -> self.count_(self.count + 1)
# Alternative -> self.count_ = self.count + 1
self.count += 1
In our do_count, we simply increase the value of self.count. This will
(as explained above) trigger the observable self.pyroes_ and update the
value of our <h1> tag.
Experienced Python programmers will have by now for sure noticed that during
the bindx operation no lambda was used and this because self.count +=
1 wouldn’t be valid.
with html.button('Count up!') as b:
b._bindx('click', lambda: self.count += 1) # <- NOT VALID
One has to use an expression inside the lambda and the auto-increment
operation doesn’t count as one.
But looking at the alternatives of how to set the value of self.count using
the observable we could have actually used a lambda. For example:
with html.button('Count up!') as b:
b._bindx('click', lambda: self.count_(self.count + 1)) # <- VALID
Removing with it the need to have a dedicated do_count method.
For the sake of it, let’s show a final possibility, which is related as how one declares the event to bind to.
with html.button('Count up!') as b:
b._bindx.click(lambda: self.count_(self.count + 1)) # <- VALID
Rather than specifying click as the first argument of _bindx it can be
chained in standard dot notation, leaving the lambda as the only argument
inside the call.
We believe this is actually a lot more readable, but the programmer is king.