Part 1 - Editing Pyroes *********************** Component auto-generation ========================= Having seen the basic layout of the application and how the basics of a *Component* look like, we can work our way up the ranks to create a **Pyro Editor**. Copy the ``top0`` folder to ``top1`` and enter it. For example, with:: cp -r top0 top1 cd top1 .. note:: Under *Windows* and unless you have a proper shell installed (*Cygwin*, *MSYS*, *GitBash*, ...) you are probably better off using the *Windows Explorer* to make a copy of the directory) From inside the app directory create the skeleton for a *Component*:: cd app anpylar-component Pyroes .. note:: If the name of the component to add doesn't end with *Component*, it will be automatically added For small projects/components one can include the html content directly in the component. Let's generate it automatically:: anpylar-component --htmlsheet Pyroes The generated python code would look like this in each case .. tabs:: .. code-tab:: python __init__.py from .pyroes_component import PyroesComponent .. code-tab:: python pyroes_component.py from anpylar import Component, html class PyroesComponent(Component): bindings = {} def render(self, node): pass .. code-tab:: python pyroes_component.py (with ``--htmlsheet``) from anpylar import Component, html class PyroesComponent(Component): htmlsheet = ''' ''' bindings = {} def render(self, node): pass The addition of ``--htmlsheet`` has added an attribute with that name which is a *literal string* in Python. We could then add the content as in:: htmlsheet = '''

My Heading 1

''' In both cases the generated component is imported into ``__init__.py`` to make it easy available for others to import it. The layout ========== With our additions (let's stick to having html content in a separate file), the layout looks like this .. tabs:: .. code-tab:: none Layout ├── app │   ├── pyroes │   │   ├── __init__.py │   │   ├── pyroes_component.css │   │   ├── pyroes_component.html │   │   └── pyroes_component.py │   ├── __init__.py │   ├── app_component.css │   ├── app_component.html │   └── app_module.py ├── anpylar.js ├── index.html ├── package.json └── styles.css .. code-tab:: none Layout with ``--htmlsheet`` ├── app │   ├── pyroes │   │   ├── __init__.py │   │   ├── pyroes_component.css │   │   └── pyroes_component.py │   ├── __init__.py │   ├── app_component.css │   ├── app_component.html │   ├── app_module.py │   └── pyro.py ├── anpylar.js ├── package.json └── styles.css Out of curiosity, had we applied the ``--htmlsheet`` command line switch, the layout would miss one of the files: ``pyroes_component.html``, because the html content would be inside the component. The Editor ========== A Pyro ------ Actually, and before editing we are going to define a ``Pyro``. As you may have seen in the layout above, we have added a ``pyro.py`` file to the ``app``. Not much is actually needed: - A ``name`` (what's a *Pyro* without one!) - A ``pyd`` to make them unique in case two or more chose to bear the same names (yes even Pyroes could suffer from vanity!) Add ``pyro.py`` to the hierarchy (let's put the ``--htmlsheet`` aside for simplicity) .. tabs:: .. code-tab:: none Layout ├── app │   ├── pyroes │   │   ├── __init__.py │   │   ├── pyroes_component.css │   │   ├── pyroes_component.html │   │   └── pyroes_component.py │   ├── __init__.py │   ├── app_component.css │   ├── app_component.html │   ├── app_module.py │   └── pyro.py ├── anpylar.js ├── index.html ├── package.json └── styles.css And put some Python in action. .. code-block:: python from anpylar import Model class Pyro(Model): bindings = { 'pyd': 0, 'name': '', } Uhmmm!!! The definition of the attributes is done in a dictionary named ``bindings`` (remember it was also auto-generated above for the ``PyroesComponent``) and the class inherits from ``Model``. Quick and dirty explanation - Subclassing from ``Model`` allows the directive ``bindings`` to take effect. Many of the classes in *AnPyLar* use this functionality (``Component`` based classes do) - The definitions inside ``bindings`` will have two effects: 1. Each instance will have an attribute with the name and default values defined in the dictionary. This will be valid code inside the class:: if self.pyd > 10: print('my name is:', self.name) 2. An additional attribute will be created and this will be an **Observable** which can be used to, obviously, observe the attribute. The new attribute will be named: ``self.pyd_`` .. note:: *AnPyLar* has a built-in implementation of reactive programming following as closely as possible the RxJS/RxPY APIs. The initial set of operations is limited, but it will be expanded over time. If you don't know what an *Observable* is all about *Reactive Programming*. You may want to see: `RxJS `_ or the Python version at: `RxPY `_. Although this is a NOP (No Operation) class, we will later find several use cases for the observables and reactive programming. Editing ------- With a ``Pyro`` in the hand, we can now complete the ``PyroesComponent`` and the editor with it. .. tabs:: .. code-tab:: html pyroes_component.html

{name} Details

pyd: {}
.. code-tab:: py pyroes_component.py from anpylar import Component, html from app.pyro import Pyro class PyroesComponent(Component): bindings = { 'pyro': Pyro(pyd=11, name='Pyro Nakamura') } def render(self, node): pass .. code-tab:: py app_component.py from anpylar import Component, html from .pyroes import PyroesComponent class AppComponent(Component): title = 'Tour of Pyroes' bindings = {} def render(self, node): PyroesComponent() Let's focus on the specifics of the code parts from above. .. rubric:: pyroes_component.py .. code-block:: python bindings = { 'pyro': Pyro(pyd=11, name='Pyro Nakamura') } We added a binding and this is usable in the directives in the html code. .. rubric:: pyroes_component.html As much as possible is being rendered with the *AnPyLar* directives, to avoid rendering in code. (Remember: this is known as *htmlista* mode). Notice one specific html tag: - ``{}``: an HTML tag named ````. This is a liberty taken by *AnPyLar* to be able to deliver. In this case and under ``
`` there are two things: a ```` and text. Without putting the text to format inside another tag (````) the supercharged method ``_fmt`` would not know that it has only to format that. .. code-block:: html

{name} Details

As we mentioned before, the ``name`` inside ``Pyro`` would also have an associated observable ``name_``. You can apply observable operations to the observable like in this case ``map(lambda x: x.upper())`` which will uppercase any text passed to it. There is some extra magic there: - The node's method ``_fmt`` is subscribing to the *Observable* in the background Which means: any changes to ``self.pyro.name`` will be pushed through the observable ``self.pyro.name_``, in turn through any operations (our ``map(...``) and then delivered to the subscriber, i.e.: ``_fmt``, which has been invoked with the directive in ``

pyd: {}

And that's because the ``pyd`` is not going to be edited. It won't change during the lifecycle of the component. The binding will simply take the current face value and put it **once** on screen. For the input field .. code-block:: python Once again, we are using the observable and passing it to a supercharged method named ``_fmtvalue`` with the ``*`` directive syntax, which does the following: - Subscribe to an *observable* if one is passed and format the changes in to the value field. - Publish the changes back to the underlying observed attribute (``self.pyro.name`` without the ``_``) when something is edited .. rubric:: app_component.py Having a finished ``PyroesComponent`` is nice, but it wouldn't be of any use if we are not starting it. We'll do that inside our main ``AppComponent``. There are actually different ways of doing it. For the sake of it, we just instantiate it. .. code-block:: python def render(self, node): PyroesComponent() Let's see what our editor delivers. First serve the application:: anpylar-serve top1 And go the browser http://127.0.0.1:2222 Which will deliver this. .. image:: top1-00.png If we use the backspace key to delete part of the name, this is the result .. image:: top1-01.png Blistering barnacles!!! The changes in the *input* field are automatically propagated to the name displayed above it. All thanks to reactive programming and having it integrated in *AnPyLar* Sub-Component Rendering Notes ============================= ``PyroesComponent`` is being instantiated inside ``AppComponent`` and as such it is a *Sub-Component* or *Child-Component*. There are several properties, but let's concentrate in this tutorial in two aspects. Actual DOM Rendering -------------------- .. image:: top1-html-elements.png Nothing surprising here: ``PyroesComponent`` has generated a ```` HTML tag and when rendering this has been placed in the DOM as a child of ```` Further nesting sub-components is of course possible, with no actual hard limits set anywhere. .. note:: The previous chapter of the tutorial had the tag ```` for the ``AppComponent``, whilst this is now ````. The extra ``-x`` is calculated each time and depends on class creation order (import order) Never count on it to be fixed, because it won't be. Sub-Component Rendering ----------------------- As mentioned above, there are different ways to have ``PyroesComponent`` render inside the ``AppComponent``. Let's list them .. tabs:: .. code-tab:: py Instance # This is the one we used above. Just instantiate the component # inside the ``render`` method def render(self, node): ... PyroesComponent() .. code-tab:: py Class Render # Tell it to output its selector def render(self, node): ... PyroesComponent.selector_render() .. code-tab:: py Manual # If the selector of the component is known in advance # (it has for example been manually set) from anpylar import html ... def render(self, node): ... html._tag('my-selector') .. code-tab:: html HTML

some h1 content here

Paragraph content

.. rubric:: Take into account: For the *Manual* and *HTML* methods, the component **has to be imported** somewhere. If the component which has to be instantiated is never imported, there will be no relationship between the component and the DOM node with the same selector name. From a practical point of view, if the file containing the Python code is never imported is just like a text file, with no meaning. Observables as callbacks ======================== The *reactive extensions* implementation in *AnPyLar* has chosen to make certain observables also **callables**. Specifically those that are created in the ``bindings`` directive. As in .. code-block:: python class PyroesComponent(Component): ... bindings = { 'pyro': Pyro(pyd=11, name='Pyro Nakamura') } which allows later the following .. tabs:: .. code-tab:: python with select def render(self, node): ... node.select('input')._fmtvalue(self.pyro.name_) .. code-tab:: python with instantiation def render(self, node): ... html.input(placeholder='name')._fmtvalue(self.pyro.name_) .. code-tab:: html with html Here the *Observable* doubles as the callback to which the *value* will be published when it changes in the ``input`` field. This means effectively that: .. code-block:: python self.pyro.name_(some_value) # is equal to: self.pyro.name = some_value Although for this short tutorial sample it doesn't play a big role, it can do so when using it as a callable subscribing to another observable, for example.