web2py ajax function in py4web

291 views
Skip to first unread message

stifan kristi

unread,
Nov 9, 2020, 1:39:03 PM11/9/20
to py4web
http://web2py.com/books/default/chapter/29/11/jquery-and-ajax#The-ajax-function
how to achieve this in py4web ?

thx n best regards,
stifan

Andrew Gavgavian

unread,
Nov 9, 2020, 10:28:17 PM11/9/20
to py4web
I do not believe this is integrated by default to py4web, but it really isn't necessary if you use js libraries like axios to do ajax calls, or the built in util.js' ajax functions. It will mean a little more code in javascript, but it's pretty useful overall to know anyway.

Luca has a good example repo on how to do that in py4web here but it isn't that bad.

Here is a quick and dirty example using the web2py example in a github gist here: I'm sure the syntax isn't 100% correct, but it gets the idea across.

~Andrew

Jim Steil

unread,
Nov 10, 2020, 10:13:42 AM11/10/20
to py4web
Here is what I came up with to do it using utils.js ajax

controller
@action('get_name', method=['GET'])
def get_name():
   
return dict(name='Stifan')


@action('test_ajax', method=['GET'])
@action.uses('test_ajax.html')
def test_ajax():
   
return dict(greeting='Hello')

template
<script type="text/javascript">
   
function get_name() {
        Q
.ajax('GET', "[[=URL('get_name') ]]")
       
.then(function(res) {
            alert
(JSON.parse(res.data).name);
       
})
        .error(function(error) {
            alert('AJAX error')
        })
   
}
</script>

[[=greeting]]
<br />
<button onclick="get_name()">Get Name</button>

<script src="[[=URL('static', 'js/utils.js') ]]"></script>

Not sure if this the best way to do it, but it does use the utils.js AJAX function which is included with py4web.

-Jim

stifan kristi

unread,
Nov 10, 2020, 7:51:48 PM11/10/20
to py4web
thanks andrew and jim for reference

current work in py4web, but it return static value name='test'
controllers.py
@action('test_ajax', method=['GET'] )
@action.uses('test_ajax.html')
def test_ajax():
    return dict()

@action('get_fields', method=['GET'] )
def get_fields():
    return dict(name = 'test')

templates/test_ajax.html
<form>
   <input name="name" onchange="get_fields()" />
   <input name="phone" onchange="get_fields()" />
</form>
<div id="target"></div>
<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script type="text/javascript">
    function get_fields() {
        Q.ajax('GET', "[[=URL('get_fields') ]]")
        .then(function(res) {
            document.getElementById("target").innerHTML = JSON.parse(res.data).name;
        })
    }
</script>

want to achieve like in web2py
controllers.py
def test_ajax():
    return dict()

def get_fields():
    name = request.vars['name']
    phone = request.vars['phone']
    return dict(name = name, phone = phone)

want receive the form vars value from another function or equivalent in web2py request.vars like example above
and
want to show multiple form field data in div target 
e.g. 
            document.getElementById("target").innerHTML = JSON.parse(res.data).name;
            document.getElementById("target").innerHTML = JSON.parse(res.data).phone;

how to achieve it using py4web way ?

thanks and best regards,
stifan

Jim Steil

unread,
Nov 10, 2020, 9:42:56 PM11/10/20
to py4web
How does this look? 

controller
@action('get_names', method=['POST'])
@action.uses(session, db, auth)
def get_names():
    first = request.json['first']
    last = request.json['last']
    return dict(first=first, last=last)



@action('test_ajax', method=['GET'])
@action.uses('test_ajax.html')
def test_ajax(path=None):
    return dict(message='Hello')
    

template   
<script type="text/javascript">
    var onsuccess = function(res) {
        var data = JSON.parse(res.data);
        document.getElementById('display_first').innerHTML = data.first;
        document.getElementById('display_last').innerHTML = data.last;
    };
    var onerror = function(res) {
        alert('ERROR in call');
    };

    function get_names() {
        var first = document.getElementById("first_name").value;
        var last = document.getElementById("last_name").value;
        Q.ajax("POST", "[[=URL('get_names')]]", {
            first: first,
            last: last
        }).then(onsuccess).catch(onerror);
    }
</script>

[[=message]] <span id="display_first"></span> <span id="display_last"></span>
<br /><br />
<input type="text" id="first_name" />
<input type="text" id="last_name" />
<br /><br />
<button onclick="get_names()">Update Greeting</button>

<script src="/app_name/static/js/utils.js"></script>

stifan kristi

unread,
Nov 10, 2020, 11:06:17 PM11/10/20
to py4web
wonderful, work like a charm, thx jim
controllers.py
@action('get_fields', method = ['POST'] )
@action.uses(db)
def get_fields():
    first = request.json['first']
    last = request.json['last']
    return dict(first = first, last = last)
@action('test_ajax', method = ['GET'] )
@action.uses('test_ajax.html')
def test_ajax():
    return dict()

templates/test_ajax.html
<form>
<div>
<input id = "first" value = "first" onchange = "get_fields()" />
</div>
<div>
<input id = "last" value = "last" onchange = "get_fields()" />
</div>
</form>
<div id = "target"></div>
<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script type="text/javascript">
    var onsuccess = function(res) {
        var data = JSON.parse(res.data);
        document.getElementById('target').innerHTML = data.first + ' ' + data.last;
    };
    var onerror = function(res) {
        alert('ERROR in call');
    };
    function get_fields() {
        var first = document.getElementById("first").value;
        var last = document.getElementById("last").value;
        Q.ajax("POST", "[[=URL('get_fields') ]]", {
            first: first,
            last: last
        }).then(onsuccess).catch(onerror);
    }
</script>

Jim Steil

unread,
Nov 10, 2020, 11:24:35 PM11/10/20
to stifan kristi, py4web
Happy to help.

I'd appreciate any feedback from others on the solution I provided. I'm not strong in JavaScript so this may not be considered best practice.

Jim


--
You received this message because you are subscribed to a topic in the Google Groups "py4web" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/py4web/fIL8Jmb3hqY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to py4web+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/py4web/c165e36a-9b93-4ea8-9c9d-5e3603eb7aa6n%40googlegroups.com.

stifan kristi

unread,
Nov 11, 2020, 12:35:22 AM11/11/20
to py4web
is it possible to call python function from javascript that have been success (onsuccess) in py4web
e.g.
controllers.py
@action('report_note_intervals', method = ['GET', 'POST'] )
@action.uses('report_note_intervals.html')
def report_note_intervals():
    # this one work as is http://localhost:8000/music/report_note_intervals when change variable to integer
    from_semitone = request.json['from_note'] 
    to_semitone = request.json['to_note']
    ...
    return dict(header = header, rows = rows) 

# not work when called from another 
templates/form_note_intervals.html
<select id = "from_note" title = "From Note" onchange = "get_fields()" autofocus>
[[for tbl in in_set_tonic:]]
<option value = [[=tbl[0] ]]>
[[=tbl[1] ]]
</option>
[[pass]]
</select>
<select id = "to_note" title = "To Note" onchange = "get_fields()">
[[for tbl in in_set_tonic:]]
<option value = [[=tbl[0] ]]>
[[=tbl[1] ]]
</option>
[[pass]]
</select>
<div id = "target"></div>
<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script type="text/javascript">
    var onsuccess = function(res) {
        document.getElementById('target').innerHTML = Q.ajax('GET', "[[=URL('report_note_intervals') ]]")
    };
    var onerror = function(res) {
        alert('ERROR in call');
    };
    function get_fields() {
        var from_note = document.getElementById("from_note").value;
        var to_note = document.getElementById("to_note").value;
        Q.ajax("POST", "[[=URL('report_note_intervals') ]]", {
            from_note: from_note,
            to_note: to_note
        }).then(onsuccess).catch(onerror);
    }
</script>

any idea how to call python function from javascript using py4web way ?

Jim Steil

unread,
Nov 11, 2020, 9:16:46 AM11/11/20
to py4web
Not sure why you'd want to call it again.

The variable 'res' has all the values that 'report_note_intervals' sent back from python.  

If you really need to call another function from the 'then' callback then you need to setup the whole structure again.  Assigning innerHTML to the return value of the Q.ajax call will not give you the variables you're looking for.  I think you need to setup a .then callback on that call to get the results.

Make sense?

-Jim

stifan kristi

