Guides¶
Installation¶
Anvil-Extras is intended to be used as a dependency for other Anvil applications.
Clone anvil-extras to your account:
Add anvil-extras as a dependency to your own app(s):
From the gear icon at the top of your app’s left hand sidebar, select ‘Dependencies’
From the ‘Add a dependency’ dropdown, select ‘Anvil-Extras’
That’s it! You should now see the extra components available in your app’s toolbox on the right hand side and all the other features are available for you to import.
Contributing¶
All contributions to this project are welcome via pull request (PR) on the Github repository
Issues¶
Please open an Issue and describe the contribution you’d like to make before submitting any code. This prevents duplication of effort and makes reviewing the eventual PR much easier for the maintainers.
If you could start the name of the branch you work on with the number of the issue you create, that would be very helpful as github will automatically link the two together.
Commits¶
Please try to use commit messages that give a meaningful history for anyone using git’s log features. Try to use messages that complete the sentence, “This commit will…” There is some excellent guidance on the subject from Chris Beams
Please ensure that your commits do not include changes to either anvil.yaml or .anvil_editor.yaml.
Components¶
All the components in the library are intended to work from the anvil toolbox as soon as the dependency has been added to an application, without any further setup. This means that they cannot use any of the features within the library’s theme.
If you are thinking of submitting a new component, please ensure that it is entirely standalone and does not require any css or javascript from within a theme element or native library.
Python Code¶
Please try, as far as possible, to follow PEP8
Use the Black formatter to format all code and the isort utility to sort import statements.
Documentation¶
Please include documentation for your contribution as part of your PR. Our documents are written in reStructuredText and hosted at Read The Docs
Our docs are built using Sphinx which you can install locally and use to view your work before submission. To build a local copy of the docs in a ‘build’ directory:
sphinx-build docs build
You can then open ‘index.html’ from within the build directory using your favourite browser.
Testing¶
The project uses the Pytest library and its test suite can be run with:
python -m pytest
We appreciate the difficulty of writing unit tests for Anvil applications but, if you are submitting pure Python code with no dependency on any of the Anvil framework, we’ll expect to see some additions to the test suite for that code.
Merging¶
We require both maintainers to have reviewed and accepted a PR before it is merged.
If you would like feedback on your contribution before it’s ready to merge, please create a draft PR and request a review.
Copyright¶
By submitting a PR, you agree that your work may be distributed under the terms of the project’s licence and that you will become one of the project’s joint copyright holders.
Messaging¶
Introduction¶
This library provides a mechanism for forms (and other components) within an Anvil app to communicate in a ‘fire and forget’ manner.
It’s an alternative to raising and handling events - instead you ‘publish’ messages to a channel and, from anywhere else, you subscribe to that channel and process those messages as required.
Usage¶
Create the Publisher¶
You will need to create an instance of the Publisher class somewhere in your application that is loaded at startup.
For example, you might create a client module at the top level of your app called ‘common’ with the following content:
from .messaging import Publisher
publisher = Publisher()
and then import that module in your app’s startup module/form.
Publish Messages¶
From anywhere in your app, you can import the publisher and publish messages to a channel. e.g. Let’s create a simple form that publishes a ‘hello world’ message when it’s initiated:
from ._anvil_designer import MyPublishingFormTemplate
from .common import publisher
class MyPublishingForm(MyPublishingFormTemplate):
def __init__(self, **properties):
publisher.publish(channel="general", title="Hello world")
self.init_components(**properties)
The publish method also has an optional ‘content’ parameter which can be passed any object.
Subscribe to a Channel¶
Also, from anywhere in your app, you can subscribe to a channel on the publisher by providing a handler function to process the incoming messages.
The handler will be passed a Message object, which has the title and content of the message as attributes.
e.g. On a separate form, let’s subscribe to the ‘general’ channel and print any ‘Hello world’ messages:
from ._anvil_designer import MySubscribingFormTemplate
from .common import publisher
class MySubscribingForm(MySubscribingFormTemplate):
def __init__(self, **properties):
publisher.subscribe(
channel="general", subscriber=self, handler=self.general_messages_handler
)
self.init_components(**properties)
def general_messages_handler(self, message):
if message.title = "Hello world":
print(message.title)
You can unsubscribe from a channel using the publisher’s unsubscribe method.
You can also remove an entire channel using the publisher’s close_channel method.
Be sure to do one of these if you remove instances of a form as the publisher will hold references to those instances and the handlers will continue to be called.
Logging¶
By default, the publisher will log each message it receieves to your app’s logs (and the output pane if you’re in the IDE).
You can change this default behaviour when you first create your publisher instance:
from .messaging import Publisher
publisher = Publisher(with_logging=False)
)
The publish, subscribe, unsubscribe and close_channel methods each take an optional with_logging parameter which can be used to override the default behaviour.
Authorisation¶
A server module that provides user authentication and role based authorisation for server functions.
Installation¶
You will need to setup the Users and Data Table services in your app:
Ensure that you have added the ‘Users’ service to your app
- In the ‘Data Tables’ service, add:
a table named ‘permissions’ with a text column named ‘name’
a table named ‘roles’ with a text column named ‘name’ and a ‘link to table’
column named ‘permissions’ that links to multiple rows of the permissions table * a new ‘link to table’ column in the Users table named ‘roles’ that links to multiple rows of the ‘roles’ table
Usage¶
Users and Permissions¶
Add entries to the permissions table. (e.g. ‘can_view_stuff’, ‘can_edit_sensitive_thing’)
Add entries to the roles table (e.g. ‘admin’) with links to the relevant permissions
In the Users table, link users to the relevant roles
Server Functions¶
The module includes two decorators which you can use on your server functions:
authentication_required
Checks that a user is logged in to your app before the function is called and raises an error if not. e.g.:
import anvil.server
from anvil_extras.authorisation import authentication_required
@anvil.server.callable
@authentication_required
def sensitive_server_function():
do_stuff()
authorisation_required
Checks that a user is logged in to your app and has sufficient permissions before the function is called and raises an error if not:
import anvil.server
from anvil_extras.authorisation import authorisation_required
@anvil.server.callable
@authorisation_required("can_edit_sensitive_thing")
def sensitive_server_function():
do_stuff()
You can pass either a single string or a list of strings to the decorator. The function will only be called if the logged in user has ALL the permissions listed.
- Notes:
The import lines in the examples above assume you have installed the Extras ibrary as a
dependency. If you used direct inclusion, you will need to import from your own copy of the module. * The order of the decorators matters. anvil.server.callable must come before either of the authorisation module decorators.
Augmentation¶
A client module for adding custom jQuery events to any anvil component

