0

So I have the following dilemma:

I am using Brython and everything is working ok. I have a small piece of code that executes ajax requests for me and I added that in the header to bind everything on the current elements in the page.

    from browser import document, ajax

# URL Query String
qs = ''
# URL to work on
url = ''


def post_data(url, qs):
    req = ajax.ajax()
    # Bind the complete State to the on_post_complete function
    req.bind('complete', on_post_complete)
    # send a POST request to the url
    req.open('POST', url, True)
    req.set_header('content-type', 'application/x-www-form-urlencoded')
    # send data as a dictionary
    req.send(qs)


def get_data(url, qs):
    req = ajax.ajax()
    req.bind('complete', on_get_complete)
    # Bind the complete State to the on_get_complete function
    req.open('GET', url+'?'+qs, True)
    req.set_header('content-type', 'application/x-www-form-urlencoded')
    req.send()


def on_post_complete(req):
    if req.status == 200 or req.status == 0:
        #  Take our response and inject it into the html div with id='main'
        document["main_area"].html = req.text
    else:
        document["main_area"].html = "error " + req.text


def on_get_complete(req):
    if req.status == 200 or req.status == 0:
        #  Take our response and inject it into the html div with id='main'
        document["main_area"].html = req.text
    else:
        document["main_area"].html = "error " + req.text


def account_click(ev):
    get_data("/account", qs)


def contact_link_click(ev):
    get_data("/contact", qs)


def logo_link_click(ev):
    get_data("/main_page", qs)


def products_link_click(ev):
    get_data("/products_page", qs)


def register_link_click(ev):
    get_data("/register", qs)

document['login_link'].bind('click', account_click)
document['contact_link'].bind('click', contact_link_click)
document['logo_link'].bind('click', logo_link_click)
document['register_link'].bind('click', register_link_click)

document['running_link'].bind('click', products_link_click)
document['fitness_link'].bind('click', products_link_click)
document['tennis_link'].bind('click', products_link_click)
document['football_link'].bind('click', products_link_click)
document['golf_link'].bind('click', products_link_click)

Ok now my bigger problem is the fact that register_link is not in the page from the beginning. To be more exact register_link will only be loaded into the DOM after the login_link link is clicked after which the register link does nothing because the event was unable to be bound on it from the get go.

Now I know that I could easily bypass this just by importing this again in that page but I would want to avoid redundant imports and i'm not really sure exactly how to go about doing this.

EDIT: Or is there a way in brython to wait for the DOM to be loaded completely?

davidism
  • 121,510
  • 29
  • 395
  • 339
Nick
  • 545
  • 12
  • 31
  • I found a method using the brython timer but I would still want to know if there is any better way of doing this – Nick Oct 10 '17 at 09:04

2 Answers2

1

As you noticed, writing account_click like this :

def account_click(ev):
    get_data("/account", qs)
    document['register_link'].active = True
    document['register_link'].bind('click', register_link_click)

doesn't work, because the program doesn't wait for get_data to complete before executing the next 2 lines.

A solution is to write a specific version of get_data and on_get_complete for this case (I have supposed that the "register_link" button is in the page, but initially disabled):

def complete_register(req):
    """Called when the Ajax request after "login_link" is complete."""
    if req.status == 200 or req.status == 0:
        #  Take our response and inject it into the html div with id='main'
        document["main_area"].html = req.text
        # enable "register link" button and add binding
        document['register_link'].disabled = False
        document['register_link'].bind('click', register_link_click)
    else:
        document["main_area"].html = "error " + req.text

def get_data_and_register(url, qs):
    req = ajax.ajax()
    req.bind('complete', complete_register)
    req.open('GET', url+'?'+qs, True)
    req.set_header('content-type', 'application/x-www-form-urlencoded')
    req.send()

def account_click(ev):
    get_data_and_register("/account", qs)

Another option would be to keep the generic functions get_data and on_get_complete, and add an optional parameter callback:

def get_data(url, qs, callback=None):
    req = ajax.ajax()
    req.bind('complete', lambda req:on_get_complete(req, callback))
    # Bind the complete State to the on_get_complete function
    req.open('GET', url+'?'+qs, True)
    req.set_header('content-type', 'application/x-www-form-urlencoded')
    req.send()

def on_get_complete(req, callback=None):
    if req.status == 200 or req.status == 0:
        #  Take our response and inject it into the html div with id='main'
        document["main_area"].html = req.text
        if callback is not None:
            callback(req)
    else:
        document["main_area"].html = "error " + req.text
marqueemoon
  • 376
  • 2
  • 5
0

This is nothing common sense can't work for you - and Brython in this respect does just the same as Javascript: any DOM element you want to change needs to exist before you try to modify/bind it.

For decades the "usual" way to do that in Javascript has been place the bindings in a function and just call it at the bottom of the page, or on the body tag onload event, after everything else is loaded. "Modern" Javascript code "solves" this by using jQuery or other framework and its ready() method.

You have to do the same there - the timer might work, but it is risky. And, of course, elements that just exist after one or more of the other functions are triggered should be dealt with inside the respective functions:

def account_click(ev):
    get_data("/account", qs)
    document['register_link'].bind('click', register_link_click)
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • I did just that as well but it will still try to bind to an ID that "MAY" not have been loaded yet at the time of the bind which would still force me to import my file again which is wasteful – Nick Oct 11 '17 at 08:25