unread,
Nov 11, 2020, 11:32:29 AM11/11/20
to py4web
let say already define a views in report_note_intervals.html
templates/report_note_intervals.html
[[=header]]
<table>
<thead>
<tr>
<th>
Note
</th>
</tr>
</thead>
[[for i in rows:]]
<tr>
<td>
[[=i[0] ]]
</td>
</tr>
[[pass]]
</table>

tried something like but no luck, error occured, but the result is empty (not expected)
<script type="text/javascript">
    var onsuccess = function(res) {
        var data = JSON.parse(res.data);
        var html = data.header + "<table>" + "<thead><tr><th>Note</th></tr><thead>" + "<tbody>" + for i in data.rows: + "<tr><td>oi</td></tr>" + pass + "</tbody>" + "</table>"
        document.getElementById('target').innerHTML = html;
    };
    var onerror = function(res) {
        alert('ERROR in call');
    };
    function get_fields() {
        var from_note = document.getElementById("from_note").value;
        var to_note = document.getElementById("to_note").value;
        Q.ajax("POST", "[[=URL('report_note_intervals') ]]", {
            from_note: from_note,
            to_note: to_note
        }).then(onsuccess).catch(onerror);
    }
</script>

how can call it onsuccess from javascript above ?

Val K

unread,
Nov 11, 2020, 11:50:51 AM11/11/20
to py4web
You cant do thing like this:
<tbody>" + for i in data.rows: + "<tr><td>oi</td>
in js function, you use pure  js-code. Keep in mind that yatl render on server side
среда, 11 ноября 2020 г. в 19:32:29 UTC+3, steve.van...@gmail.com:

Val K

unread,
Nov 11, 2020, 11:51:45 AM11/11/20
to py4web

you have to use pure  js-code
среда, 11 ноября 2020 г. в 19:50:51 UTC+3, Val K:

Val K

unread,
Nov 11, 2020, 12:47:05 PM11/11/20
to py4web
 Also, when you get tired of js-fighting
 you can try my funny IDE

среда, 11 ноября 2020 г. в 19:51:45 UTC+3, Val K:

stifan kristi

unread,
Nov 11, 2020, 5:25:21 PM11/11/20
to py4web
<script type="text/javascript">
    var onerror = function(res) {
        alert('ERROR in call');
    };
    function get_fields() {
        var from_note = document.getElementById("from_note").value;
        var to_note = document.getElementById("to_note").value;
        Q.ajax("POST", "[[=URL('report_note_intervals') ]]", {
            from_note: from_note,
            to_note: to_note
        }).then(Q.ajax('GET', "[[=URL('report_note_intervals') ]]") ).catch(onerror);
    }
</script>
use 'GET' method in .then() not work
Traceback (most recent call last):
  File "/Users/MacBookPro/Downloads/py4web_cmd/py4web/core.py", line 726, in wrapper
    ret = func(*func_args, **func_kwargs)
  File "/Users/MacBookPro/Downloads/py4web_cmd/py4web/core.py", line 686, in wrapper
    ret = func(*args, **kwargs)
  File "/Users/MacBookPro/Downloads/py4web_cmd/apps/music/report.py", line 52, in report_note_intervals
    from_semitone = int(request.json['from_note'] )
TypeError: 'NoneType' object is not subscriptable

<script type="text/javascript">
    var onsuccess = function(res) {
        var data = JSON.parse(res.data);
        var html = data.header + "<table>" + "<tr><th>Note</th></tr>" + for(i in data.rows) + "<tr><td>i</td></tr>"+ "</table>"
        document.getElementById('target').innerHTML = html;
    };
    var onerror = function(res) {
        alert('ERROR in call');
    };
    function get_fields() {
        var from_note = document.getElementById("from_note").value;
        var to_note = document.getElementById("to_note").value;
        Q.ajax("POST", "[[=URL('report_note_intervals') ]]", {
            from_note: from_note,
            to_note: to_note
        }).then(onsuccess).catch(onerror);
    }
</script>
try js for looping not work : 
browser console
form_note_intervals:122 Uncaught SyntaxError: Unexpected token 'for'

objective
if form input change, then the function report_note_intervals is called with it's prepared html document (report_note_intervals.html)
question 
how to achieve it in py4web way ?

could provide work code in web2py (using ajax function), if needed