Examples¶
from anvil_extras import augment
augment.set_event_handler(self.link, 'hover', self.link_hover)
# equivalent to
# augment.set_event_handler(self.link, 'mouseenter', self.link_hover)
# augment.set_event_handler(self.link, 'mouseleave', self.link_hover)
# or
# augment.set_event_handler(self.link, 'mouseenter mouseleave', self.link_hover)
def link_hover(self, **event_args):
if 'enter' in event_args['event_type']:
self.link.text = 'hover'
else:
self.link.text = 'hover_out'
#================================================
# augment.set_event_handler equivalent to
augment.add_event(self.button, 'focus')
self.button.set_event_handler('focus', self.button_focus)
def button_focus(self, **event_args):
self.button.text = 'Focus'
self.button.role = 'secondary-color'
need a trigger method?¶
# 'augment' the component by adding any event...
augment.add_event(self.textbox, 'select')
# augment.add_event(self.textbox, 'custom')
# augment.add_event(self.textbox, '')
def button_click(self, **event_args):
self.textbox.trigger('select')
Keydown example¶
augment.set_event_handler(self.text_box, 'keydown', self.text_box_keydown)
def text_box_keydown(self, **event_args):
key_code = event_args.get('key_code')
key = event_args.get('key')
if key_code == 13:
print(key, key_code)
advanced feature¶
you can prevent default behaviour of an event by returning a value in the event handler function - example use case*
augment.set_event_handler(self.text_area, 'keydown', self.text_area_keydown)
def text_area_keydown(self, **event_args):
key = event_args.get('key')
if key.lower() == 'enter':
# prevent the standard enter new line behaviour
# prevent default
return True
Popover¶
A client module that allows bootstrap popovers in anvil
Live Example: popover-example.anvil.app
Example Clone Link:
