brython-dev / brython

Brython (Browser Python) is an implementation of Python 3 running in the browser
BSD 3-Clause "New" or "Revised" License
6.39k stars 511 forks source link

Dynamic Bind #1506

Closed dreylago closed 4 years ago

dreylago commented 4 years ago

This bind is common when working, for instance, with table inputs. Tables can grow/contract. Then for each input cell we have same handler but with additional parameters (to idenfity the source cell). This is a example is a simplification with just two counters. When I use lambdas to capture the counter name, it does not work.

The solution that works for me: eval the lambda expression. I am not sure if I am doing it wrong or there are better ways to achieve this. Commented out is the bind expression that should be the solution, but it does not work (Firefox)

<!doctype html>

<html lang="en">
<head>
  <meta charset="utf-8">

  <title>Demo</title>

  <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>

  <script type="text/javascript"
    src="https://cdn.jsdelivr.net/npm/brython@3.8.10/brython.min.js">
  </script>

  <script type="text/javascript"
    src="https://cdn.jsdelivr.net/npm/brython@3.8.10/brython_stdlib.js">
  </script>

</head>

<body onload="brython()">

<script type="text/python">
  from browser import document, window
  from browser.html import DIV
  import json

  storage = window.localStorage

  def update_view(tables):
    document['tables'].clear()
    document['tables'] <= DIV(render_tables(tables))

  def init():
    tables=dict(a=0,b=0)
    update_view(tables)
    set_binds(tables)
    storage.setItem('tables',json.dumps(tables))

  def add(event, k):
    print(f'add: {k}')
    tables = json.loads(storage.getItem('tables'))
    tables[k] += 1
    update_view(tables)
    set_binds(tables)
    storage.setItem('tables',json.dumps(tables))

  def set_binds(tables):
    for k, _ in tables.items():
#     document[f'add-{k}'].bind('click', lambda e: add(e, k))
      document[f'add-{k}'].bind('click', eval(f'lambda e: add(e, "{k}")'))

  def render_tables(tables):
    o = ''
    for k, cnt in tables.items():
      cmd = f'<a href="#" id="add-{k}">[+]</a>'
      s = f'<div><label>{k.title()} {cmd}</label><div>{cnt}</div></div>'
      o += s
    return o

  init()
</script>

<div id="tables"></div>

</body>
</html>

Thanks!

dreylago commented 4 years ago

This is not a Brython issue. This is how Python treats variables (pointer to objects).