Jim Steil

unread,
Nov 11, 2020, 5:52:02 PM11/11/20
to py4web
1 - use 'GET' method in .then() not work - You didn't pass it any variables so it from_note is not in request.json.

Do you have py4web running in a debugger?  I run my through the pycharm debugger and set breakpoints in my functions so I can inspect the values being passed in.  Maybe that could help you.

2 - try js for looping not work - for(i in data.row) - I don't believe that is how you build a for loop in javascript.  A javascript for loop is going to look more like:

for (var i = 0; i < data.rows.length; i++) {
    var obj = data.rows[i];
    //  add a row to the table
}

It would look something like that but I'm sure I have something wrong there.  But, you can use the debugger in your browsers developer tools to set breakpoints in the code and see what happens.

I hope this helps.

-Jim

Val K

unread,
Nov 11, 2020, 5:52:15 PM11/11/20
to py4web
since your controller  'report_note_intervals'  uses template - it returns html, not json.  

четверг, 12 ноября 2020 г. в 01:25:21 UTC+3, steve.van...@gmail.com:

stifan kristi

unread,
Nov 11, 2020, 7:18:37 PM11/11/20
to py4web
<script type="text/javascript">
    var onsuccess = function(res) {
        document.getElementById('target').innerHTML = res.data;
    };
    var onerror = function(res) {
        alert(res);
    };
    function get_fields() {
        var from_note = document.getElementById("from_note").value;
        var to_note = document.getElementById("to_note").value;
        Q.ajax("POST", "[[=URL('report_note_intervals') ]]", {
            from_note: from_note,
            to_note: to_note
        }).then(onsuccess).catch(onerror);
    }
</script>

combine suggestion from Jim and valq, it works now, thanks a lot

best regards,
stifan
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

toa...@gmail.com

unread,
Nov 11, 2020, 9:56:43 PM11/11/20
to py4web
I used code in web2py (ajax function)

        ajax = "ajax('%s', ['keywords'], 'step3')"%URL('get_name')
        btn = INPUT(_value="Chấp nhận",_type="button",_class="btn btn-sm btn-primary",_onclick=ajax) 


@action("get_name", method=["GET","POST"])
def get_name():
    import json, urllib.parse
    print (13,json.dumps(urllib.parse.parse_qs(request.json)),345)
    return "Hello one"



Message has been deleted

toa...@gmail.com

unread,
Nov 11, 2020, 9:58:18 PM11/11/20
to py4web
add ajax.js in view



function ajax(u, s, t, options) {
    /*simple ajax function*/

    // set options default value
    options = typeof options !== 'undefined' ? options : {};
    var query = '';
    var jq = {};
    if (typeof s == 'string') {
        var d = $(s).serialize();
        if (d) {
            query = d;
            jq[s] = d;
        }
    } else {
        var pcs = [];
        if (s !== null)
            for (var i = 0; i < s.length; i++) {
                var q = $('[name=' + s[i] + ']').serialize();
                if (q) {
                    pcs.push(q);
                    jq[s[i]]=q;
                }
            }
        if (pcs.length > 0) {
            query = pcs.join('&');
        }
    }

    // default success action
    var success_function = function (res) {
        var msg = res.data;
        if (t) {
            if (t == ':eval') eval(msg);
            else if (typeof t == 'string') $('#' + t).html(msg);
            else t(msg);
        }
    };

    var onerror = function(res) {
        alert('ERROR in call');
    };

    // declare success actions as array
    var success = [success_function];

    // add user success actions
    if ($.isArray(options.done)){
        success = $.merge(success, options.done);
    } else {
        success.push(options.done);
    }

    // default jquery ajax options
    var ajax_options = {
        type: 'POST',
        url: u,
        data: query,
        success: success
    };

    //remove custom "done" option if exists
    delete options.done;

    // merge default ajax options with user custom options
    for (var attrname in options) {
            ajax_options[attrname] = options[attrname];
    }

    // call ajax function
    var link = document.createElement("a");
    link.href = "../../static/img/loading.gif";
    var absolutePath = link.protocol+"//"+link.host+link.pathname+link.search+link.hash;  
    // $.ajax(ajax_options);
    Q.ajax("POST", u, query).then(success_function).catch(onerror);   
    if (typeof t == 'string') $('#' + t).html('<center><img src="'+ absolutePath + '></center>');
    
}   


