Component ######### For the reference see: :doc:`/reference/component` A *Component* takes over a section of the display. It can render inside that section (the section is inside an HTML tag) and is king when it comes to manage anything rendered inside (like binding to events of buttons, changing the text of elements, re-rendering elements on-demand, rendering from network content, applying privately scoped styles, ...) It can host subcomponents which can be in turn in charge of portions of the display which fall within the realm of the main *Component*. The usual combination for rendering involves 3 pieces - HTML - CSS - Python Which can be combined in a single Python *class* or be divided in 3 proper ``.html``, ``.css`` and ``.py`` files. .. note:: You can use ``ComponentInline``, which already configures ``htmlpath=None`` and ``stylepath=None`` for you, to avoid looking for external *html* and *css* resources for the component Properties of a *Component* *************************** - Attributes and other definitions in the *Component* can be accessed by: - Subcomponents (and subcomponents of it) - It can access attributes of parent *Component* instances and the parent *Module* - It can declare: - *bindings*: which auto-generates an attribute bound to an *Observable* - *services*: which will be added as attributes and instantiated to provide a service to the component and subcomponents .. note:: *components* is not missing from the declaration. The *subcomponents* mentioned above are generated by means of direct instantiation, by tag based auto-instantiation, or because they are children in a route definition hierarchy in the module. - It can define the tag under which it will be rendered or let it be auto-generated (see ``selector``) - It can host *Html* content either fetched from a file or declared inline (see ``htmlpath`` and ``htmlsheet``) - It can host *CSS* content either fetched from a file or declared inline (see ``stylepath`` and ``stylesheet``) - It can render everything programmatically if needed be and/or modify the generated content/styles (see methods ``render`` and ``styler``) - It can react to being loaded unloaded from the DOM (see the methods ``loading`` and ``unloading`` or the more generic version ``load``) Component instantiation *********************** In order for a component to do its work it has to be instantiated. There are several possibilities. But before seeing them: - If a component is directly instantiated, it will look for the matching ``selector`` tag to render itself. Should the tag not be found, the default behavior is to render the tag and then render itself inside - If a tag is found in generated/fetched html code which matches the ``selector`` of a component, it will be instantiated so that it can render itself inside the tag. .. note:: Remember, as stated above, that if no ``selector`` is specifically defined, the *Component* will auto-generate one. It will use its own *class* name (with hyphens in between lower/upper-case boundaries, then lowercased and receiving a unique suffix) This dual behavior gives the programmer complete flexibility about how components will render by simply specifying the matching tags in html code, or by instantiating the component directly and letting it auto-render. Module Bootstrapping ==================== The component is declared in the ``components`` directive of a *Module*. It will be instantiated before the routing engine takes over. .. code-block:: python from .app_component import AppComponent class MyModule(Module): ... components = AppComponent # can be a single item or an iterable ... Route Bootstrapping =================== Components will be instantiated (and given a place to render) when the defined routes are visited .. code-block:: python from .one_component import OneComponent from .two_component import TwoComponent class MyModule(Module): ... routes = [ {'path': 'one', 'component': OneComponent,}, {'path': 'two', 'component': TwoComponent,}, ] ... Inline instantiation ==================== When a component is being rendered in ``render``, an instance of another component can be created (it will become a sub-component) .. code-block:: python from .two_component import TwoComponent class OneComponent(Module): def render(self, node): ... # do some work ... TwoComponent() # create sub-component inline Tag auto-instantiation ====================== Specify the ``selector`` belonging to the component to be rendered in the Html code. .. note:: The component has to be imported **somewhere**. If not, the python file will be just a text file sitting somehwere in the file hierarchy. In the snippet below it is imported in the same module in which the instantiation will happen in the Html code, but it can actually be imported anywhere First the component to be rendered, to show the ``selector`` being set to a fixed value. .. code-block:: python class TwoComponent(Module): selector = 'two-component' ... .. code-block:: python from .two_component import TwoComponent class OneComponent(Module): # Use the selector defined inside TwoComponent htmlsheet = '''

hello

''' def render(self, node): ... # do some work ... Tag auto-instantiation (2) ========================== Same as in the previous example but issuing the tag programmatically .. code-block:: python from anpylar import Component, html from .two_component import TwoComponent class OneComponent(Module): # Use the selector defined inside TwoComponent htmlsheet = '''

hello

''' def render(self, node): ... # do some work ... html._tag('') # or the alias html._tagout This is also possible as: .. code-block:: python from anpylar import Component, html from .two_component import TwoComponent class OneComponent(Module): # Use the selector defined inside TwoComponent htmlsheet = '''

hello

''' def render(self, node): ... # do some work ... TwoComponent.selector_render() Component proceedings ********************* The lifecycle of a component follows this path. **Initialization** - ``__init__`` will be called if defined - If *css* is defined as a path (``stylepath``) or inline (``stylesheet``) it will be placed in the DOM. .. note:: The *css* will be scoped to the component and it is thus private to your component. This may generate an *ajax* call if the application has not been packaged and a path or auto-path (``stylepath``) is defined. The default behavior is to look for a *css* file which uses the Python *class* name transformed (underscores are placed in the upper/lower-case boundaries, all lowercased and with ``.css`` as the extension) - If *html* is defined as a path (``htmlpath``) or inline (``htmlsheet``) it will be placed in the DOM. This also includes generating super-charged DOM objects for each element generated, which can be later used in the code. This may generate an *ajax* call if the application has not been packaged and a path (or auto-path) is defined .. seealso:: Pseudo-Programming can be done inside the html code with special directives in the names of attributes. See: :doc:`html-programming` The default behavior is to look for an *html* file which uses the Python *class* name transformed (underscores are placed in the upper/lower-case boundaries, all lowercased and with ``.html`` as the extension) - The method ``styler()`` will be called If no stylesheet has been defined, this method can return text content containing the stylesheet - The method ``render(node)`` will be called **node** is the html element under which rendering takes place. If *html* code (from either a file or an inline definition) was available, it will already be present and ready under *node*. If not, any html element generated with the ``anpylar.html`` module tags will be placed under *node* **DOM Loading/Unloading** - After the initialization or when the component is again being navigated to, the method ``loading()`` will be called to indicate that the component is being placed in the DOM - When the component is being unloaded from the DOM (for example: navigating away to other route) the method ``unloading()`` will be called. These two methods allow executing actions like for example clearing an *input* field when a component is loaded/unloaded. Upon the next visit to the component, the user will always have a fresh *input* field to type in. There is a generic method ``load(loading=True)`` which can be overridden too. The default behavior is to call ``loading`` or ``unloading``. *Deactivation* - If the route that took to this component defines it, ``can_deactivate()`` will be called before navigating away (before ``unloading``) ``can_deactivate`` can prevent navigating away, because the component may ask the application user if changes have to be saved. Component actions ***************** Those are the ones not defined by the platform. These will be the consequence of binding and callback functions set during rendering and/or loading/unloading. It's the programmer the one providing the sauce here.