8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-26 22:52:26 +00:00

Valable sans JS

This commit is contained in:
Maël Kervella 2017-10-15 18:47:14 +00:00
parent 25d460cb91
commit e29f7bc406
2 changed files with 211 additions and 103 deletions

View file

@ -129,11 +129,13 @@ def generate_ipv4_mbf_param( form, is_type_tt ):
i_engine = { 'ipv4': generate_ipv4_engine( is_type_tt ) } i_engine = { 'ipv4': generate_ipv4_engine( is_type_tt ) }
i_match_func = { 'ipv4': generate_ipv4_match_func( is_type_tt ) } i_match_func = { 'ipv4': generate_ipv4_match_func( is_type_tt ) }
i_update_on = { 'ipv4': [f_type_id( is_type_tt )] } i_update_on = { 'ipv4': [f_type_id( is_type_tt )] }
i_gen_select = { 'ipv4': False }
i_mbf_param = { i_mbf_param = {
'choices': i_choices, 'choices': i_choices,
'engine': i_engine, 'engine': i_engine,
'match_func': i_match_func, 'match_func': i_match_func,
'update_on': i_update_on 'update_on': i_update_on,
'gen_select': i_gen_select
} }
return i_mbf_param return i_mbf_param

View file

@ -113,12 +113,29 @@ def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
A dict of list of ids that the values depends on. The engine A dict of list of ids that the values depends on. The engine
and the typeahead properties are recalculated and reapplied. and the typeahead properties are recalculated and reapplied.
Example : Example :
'addition' : { 'update_on' : {
'field_A' : [ 'id0', 'id1', ... ] , 'field_A' : [ 'id0', 'id1', ... ] ,
'field_B' : ... , 'field_B' : ... ,
... ...
} }
gen_select (optional)
A dict of boolean telling if the form should either generate
the normal select (set to true) and then use it to generate
the possible choices and then remove it or either (set to
false) generate the choices variable in this tag and do not
send any select.
Sending the select before can be usefull to permit the use
without any JS enabled but it will execute more code locally
for the client so the loading might be slower.
If not specified, this variable is set to true for each field
Example :
'gen_select' : {
'field_A': True ,
'field_B': ... ,
...
}
See boostrap_form_ for other arguments See boostrap_form_ for other arguments
**Usage**:: **Usage**::
@ -146,6 +163,11 @@ def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
[ '<field1>': '<update_on1>' [ '<field1>': '<update_on1>'
[, '<field2>': '<update_on2>' [, '<field2>': '<update_on2>'
[, ... ] ] ] [, ... ] ] ]
} ],
[, 'gen_select': {
[ '<field1>': '<gen_select1>'
[, '<field2>': '<gen_select2>'
[, ... ] ] ]
} ] } ]
} ] } ]
[ <standard boostrap_form parameters> ] [ <standard boostrap_form parameters> ]
@ -163,6 +185,7 @@ def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
engine = param.get('engine', {}) engine = param.get('engine', {})
match_func = param.get('match_func', {}) match_func = param.get('match_func', {})
update_on = param.get('update_on', {}) update_on = param.get('update_on', {})
gen_select = param.get('gen_select', {})
hidden_fields = [h.name for h in form.hidden_fields()] hidden_fields = [h.name for h in form.hidden_fields()]
html = '' html = ''
@ -184,40 +207,43 @@ def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
multiple = f_value.widget.allow_multiple_selected multiple = f_value.widget.allow_multiple_selected
f_bound = f_value.get_bound_field( form, f_name ) f_bound = f_value.get_bound_field( form, f_name )
if gen_select.get(f_name, True) :
html += render_field(
f_bound,
*args,
**kwargs
)
f_value.widget = TextInput( f_value.widget = TextInput(
attrs = { attrs = {
'name': 'mbf_'+f_name, 'name': 'mbf_'+f_name,
'placeholder': f_value.empty_label 'placeholder': f_value.empty_label
} }
) )
html += render_field( replace_input = render_field(
f_value.get_bound_field( form, f_name ), f_value.get_bound_field( form, f_name ),
*args, *args,
**kwargs **kwargs
) )
if multiple : if not gen_select.get(f_name, True) :
content = mbf_js( html += replace_input
f_name,
f_value, content = mbf_js(
f_bound, f_name,
multiple, f_value,
choices, f_bound,
engine, multiple,
match_func, replace_input,
update_on choices,
) engine,
else : match_func,
content = hidden_tag( f_bound, f_name ) + mbf_js( update_on,
f_name, gen_select
f_value, )
f_bound, if not multiple and not gen_select.get(f_name, True) :
multiple, content += hidden_tag( f_bound, f_name )
choices,
engine,
match_func,
update_on
)
html += render_tag( html += render_tag(
'div', 'div',
content = content, content = content,
@ -257,12 +283,14 @@ def hidden_tag( f_bound, f_name ):
} }
) )
def mbf_js( f_name, f_value, f_bound, multiple, def mbf_js( f_name, f_value, f_bound, multiple, replace_input,
choices_, engine_, match_func_, update_on_ ) : choices_, engine_, match_func_, update_on_, gen_select_ ) :
""" The whole script to use """ """ The whole script to use """
gen_select = gen_select_.get( f_name, True )
choices = ( mark_safe( choices_[f_name] ) if f_name in choices_.keys() choices = ( mark_safe( choices_[f_name] ) if f_name in choices_.keys()
else default_choices( f_value ) ) else default_choices( f_value, f_bound, gen_select ) )
engine = ( mark_safe( engine_[f_name] ) if f_name in engine_.keys() engine = ( mark_safe( engine_[f_name] ) if f_name in engine_.keys()
else default_engine ( f_name ) ) else default_engine ( f_name ) )
@ -272,82 +300,120 @@ def mbf_js( f_name, f_value, f_bound, multiple,
update_on = update_on_[f_name] if f_name in update_on_.keys() else [] update_on = update_on_[f_name] if f_name in update_on_.keys() else []
if multiple : if gen_select :
js_content = ( if multiple :
'var choices_{f_name} = {choices};' js_content = (
'var engine_{f_name};' '$( "#{input_id}" ).ready( function() {{'
'var setup_{f_name} = function() {{' 'var choices_{f_name} = {choices};'
'engine_{f_name} = {engine};' '{del_select}'
'$( "#{input_id}" ).tokenfield( "destroy" );' 'var engine_{f_name};'
'$( "#{input_id}" ).tokenfield({{typeahead: [ {datasets} ] }});' 'var setup_{f_name} = function() {{'
'}};' 'engine_{f_name} = {engine};'
'$( "#{input_id}" ).bind( "tokenfield:createtoken", {create} );' '$( "#{input_id}" ).tokenfield( "destroy" );'
'$( "#{input_id}" ).bind( "tokenfield:edittoken", {edit} );' '$( "#{input_id}" ).tokenfield({{typeahead: [ {datasets} ] }});'
'$( "#{input_id}" ).bind( "tokenfield:removetoken", {remove} );' '}};'
'{updates}' '$( "#{input_id}" ).bind( "tokenfield:createtoken", {tok_create} );'
'$( "#{input_id}" ).ready( function() {{' '$( "#{input_id}" ).bind( "tokenfield:edittoken", {tok_edit} );'
'setup_{f_name}();' '$( "#{input_id}" ).bind( "tokenfield:removetoken", {tok_remove} );'
'{init_input}' '{tok_updates}'
'}} );' 'setup_{f_name}();'
).format( '{tok_init_input}'
f_name = f_name, '}} );'
choices = choices, )
engine = engine, else :
input_id = input_id( f_bound ), js_content = (
datasets = default_datasets( f_name, match_func ), '$( "#{input_id}" ).ready( function() {{'
create = tokenfield_create( f_name, f_bound ), 'var choices_{f_name} = {choices};'
edit = tokenfield_edit( f_name, f_bound ), '{del_select}'
remove = tokenfield_remove( f_name, f_bound ), '{gen_hidden}'
updates = ''.join( [ ( 'var engine_{f_name};'
'$( "#{u_id}" ).change( function() {{' 'var setup_{f_name} = function() {{'
'setup_{f_name}();' 'engine_{f_name} = {engine};'
'{reset_input}' '$( "#{input_id}" ).typeahead( "destroy" );'
'}} );' '$( "#{input_id}" ).typeahead( {datasets} );'
).format( '}};'
u_id = u_id, '$( "#{input_id}" ).bind( "typeahead:select", {typ_select} );'
reset_input = tokenfield_reset_input( f_bound ), '$( "#{input_id}" ).bind( "typeahead:change", {typ_change} );'
f_name = f_name '{typ_updates}'
) for u_id in update_on ] 'setup_{f_name}();'
), '{typ_init_input}'
init_input = tokenfield_init_input( f_name, f_bound ), '}} );'
) )
else : else :
js_content = ( if multiple :
'var choices_{f_name} = {choices};' js_content = (
'var engine_{f_name};' 'var choices_{f_name} = {choices};'
'var setup_{f_name} = function() {{' 'var engine_{f_name};'
'engine_{f_name} = {engine};' 'var setup_{f_name} = function() {{'
'$( "#{input_id}" ).typeahead( "destroy" );' 'engine_{f_name} = {engine};'
'$( "#{input_id}" ).typeahead( {datasets} );' '$( "#{input_id}" ).tokenfield( "destroy" );'
'}};' '$( "#{input_id}" ).tokenfield({{typeahead: [ {datasets} ] }});'
'$( "#{input_id}" ).bind( "typeahead:select", {select} );' '}};'
'$( "#{input_id}" ).bind( "typeahead:change", {change} );' '$( "#{input_id}" ).bind( "tokenfield:createtoken", {tok_create} );'
'{updates}' '$( "#{input_id}" ).bind( "tokenfield:edittoken", {tok_edit} );'
'$( "#{input_id}" ).ready( function() {{' '$( "#{input_id}" ).bind( "tokenfield:removetoken", {tok_remove} );'
'setup_{f_name}();' '{tok_updates}'
'{init_input}' '$( "#{input_id}" ).ready( function() {{'
'}} );' 'setup_{f_name}();'
).format( '{tok_init_input}'
f_name = f_name, '}} );'
choices = choices,
engine = engine,
input_id = input_id( f_bound ),
datasets = default_datasets( f_name, match_func ),
select = typeahead_select( f_bound ),
change = typeahead_change( f_bound ),
updates = ''.join( [ (
'$( "#{u_id}" ).change( function() {{'
'setup_{f_name}();'
'{reset_input}'
'}} );'
).format(
u_id = u_id,
reset_input = typeahead_reset_input( f_bound ),
f_name = f_name
) for u_id in update_on ]
),
init_input = typeahead_init_input( f_name, f_bound ),
) )
else :
js_content = (
'var choices_{f_name} = {choices};'
'var engine_{f_name};'
'var setup_{f_name} = function() {{'
'engine_{f_name} = {engine};'
'$( "#{input_id}" ).typeahead( "destroy" );'
'$( "#{input_id}" ).typeahead( {datasets} );'
'}};'
'$( "#{input_id}" ).bind( "typeahead:select", {typ_select} );'
'$( "#{input_id}" ).bind( "typeahead:change", {typ_change} );'
'{typ_updates}'
'$( "#{input_id}" ).ready( function() {{'
'setup_{f_name}();'
'{typ_init_input}'
'}} );'
)
js_content = js_content.format(
f_name = f_name,
choices = choices,
del_select = del_select( f_bound, replace_input ),
gen_hidden = gen_hidden( f_bound ),
engine = engine,
input_id = input_id( f_bound ),
datasets = default_datasets( f_name, match_func ),
typ_select = typeahead_select( f_bound ),
typ_change = typeahead_change( f_bound ),
tok_create = tokenfield_create( f_name, f_bound ),
tok_edit = tokenfield_edit( f_name, f_bound ),
tok_remove = tokenfield_remove( f_name, f_bound ),
typ_updates = ''.join( [ (
'$( "#{u_id}" ).change( function() {{'
'setup_{f_name}();'
'{reset_input}'
'}} );'
).format(
u_id = u_id,
reset_input = typeahead_reset_input( f_bound ),
f_name = f_name
) for u_id in update_on ]
),
tok_updates = ''.join( [ (
'$( "#{u_id}" ).change( function() {{'
'setup_{f_name}();'
'{reset_input}'
'}} );'
).format(
u_id = u_id,
reset_input = tokenfield_reset_input( f_bound ),
f_name = f_name
) for u_id in update_on ]
),
tok_init_input = tokenfield_init_input( f_name, f_bound ),
typ_init_input = typeahead_init_input( f_name, f_bound ),
)
return render_tag( 'script', content=mark_safe( js_content ) ) return render_tag( 'script', content=mark_safe( js_content ) )
@ -403,9 +469,21 @@ def tokenfield_reset_input( f_bound ) :
input_id = input_id( f_bound ), input_id = input_id( f_bound ),
) )
def default_choices( f_value ) : def default_choices( f_value, f_bound, gen_select ) :
""" The JS script creating the variable choices_<fieldname> """ """ The JS script creating the variable choices_<fieldname> """
return '[{objects}]'.format( if gen_select :
c = ( 'function plop(o) {{'
'var c = [];'
'for( let i=0 ; i<o.length ; i++) {{'
'c.push( {{ key: o[i].value, value :o[i].text }} );'
'}}'
'return c;'
'}} ($("#{select_id}")[0].options)'
).format (
select_id = input_id( f_bound )
)
else :
c = '[{objects}]'.format(
objects = ','.join( objects = ','.join(
[ '{{key:{k},value:"{v}"}}'.format( [ '{{key:{k},value:"{v}"}}'.format(
k = choice[0] if choice[0] != '' else '""', k = choice[0] if choice[0] != '' else '""',
@ -414,6 +492,34 @@ def default_choices( f_value ) :
) )
) )
return c
def del_select( f_bound, replace_input ) :
""" The JS script to delete the select if it has been generated
and replace it with an input. """
return ( 'var p = $("#{select_id}").parent()[0];'
'var new_input = `{replace_input}`;'
'p.innerHTML = new_input;'
).format(
select_id = input_id( f_bound ),
replace_input = replace_input
)
def gen_hidden( f_bound ):
""" The JS script to add a hidden tag to store the value. """
return ( 'var d = $("#{custom_div_id}")[0];'
'var i = document.createElement("input");'
'i.id = "{hidden_id}";'
'i.name = "{name}";'
'i.value = "";'
'i.type = "hidden";'
'd.appendChild(i);'
).format(
custom_div_id = custom_div_id( f_bound ),
hidden_id = hidden_id( f_bound ),
name = f_bound.html_name
)
def default_engine ( f_name ) : def default_engine ( f_name ) :
""" The JS script creating the variable engine_<field_name> """ """ The JS script creating the variable engine_<field_name> """
return ( return (