stifan kristi

unread,
Nov 12, 2020, 6:59:13 AM11/12/20
to py4web
e.g.
templates/form_clef_in_notation.html
<form>
<select id="clef" title="Clef" onchange="get_fields()" autofocus="">
<option value="0">
Treble
</option>
<option value="2">
Bass
</option>
</select>
</form>
<div id = 'target'></div>
<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script type="text/javascript">
    var onsuccess = function(res) {
        document.getElementById('target').innerHTML = res.data;
    };
    var onerror = function(res) {
        alert(res);
    };
    function get_fields() {
        var clef = document.getElementById("clef").value;
        Q.ajax("POST", "[[=URL('notation/report_clef_in_notation') ]]", {
            clef: clef, 
        }).then(onsuccess).catch(onerror);
    }
</script>

templates/report_clef_in_notation.html
<h3>Treble Clef in Notation</h3>
<button onclick="downloadSVG()">Download</button>

<div id="score"></div>
<script src = "[[=URL('static', 'js/vexflow-min.js') ]]"></script>
<script>
VF = Vex.Flow;
var WorkspaceInformation = {
    div: document.getElementById("score"),
    canvasWidth: 600,
    canvasHeight: 200
};
var renderer = new VF.Renderer(
    WorkspaceInformation.div,
    VF.Renderer.Backends.SVG
);
renderer.resize(WorkspaceInformation.canvasWidth, WorkspaceInformation.canvasHeight);
var context = renderer.getContext();
var stave = new VF.Stave(10, 10, 300);
stave.addClef("[[=clef]]");
stave.setContext(context).draw();
function downloadSVG() {
    var s = new XMLSerializer();
    var content = s.serializeToString(context.svg);
    var fileName = "Clef.svg";
    var a = document.createElement("a");
    var file = new Blob([content], {type: 'text/plain'});
    a.href = URL.createObjectURL(file);
    a.download = fileName;
    a.click();
}
</script>

above code result is just show download button (no svg notation painted) when clicked the button it said :
Uncaught ReferenceError: downloadSVG is not defined

perhaps it's just show the button while the javascript function on is not callable on templates/report_clef_in_notation.html

Q : can javascript onsuccess called another javascript function because the same code is work well on web2py using ajax function ?

Jim Steil

unread,
Nov 12, 2020, 10:56:47 AM11/12/20
to py4web
To answer the question, Yes, you can call another javascript function inside the onsuccess callback.

But, I don't think that has anything to do with the downloadSVG function, it appears they are on different html pages.

I think I'm missing a piece of the puzzle.

-Jim

stifan kristi

unread,
Nov 12, 2020, 1:56:11 PM11/12/20
to py4web
tried this one (similar to web2py.js) not work 
controllers.py
@action('notation/form_clef_in_notation', method = ['GET'] )
@action.uses('notation/form_clef_in_notation.html')
def form_clef_in_notation():
    ajax = "ajax('%s', ['keywords'], 'step3')" % URL('get_name')
    btn = INPUT(_value="Chấp nhận", _type="button", _class="btn btn-sm btn-primary", _onclick=ajax) 
    return dict(btn = btn) 

@action("get_name", method=["GET","POST"] )
def get_name():
    import json, urllib.parse
    print (13, json.dumps(urllib.parse.parse_qs(request.json) ), 345)
    return "Hello one"

