Part 3 - Componentization

Our List of Pyroes can be displayed and individual Pyroes can be edited, but it is all an amalgam.

Since we are already doing it with our main application component, AppComponent, and the listing component, PyroesComponent and how it is better done when creating large applications, we can separate the listing and editing functionalities.

Copy the top2 folder to top3 and enter it. For example, with:

cp -r top2 top3
cd top3

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)

Adding a PyroDetailComponent

Just as we did before to create PyroesComponent, we can do it for PyroDetailComponent. From inside the app directory create the skeleton for a Component:

anpylar-component PyroDetail

The view of the project layout is now

├── app
│   ├── pyro_detail
│   │   ├── __init__.py
│   │   ├── pyro_detail_component.css
│   │   ├── pyro_detail_component.html
│   │   └── pyro_detail_component.py
│   ├── pyroes
│   │   ├── __init__.py
│   │   ├── pyroes_component.css
│   │   ├── pyroes_component.html
│   │   └── pyroes_component.py
│   ├── __init__.py
│   ├── app_component.css
│   ├── app_component.html
│   ├── app_component.py
│   ├── app_module.py
│   ├── mock_pyroes.py
│   └── pyro.py
├── anpylar.js
├── index.html
├── package.json
└── styles.css

We’ll now move the details part from the html content of PyroesComponent to the html content of PyroDetailComponent. Both html files.

<h2>My Pyroes</h2>
<ul class="pyroes">
</ul>
<pyro-detail></pyro-detail>
<div *_display=selected_.pyd_>
  <h2 {name}="selected_.name_.map(lambda x: x.upper())">{name} Details</h2>
  <div><span>pyd: </span><txt [selected_.pyd_]>{}</txt></div>
  <div>
      <label>name:
        <input *_fmtvalue=selected_.name_ placeholder="name"/>
      </label>
  </div>
</div>

Things to notice:

  • In pyroes_component.html we added a new tag:

    <pyro-detail></pyro-detail>
    

    This is where the PyroDetailComponent will be auto-rendered.

    To make sure this is the case, we will define the selector to have this specific value in PyroDetailComponent

  • The html in pyro_detail_component.html still references the observable selected_. Recall that this was defined in PyroesComponent and in fact: it will still be.

Let’s see the Python counterparts before delivering the full explanation

from anpylar import Component, html
from .pyroes import PyroesComponent
from .pyro_detail import PyroDetailComponent


class AppComponent(Component):

    title = 'Tour of Pyroes'

    bindings = {}

    def render(self, node):
        PyroesComponent()
from anpylar import Component, html
from app.pyro import Pyro
from app.mock_pyroes import Pyroes


class PyroesComponent(Component):

    bindings = {
        'selected': Pyro(),
    }

    def render(self, node):
        with node.select('ul'):  # find the node where to display the list
            for pyro in Pyroes:
                with html.li() as li:  # create a list item per Pyro
                    # if the selected pyro is this pyro ... set a class attr
                    li._class.selected(self.selected_.pyd_ == pyro.pyd)
                    # bind a click to do self.selected_(pyro)
                    li._bindx.click(self.selected_, pyro)
                    # show the pyd in a <apan> as a badge (child of list item)
                    html.span(pyro.pyd, Class='badge')
                    # show the name as text inside the list item
                    html.txt(' {name}')._fmt(name=pyro.name_)
from anpylar import Component, html


class PyroDetailComponent(Component):
    selector = 'pyro-detail'

    bindings = {}

    def render(self, node):
        pass

Parent-Child Relationship

As mentioned above, the observable selected_ is defined as a binding in PyroesComponent. It is nowhere to be seen in PyroDetailComponent, but the associated html content uses it. A Master-Child or Parent-Child or Component-SubComponent relationship is responsible.

Remember the html content for PyroesComponent:

<h2>My Pyroes</h2>
<ul class="pyroes">
</ul>
<pyro-detail></pyro-detail>
...

class PyroDetailComponent(Component):
    selector = 'pyro-detail'

    ...

The <pyro-detail> tag and the selector = 'pyro-detail' are the keys. Because it happens inside the html code for PyroesComponent, when the associated component for the tag is instantiated (namely PyroDetailComponent) it will become a child of the component in which is being created.

Being a child, it can access the bindings from the parent. Hence the capability to use the selected_ observable.

Note

As done with PyroesComponent, which is instantiated inside AppComponent, we could have done the same with PyroDetailComponent

In this case and to show an alternative, we have chosen to instantiate using a tag and defining a selector

Importing pyro_detail_component

You may have noticed that we also shown app_component.py in the code samples above. And this is to show this

from anpylar import Component, html
from .pyroes import PyroesComponent
from .pyro_detail import PyroDetailComponent

...

PyroDetailComponent needs to be imported somewhere. It can be done in this module or it could for example be done in app_component.py or even in pyroes_component.py: the choice is yours.

But if not imported: it will be just a file being idle in your file structure. Importing it, makes the component be part of the arsenal you can use in your app.

Let’s execute

We haven’t changed the functionality, simply how we distribute the functionality across components. The results are the same as in the previous example.

anpylar-serve top3

And go the browser

And our list of Pyroes will be displayed

../../_images/top3-00.png

Clicking on one of the Pyroes will:

  • Show the editor
  • Change the class of the selected Pyro so that it becomes highlighted
../../_images/top3-01.png

And making changes in the editor is automatically reflected not only in the input field, but also in the uppercased name in the editor and in the list of Pyroes

../../_images/top3-02.png

Now that things have been broken down in different components, we can move on.