diff --git a/pyscriptjs/src/components/pybutton.ts b/pyscriptjs/src/components/pybutton.ts index 5c63528f..4d3803cb 100644 --- a/pyscriptjs/src/components/pybutton.ts +++ b/pyscriptjs/src/components/pybutton.ts @@ -37,6 +37,7 @@ export class PyButton extends BaseEvalElement { } connectedCallback() { + this.checkId(); this.code = htmlDecode(this.innerHTML); this.mount_name = this.id.split('-').join('_'); this.innerHTML = ''; diff --git a/pyscriptjs/src/components/pyinputbox.ts b/pyscriptjs/src/components/pyinputbox.ts index 73525a81..aa23d62e 100644 --- a/pyscriptjs/src/components/pyinputbox.ts +++ b/pyscriptjs/src/components/pyinputbox.ts @@ -17,6 +17,7 @@ export class PyInputBox extends BaseEvalElement { } connectedCallback() { + this.checkId(); this.code = htmlDecode(this.innerHTML); this.mount_name = this.id.split('-').join('_'); this.innerHTML = ''; diff --git a/pyscriptjs/src/components/pyscript.ts b/pyscriptjs/src/components/pyscript.ts index c22b2479..f6ee96c5 100644 --- a/pyscriptjs/src/components/pyscript.ts +++ b/pyscriptjs/src/components/pyscript.ts @@ -127,17 +127,36 @@ export class PyScript extends BaseEvalElement { } } -/** Initialize all elements with py-onClick handlers attributes */ +/** Defines all possible pys-on* and their corresponding event types */ +const pysAttributeToEvent: Map = new Map([ + ["pys-onClick", "click"], + ["pys-onKeyDown", "keydown"] +]); + +/** Initialize all elements with pys-on* handlers attributes */ async function initHandlers() { console.log('Collecting nodes...'); const pyodide = await pyodideReadyPromise; - let matches: NodeListOf = document.querySelectorAll('[pys-onClick]'); - let output; - let source; + for (const pysAttribute of pysAttributeToEvent.keys()) { + await createElementsWithEventListeners(pyodide, pysAttribute); + } +} + +/** Initializes an element with the given pys-on* attribute and its handler */ +async function createElementsWithEventListeners(pyodide: any, pysAttribute: string) { + const matches: NodeListOf = document.querySelectorAll(`[${pysAttribute}]`); for (const el of matches) { - const handlerCode = el.getAttribute('pys-onClick'); - source = `Element("${el.id}").element.onclick = ${handlerCode}`; - output = await pyodide.runPythonAsync(source); + if (el.id.length === 0) { + throw new TypeError(`<${el.tagName.toLowerCase()}> must have an id attribute, when using the ${pysAttribute} attribute`) + } + const handlerCode = el.getAttribute(pysAttribute); + const event = pysAttributeToEvent.get(pysAttribute); + const source = ` + from pyodide import create_proxy + Element("${el.id}").element.addEventListener("${event}", create_proxy(${handlerCode})) + `; + await pyodide.runPythonAsync(source); + // TODO: Should we actually map handlers in JS instead of Python? // el.onclick = (evt: any) => { @@ -154,12 +173,6 @@ async function initHandlers() { // } } - matches = document.querySelectorAll('[pys-onKeyDown]'); - for (const el of matches) { - const handlerCode = el.getAttribute('pys-onKeyDown'); - source = `Element("${el.id}").element.addEventListener("keydown", ${handlerCode})`; - output = await pyodide.runPythonAsync(source); - } } /** Mount all elements with attribute py-mount into the Python namespace */