Services
Blog
English
Cet article fait suite au précédent, Replacing templates with Python components. Assurez-vous de l’avoir lu avant de lire celui-ci !
Cette section rappelle comment nous pouvions déjà générer du HTML avec une API Python élégante.
Les composants sont des classes Python chargées de rendre une balise HTML. À ce titre, elles peuvent avoir du contenu, c’est-à-dire des enfants :
from ryzom.html import *
yourdiv = Div('some', P('content'))
yourdiv.render() == '<div>some <p>content</p></div>'
La plupart des composants devraient s’instancier avec *content comme premier
argument, et vous pouvez y passer autant d’enfants que nécessaire. Ils vont dans
self.content, que vous pouvez aussi modifier après instanciation.
Les balises HTML ont aussi des attributs, pour lesquels nous avons une API pythonesque :
Div('hi', cls='x', data_y='z').render() == '<div class="x" data-y="z">hi</div>'
Le déclaratif et l’héritage sont également pris en charge :
class Something(Div):
attrs = dict(cls='something', data_something='foo')
class SomethingNew(Something):
attrs = dict(addcls='new') # how to add a class without re-defining
yourdiv = SomethingNew('hi')
yourdiv.render() == '<div class="something new" data-something="foo">hi</div>'
Les styles peuvent être déclarés dans attrs, ou bien séparément.
class Foo(Div):
style = dict(margin_top='1px')
# is the same as:
class Foo(Div):
style = 'margin-top: 1px'
# is the same as:
class Foo(Div):
attrs = dict(style='margin-top: 1px')
Ce qui précède produira un bundle ressemblant à ceci :
.Foo {
margin-top: 1px;
}
Et Foo("bar") rendra :
<div class="Foo">bar</div>
class.Ce dépôt fournit un fork de py2js que vous pouvez utiliser pour écrire du JavaScript en Python. Il y a deux façons d’écrire du JS en Python : la façon “jQuery” et la façon WebComponent.
Vous devez cependant comprendre que notre objectif est d’écrire du JS en
Python, plutôt que de prendre en charge Python dans JS comme le projet
Transcrypt. Dans notre cas, nous nous limiterons à un sous-ensemble des
langages JS et Python, donc des choses comme __mro__ en Python, ou même
l’héritage multiple, ne seront pas du tout prises en charge.
Cependant, vous pouvez tout de même écrire du JS en Python et générer un bundle JS.
Ce qui suit définit un HTMLElement personnalisé avec une classe JS HTMLElement ; cela générera un web component de base.
class DeleteButton(Component):
tag = 'delete-button'
class HTMLElement:
def connectedCallback(self):
this.addEventListener('click', this.delete.bind(this))
async def delete(self, event):
csrf = document.querySelector('[name="csrfmiddlewaretoken"]')
await fetch(this.attributes['delete-url'].value, {
method: 'delete',
headers: {'X-CSRFTOKEN': csrf.value},
redirect: 'manual',
}).then(lambda response: print(response))
Cela générera le JS suivant, qui laissera le navigateur responsable du cycle de
vie des composants. Consultez la documentation de window.customElement.define
pour les détails.
class DeleteButton extends HTMLElement {
connectedCallback() {
this.addEventListener('click',this.delete.bind(this));
}
async delete() {
var csrf = document.querySelector('[name="csrfmiddlewaretoken"]');
await fetch(this.attributes['delete-url'].value,{
method: 'delete',
headers: {'X-CSRFTOKEN': csrf.value},
redirect: 'manual'
}).then(
(response) => {return console.log(response)}
);
}
}
window.customElements.define("delete-button", DeleteButton);
Et c’est plutôt rock’n’roll, si vous voulez mon avis.
MAIS il y a un piège : actuellement, vous devez définir le premier
argument à self comme en Python, afin que le transpileur sache que cette
fonction est une méthode de classe et qu’elle ne doit pas être rendue avec le
préfixe function , qui ne fonctionne pas dans les classes ES6.
Vous pouvez le faire “à la jQuery” en définissant une méthode py2js dans votre
composant avec py2js.Mixin :
class YourComponent(py2js.Mixin, Div):
def on_form_submit():
alert('submit!')
def py2js(self):
getElementByUuid(self._id).addEventListener('submit', self.on_form_submit)
Ainsi, votre composant rendra aussi l’instruction addEventListener dans une
balise script, et le bundle empaquettera la fonction on_form_submit.
Le composant dépendra de ses bundles CSS et JS. Sans Django, vous pouvez le faire manuellement ainsi :
from ryzom import bundle
your_components_modules = [
'ryzom_mdc.html',
'your.html',
]
css_bundle = bundle.css(*your_components_modules)
js_bundle = bundle.js(*your_components_modules)