templates/form_clef_in_notation.html
[[extend 'layout.html']]
[[=btn]]
<script>
function ajax(u, s, t, options) {
</script>

browser console :
form_clef_in_notation:113 Uncaught ReferenceError: $ is not defined
    at ajax (form_clef_in_notation:113)
    at HTMLInputElement.onclick (form_clef_in_notation:93)

seems like utils.js in py4web not loaded or executed javascript on the different html page that been called (target) 
yet 
the web2py.js in web2py loaded or executed javascript on the different html page

not sure which part missed on this, because no error occured both py4web ticket and browser console when tried with utils.js

looking for another solution perhaps loaded components seems similar with web2py LOAD() components, but dont know how to pass parameter from form input fields
e.g.
<ajax-component id="component_1" url="[[=URL('mycomponent.load', vars=['form_input_name_0', 'form_input_name_0'] ) ]]">
</ajax-component>

is this work in py4web ?

thanks and best regards,
stifan

Jim Steil

unread,
Nov 12, 2020, 2:20:13 PM11/12/20
to py4web
Can you check your browser developer tools to ensure that utils.js is getting loaded?

stifan kristi

unread,
Nov 12, 2020, 3:50:44 PM11/12/20
to py4web
yes, utils.js is loaded but somehow the javascript on the report_clef_in_notation is not executed to draw music notation (html button and javascript is loaded as inspected on browser elements)

controllers.py
@action('form_clef_in_notation', method = ['GET'] )
@action.uses('form_clef_in_notation.html')
def form_clef_in_notation():
    in_set_clef = [
    (0, "Bass"), 
    (1, "Treble"), 
    ]
    return dict(in_set_clef = in_set_clef) 

@action('report_clef_in_notation', method = ['GET', 'POST'] )
@action.uses('report_clef_in_notation.html')
def report_clef_in_notation():
    clef_idx = int(request.json['clef'] )
    #clef_idx = int(1)
    if clef_idx == 0:
        clef = "bass"
    elif clef_idx == 1:
        clef = "treble"
    return dict(clef = clef)

templates/form_clef_in_notation.html
[[extend 'layout.html']]
<form>
<select id = "clef" onchange = "get_fields()">
[[for tbl in in_set_clef:]]
<option value = [[=tbl[0] ]]>
[[=tbl[1] ]]
</option>
[[pass]]
</select>
</form>
<div id = 'target'></div>
<script src = "[[=URL('static', 'js/vexflow-min.js') ]]"></script>
<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script type="text/javascript">
    var onsuccess = function(res) {
        document.getElementById('target').innerHTML = res.data;
    };
    var onerror = function(res) {
        alert(res);
    };
    function get_fields() {
        var clef = document.getElementById("clef").value;
        Q.ajax("POST", "[[=URL('notation/report_clef_in_notation') ]]", {
            clef: clef, 
        }).then(onsuccess).catch(onerror);
    }
</script>

templates/report_clef_in_notation.html
<button onclick = "downloadSVG()">Download</button>

result 
- when user change the value of form input select, it loaded the report_clef_in_notation.html in the div (id = target), but the javascript function to draw music notation is not executed
- if javascript function to draw music notation is executed, the result will appear in div (id = score)
- already checked browser source code to ensure that javascript is loaded for both vexflow.js and utils.js

tried
- trying to execute report_clef_in_notation.html directly by change clef_idx into int(1) not variable, it works fine
- javascript vexflow.js loaded twice on both form and report just to testing purpose to ensure that it loaded before called and after called by form_clef_in_notation.html

so looking for solution for this using py4web way, because the same code work fine in web2py
difference web2py and py4web code in this case 
- views part delimiter {{}} in web2py, [[]] in py4web
- decorator in controller function @action() in py4web no decorator in web2py
- onchange value web2py using ajax() on web2py_ajax.html (web2py.js) while in py4web using utils.js

working in web2py

any other way how to achieve this in py4web ?

Jim Steil

unread,
Nov 12, 2020, 4:01:14 PM11/12/20
to py4web
Ok, I think I get it now.  Are you trying to replace the HTML in a portion of your page with the HTML that is returned from the ajax call?  

Let me work on a sample.....


-Jim

stifan kristi

unread,
Nov 12, 2020, 4:08:05 PM11/12/20
to py4web
yes, that's what i want to achieve in py4web (javascript function is executed after being called not just written)
think for another solution is using py4web component, but found in example it's not with parameter (no request.json() or request.query() )
not sure, just guessing and still learning new thing

Jim Steil

unread,
Nov 12, 2020, 4:14:28 PM11/12/20
to py4web
That was going to be my next question, why don't you use a component?  Evidently when you replace text in HTML with innerHTML it doesn't load the javascript.  If that is the can, can yo just load that javascript with the form_clef_in_notation page?  Then it should be there to be executed.

stifan kristi

unread,
Nov 12, 2020, 4:31:01 PM11/12/20
to py4web
think cant do that because the script in report_clef_in_notation contain variable 'clef' that need to be draw
the variable 'clef' is got from form_clef_in_notation.html passing to report_clef_in_notation controller function
already tested, put the javascript that draw notation into form_clef_in_notation.html, it return an error asking about 'clef' variable is not defined

just guessing if i know how to use py4web ajax-component with passing parameter, it can be done, but no example for that
e.g.
templates/form_clef_in_notation.html
<ajax-component id="component_1" url="[[=URL('report_clef_in_notation.load', vars=['clef'] ) ]]">
</ajax-component>

like this or the ajax-component for loaded just javascript part (no button and no div id=score) ?

best regards,
stifan

Jim Steil

unread,
Nov 12, 2020, 4:50:56 PM11/12/20
to py4web
How about something like this:

[[extend 'layout.html']]
<form>
    <select id="clef" onchange="get_fields()">
        [[for tbl in in_set_clef:]]
        <option value=[[=tbl[0] ]]>
            [[=tbl[1] ]]
        </option>
        [[pass]]
    </select>
</form>

<div id='target' style="display: none">
    <ajax-component id="my_component" url="[[=URL('report_clef_in_notation', '0')]]"></ajax-component>
</div>


<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script type="text/javascript">
    function get_fields() {
        $('.auth_user_display').click(function(event) {
            var url = "[[=URL('user')]]";
            var clef = document.getElementById("clef").value;
            Q.load_and_trap('GET', url, {clef: clef}, 'my_component');
            $('#target').show();
        });
    }    
</script>


-Jim


stifan kristi

unread,
Nov 12, 2020, 6:12:24 PM11/12/20
to py4web
controllers.py
@action('notation/form_clef_in_notation', method = ['GET'] )
@action.uses('notation/form_clef_in_notation.html')
def form_clef_in_notation():
    clef = 1
    return dict(in_set_clef = in_set_clef, clef = clef) 

@action('notation/report_clef_in_notation', method = ['GET', 'POST'] )
@action.uses('notation/report_clef_in_notation.html')
def report_clef_in_notation():
    clef_idx = int(request.json['clef'] )
    clef = notation_0(clef_idx)
    return dict(clef = clef)

templates/form_clef_in_notation.html
[[extend 'layout.html']]
<form>
[[include 'templates/form/form_select_autofocus_6.html']]
</form>
[[include 'templates/script/vexflow.html']]
<div id='target' style="display: none">
    <ajax-component id="my_component"" url="[[=URL('notation/report_clef_in_notation', vars = {clef : clef} )]]"></ajax-component>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script type="text/javascript">
    function get_fields() {
        $('#clef').change(function(event) {
            var url = "[[=URL('notation/report_clef_in_notation') ]]";
            var clef = document.getElementById("clef").value;
            Q.load_and_trap('GET', url, {clef: clef}, 'my_component');
            $('#target').show();
        });
    }    
</script>

py4web tickets
  File "/Users/MacBookPro/Downloads/py4web_cmd/apps/music/notation.py", line 32, in report_clef_in_notation
    clef_idx = int(request.json['clef'] )
TypeError: 'NoneType' object is not subscriptable

seems load components is not right because must assigned variable first to report_clef_in_notation in hidden then show it after form select change, if it's work then can use it, but it's not work too

best regards,
stifan

Jim Steil

unread,
Nov 12, 2020, 6:16:08 PM11/12/20
to stifan kristi, py4web
Can't you handle the '0' condition in your controller and your template?

Jim

--
You received this message because you are subscribed to a topic in the Google Groups "py4web" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/py4web/fIL8Jmb3hqY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to py4web+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/py4web/f8460eeb-2cf2-4de6-9f81-c1c68c2ece62n%40googlegroups.com.

stifan kristi

unread,
Nov 12, 2020, 6:31:28 PM11/12/20
to py4web
url="[[=URL('notation/report_clef_in_notation', '0') ]]"
means must access the url : http://localhost:8000/music/notation/report_clef_in_notation/0

while action decorator @action('notation/report_clef_in_notation', method = ['GET', 'POST'] )
already tried to change action decorator into
@action('notation/report_clef_in_notation/<number>', method = ['GET', 'POST'] )
but it return an error saying 
TypeError: report_clef_in_notation() got an unexpected keyword argument 'number'

got an idea from utils.js about load_and_trap
<script type="text/javascript">
    var onsuccess = function(res) {
        Q.ajax("GET", "[[=URL('notation/report_clef_in_notation') ]]", res.data);
    };
    var onerror = function(res) {
        alert(res);
    };
    function get_fields() {
        var clef = document.getElementById("clef").value;
        Q.ajax("POST", "[[=URL('notation/report_clef_in_notation') ]]", {
            clef: clef, 
        }).then(onsuccess).catch(onerror);
    }
</script>

but still not worked
utils.js:37 Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body.

any idea ?

Jim Steil

unread,
Nov 12, 2020, 7:06:55 PM11/12/20
to py4web
For the unexpected argument error, you also have to add the argument here:

def report_clef_in_notation(number):

Did you add that?

-Jim

Toan

unread,
Nov 12, 2020, 8:01:04 PM11/12/20
to py4web
Try add ajax.js in layout.html:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="[[=URL('static', 'js/utils.js') ]]"></script>
<script src="[[=URL('static', 'js/ajax.js') ]]"></script>


 
controllers.py
@action('notation/form_clef_in_notation', method = ['GET'] )
@action.uses('notation/form_clef_in_notation.html')
def form_clef_in_notation():
    ajax = "ajax('%s', ['keywords'], 'step3')" % URL('get_name')
    btn = INPUT(_value="Chấp nhận", _type="button", _class="btn btn-sm btn-primary", _onclick=ajax) 
 
        form = FORM(INPUT(_name='keywords'),btn,DIV(_id='step3'))

    return dict(btn = form) 


 
@action("get_name", method=["GET","POST"] )
def get_name():
    import json, urllib.parse
    print (13, json.dumps(urllib.parse.parse_qs(request.json) ), 345)
    return json.dumps(urllib.parse.parse_qs(request.json) )


ajax.js

stifan kristi

unread,
Nov 12, 2020, 9:07:44 PM11/12/20
to py4web
solution just to add eval() to execute javascript function with 'child_script' ID on another html file
e.g.
templates/caller.html
<script type="text/javascript">
    var onsuccess = function(res) {
        document.getElementById('target').innerHTML = res.data;
        eval(document.getElementById('child_script').innerHTML);
    };
    var onerror = function(res) {
        alert(res);
    };
    function get_fields() {
        var tablature = document.getElementById("tablature").value;
        Q.ajax("POST", "[[=URL('called') ]]", {
            tablature: tablature, 
        }).then(onsuccess).catch(onerror);
    }
</script>

templates/called.html
<script id="child_script">
</script>

perhaps any others elegant or safer way to achieve this are welcome

Jim Steil

unread,
Nov 12, 2020, 9:27:32 PM11/12/20
to stifan kristi, py4web
That's great.  Glad you found a solution.

-Jim


--
You received this message because you are subscribed to a topic in the Google Groups "py4web" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/py4web/fIL8Jmb3hqY/unsubscribe.
To unsubscribe from this group and all its topics, send an email to py4web+un...@googlegroups.com.

stifan kristi

unread,
Nov 14, 2020, 8:16:59 PM11/14/20
to py4web
objective
no need to call eval(document.getElementById('child_script_0').innerHTML);
imagine if there are more than 2 scripts that will executed

can we modify utils.js to support execute scripts like in web2py.js ?

stifan kristi

unread,
Dec 29, 2020, 7:30:24 PM12/29/20
to py4web
is there any way to catch the array data and pass it to py4web function ?
templates/form.html
<form>
<select id = "notes" title = "Notes" onchange = "get_fields()" autofocus multiple>
[[for tbl in in_set_tonic:]]
<option value = [[=tbl[0] ]]>
[[=tbl[1] ]]
</option>
[[pass]]
</select>
</form>
<div id = 'target'></div>

<script>
    var onsuccess = function(res) {
        document.getElementById('target').innerHTML = res.data;
    };
    var onerror = function(res) {
        alert(res);
    };
    function get_fields() {
        //var notes = how to catch array data py4web function ?
        Q.ajax("POST", "[[=URL('scale/report_scales_from_notes') ]]", {
            notes: notes, 
        }).then(onsuccess).catch(onerror);
    }
</script>
Reply all
Reply to author
Forward
0 new messages