Custom Elements =============== Anatomy Of An Element --------------------- Elements in ``awe`` are composed of two parts. - The server side - The element definition in Python. - The client side - The element definition in JavaScript. Each element has several properties that make it what it is. * ``element_type`` - The name of the element type. It is derived from the element type class definition. * ``id`` - Every element added to the page has an ``id``. This is id is usually generated by can be explicitly set by passing an ``id`` argument to the ``new_XXX`` when creating the element. * ``parent`` - Except for root elements, all elements are children of other elements. * ``root_id`` - Every element added to the page is always part of some root. Most elements are part of the ``root`` root which is the default root for all elements created under the ``Page`` instance. However, whenever a new prop is created with the ``new_prop`` method, a new root is created for it. All children under that prop will have the ``root_id`` of the new prop element. * ``index`` - For each root, every element added to it, gets an index that is incremented by one every time. This index is used internally to add elements in the correct order, but it may be used for other purposes if you see fit. * ``children`` - Each element may have 0 or more children elements. * ``allow_children`` - By default, elements always allow children to be created under them. However, an element definition may specify in its class definition ``allow_children = False``, so ``awe`` will verify that no ``new_XXX`` methods are invoked on it. * ``props`` - Every elements has a dict of props. They are usually passed as is to the underlying React component but they may be used to hold arbitrary data as well. * ``data`` - In addition to ``props``, every element can have arbitrary data attached to it in the ``data`` dict. * ``key`` - The key is the element ``id`` that is automatically injected to the element ``props``. Internally, React uses the ``key`` prop. * ``style`` - The element style may be passed to the ``new_XXX`` method as an argument. This is just syntactic sugar for adding a ``style`` prop explicitly to the element ``props``. The above is exposed to elements in Python and JavaScript with these names: ================== ================== =============== Name Python Javascript ================== ================== =============== ``element_type`` ``element_type`` ``elementType`` ``id`` ``id`` ``id`` ``parent`` ``parent`` ``parentId`` ``root_id`` ``root_id`` ``rootId`` ``index`` ``index`` ``index`` ``children`` ``children`` ``children`` ``allow_children`` ``allow_children`` N/A ``props`` ``props`` ``props`` ``data`` ``data`` ``data`` ``key`` ``props['key']`` ``props.key`` ``style`` ``props['style']`` ``props.style`` ================== ================== =============== Basic Element ------------- Custom elements in python are classes that extend ``awe.CustomElement`` and implement the ``_js()`` classmethod. To use the custom element in a page use the ``new`` method. .. code-block:: python from awe import Page, CustomElement class MyElement(CustomElement): @classmethod def _js(cls): return ''' register( (element) => { return (
My Element!
); } ) ''' def main(): page = Page() page.new(MyElement) page.start(block=True) if __name__ == '__main__': main() ``_init`` Method ---------------- The init method serves as the constructor for the element. The ``new`` method calls the ``_init`` with the arguments passed to it on the newly created element. (excluding ``id``, ``props`` and ``style`` which are handled by ``new`` directly) .. code-block:: python from awe import Page, CustomElement class MyElement(CustomElement): def _init(self, argument1, argument2='default value'): self.update_props({'argument1': argument1}) self.update_data({'argument2': argument2}) @classmethod def _js(cls): return ''' register((e) =>
argument1: {e.props.argument1}, argument2: {e.data.argument2}
) ''' def main(): page = Page() page.new(MyElement, argument1='value 1') page.new(MyElement, argument1='value 2', argument2='not the default value') page.start(block=True) if __name__ == '__main__': main() Accepting Additional ``props`` During Element Creation ------------------------------------------------------ .. code-block:: python from awe import Page, CustomElement class MyElement(CustomElement): @classmethod def _js(cls): return ''' register((e) =>
user supplied: {e.props.userSuppliedProp}
) ''' def main(): page = Page() page.new(MyElement, props={'userSuppliedProp': 'user supplied value'}, style={'color': '#ff0000'}) page.start(block=True) if __name__ == '__main__': main() Updating An Element After Creation ---------------------------------- .. code-block:: python import time from awe import Page, CustomElement class MyElement(CustomElement): def _init(self): self.update_props({ 'counter1': 0, 'nested': {'counter2': 0} }) self.update_data({'counter3': 0}) def increment(self): self.update_props({'counter1': self.props['counter1'] + 1}) self.update_prop(['nested', 'counter2'], self.props['nested']['counter2'] + 1) self.update_data({'counter3': self.data['counter3'] + 1}) @classmethod def _js(cls): return ''' register((e) =>
counter1: {e.props.counter1}, counter2: {e.props.nested.counter2}, counter3: {e.data.counter3}
) ''' def main(): page = Page() element = page.new(MyElement) page.start() while True: element.increment() time.sleep(1) if __name__ == '__main__': main() Advanced Element Updates ------------------------ .. code-block:: python import time from collections import deque from awe import Page, CustomElement class MyElement(CustomElement): def _init(self): self.update_props({'list1': []}) self.update_data({ 'deque1': deque(), 'nested': {'list2': []} }) def update_things(self): now = int(time.time()) list2_data = [now, now + 1] self.props['list1'].append(now) self.data['deque1'].appendleft(now) self.data['nested']['list2'].extend(list2_data) self.update_element(['props', 'list1'], action='append', data=now) self.update_element(['data', 'deque1'], action='prepend', data=now) self.update_element(['data', 'nested', 'list2'], action='extend', data=list2_data) @classmethod def _js(cls): return ''' register((e) =>
{e.props.list1.map((item, index) => (list1: {item}))}

{e.data.deque1.map((item, index) => (deque1: {item}))}

{e.data.nested.list2.map((item, index) => (list2: {item}))}
) ''' def main(): page = Page() element = page.new(MyElement) page.start() while True: element.update_things() time.sleep(1) if __name__ == '__main__': main() Available Window Globals ------------------------ .. code-block:: python from awe import Page, CustomElement class Popover(CustomElement): def _init(self, title): self.update_props({'title': title}) @classmethod def _js(cls): return ''' class Popover extends React.Component { render() { const element = this.props.element; return ( {element.children} ); } } register((e) => ) ''' def main(): page = Page() popover = page.new(Popover, title='Some Title') popover.new_button(lambda: None, 'Hover Me!') content = popover.new_prop('content') content.new_text('line 1') content.new_text('line 2') page.start(block=True) if __name__ == '__main__': main() Variables And Functions ----------------------- TODO: document these * _new_variable * element.variables (.value) * Awe.updateVariable * _register * Awe.call External Scripts And Stylesheets -------------------------------- TODO: document Custom Update Element Action ---------------------------- TODO: document Custom Serializers ------------------ TODO: document Note ---- I stopped documenting the above sections because I am assuming very few people even use the most basic functionally of ``awe``, let alone implement custom elements. If I am mistaken and you happen to be reading this and wishing the above would include useful information, open an issue on GitHub and I'll be happy to complete it, knowing someone finds it useful. Additional Reference -------------------- See the Python side implementation of the builtin elements in `awe/view.py `_. See the JavaScript side implementation of the builtin elements in `components/index.js `_. They all use the same API used by custom elements. See the `examples/custom_element.py `_ example for a simple full example.