File upload not working when form is in jDiv

28 views
Skip to first unread message

Ted G

unread,
May 1, 2009, 3:21:55 PM5/1/09
to web2py Web Framework
I've been trying out the new jDiv container, which works well with the
exception of forms containing upload fields. I thought I would start a
new thread for this, as my previous post (included below), seemed out
of place in the old thread.

Note that in the example code below, I've also tried replacing edit()
with:

def edit():
user_profile=db(db.user_profile.owner==auth.user.id).select()[0]
return crud.update(db.user_profile,user_profile.id)


The above change was order to take SQLForm out of the equation and
stick closer to the examples from the website (using crud). The result
is still the same - the form is always accepted, but only changes to
nickname and bio fields are updated to the database, the upload field
is not updated. Take the crud form out of the jDiv and put it directly
on the index page and it works as it should.



-------------- Original Post --------------------

I'm having a problem using upload fields within forms that are used
within jDiv containers. Testing edit() below by itself provides a
form
that I can successfully select and update an image. When I
encapsulate
the same edit() within a jDiv, all fields update with the exception
of
the upload field, even though form.accepts is successful.
The code below contains the essential elements of what I am doing.
Updating the profile edit form correctly forces a refresh of the
other
jDiv that is displaying the profile info, and all fields are updated
correctly when changed, with the exception of the upload field.
Any help with this is appreciated.
P.S. - Despite this hiccup, I am enthusiastic about this addition of
the jDiv container.
db.py model:
--------------------------
db.define_table('user_profile',
SQLField('nickname', type='string'),
SQLField('bio', type='text'),
SQLField('owner', db.auth_user, notnull=True, required=True,
writable=False),
SQLField('image', 'upload', uploadfield='image_data'),
SQLField('image_data', type='blob', default='', writable=False)
)
profile.py controller:
-----------------------------
def index():
# create profile form and profile info containers
profile_form = jDiv('',URL(r=request,f='edit'),_id='1')
user_profile = jDiv('',URL
(r=request,f='profile'),timeout=0.01,_id='2')
return (profile_form = profile_form )
def edit():
user_profile=db(db.user_profile.owner==auth.user.id).select()[0]
form=SQLFORM(db.user_profile, user_profile, deletable=False)
if form.accepts(request.vars,session):
# refresh profile container
jDiv.set(jDiv.trigger('#target_2','load'))
return dict(form=form,user_profile=user_profile)
def profile():
# first get our profile, if we don't yet
# have one, then create it
try:
profile=db(db.user_profile.owner==auth.user.id).select()[0]
except:
db.user_profile.insert(owner=auth.user.id,nickname='n00by')
profile=db(q_profile).select()[0]
return dict(profile=profile)
index.html view:
-----------------------------
{{extend 'layout.html'}}
{{=user_profile}}
<hr/>
{{=profile_form}}
profile.html view:
-------------------------
<img width="100", height="100" src="{{ =URL
(r=request,f='download',args=[profile.image]) }}" />
<h4>{{=auth.user.first_name + " " + auth.user.last_name}}'s Profile</
h4>
<hr/><strong>Nickname: {{ =profile.nickname }}</strong><br/>
email: {{=auth.user.email}}<br/>
{{ =profile.bio }}
edit.html view:
-----------------------
<h3>Edit profile</h3>
{{=form}}
<hr/>

mdipierro

unread,
May 1, 2009, 4:13:10 PM5/1/09
to web2py Web Framework
This may a bug in JS library used to handle jDiv. I will investigate
but it will not be solved overnight.

Massimo

Wes James

unread,
May 1, 2009, 4:38:00 PM5/1/09
to web...@googlegroups.com
ceej is also working on an update to jdiv to be out in a few weeks maybe. (if he get's time to work on it.)  Maybe he can look at this while doing his update :)

-wj

Ted G

unread,
May 1, 2009, 6:52:18 PM5/1/09
to web2py Web Framework
On a related note:

What is the best way to trigger a Javascript function upon a jDiv load
or form submit within a jDiv? For example:

I have a list of customers shown in jDiv1. User clicks a link for a
given customer and I load customer's update form in jDiv2 (which I
also make visible at that point). When user submits form, I hide
jDiv2, reload jDiv1.

Having the customer link call a Javascript function to show jDiv2 and
call jDivLoad manually with the appropriate callback for the given
customerID works fine. I'm not sure of the best way to have jDiv2's
submit success result in another Javascript function call, where I
reload jDiv1 and hide jDiv2?

I note in the example, that jDiv.set(jDiv.trigger('#target_1','load'))
is used to force a refresh of another div after submitting a form. Can
this mechanism be used instead to call a javascript function? Ideally,
I wouldn't want one jDiv to have to know about the other, I would
prefer to keep my partials loosely coupled and bind their behavior
with one another together through javascript on the client side in
order to facilitate better re-use of those partials.




On May 1, 1:38 pm, Wes James <compte...@gmail.com> wrote:
> ceej is also working on an update to jdiv to be out in a few weeks maybe.
> (if he get's time to work on it.)  Maybe he can look at this while doing his
> update :)
>
> -wj
>

Wes James

unread,
May 1, 2009, 6:59:56 PM5/1/09
to web...@googlegroups.com
Right now with my time tracker i have:

FormjDiv
HoursjDiv
Last20itemsjDiv

When I submit FormjDiv it goes away (the lower part of the jDiv), but it refreshes HoursjDiv on the callback.  At the end of the code for HoursjDiv i have it trigger FormjDiv to open it up again with a new form (like click the first top button) and also to refresh contents in Last20itemsjDiv.

There's probably a better way to do this, but this works for now.

-wj

Ted G

unread,
May 1, 2009, 7:29:18 PM5/1/09
to web2py Web Framework
That is how I've resorted to for now as well (with the exception of
the file upload part of my form not working as mentioned at start of
this thread).

Your example is a good one and similar to my own. Using your example,
I want to be able to re-use the FormjDiv partial in other pages of the
site. It is hard to re-use FormjDiv when it is coupled to HoursjDiv on
the controller side (as a result of FormjDiv refreshing HoursjDiv).
Ideally, I would want to have FormjDiv trigger a Javascript function,
either a specific named one I provide, or have it always call a
generic function - eg. jDivLoadSuccess(target) that I can implement on
the client side to take action.

In the generic case, the jDiv, upon successful submission would call
jDivLoadSuccess(target) passing its own target_id. I could then use
this to determine what action just took place and what action to take
next. In your example, if this function was called as a result of
target_FormjDiv submitting, then I would call jDivLoad
("target_HoursjDiv", myURL, null) to force a load of HoursJDiv. I
could also then hide FormJDiv, refresh it, and any other actions I
wish to take.

Not being forced to couple jDiv behavior together on the server side,
opens up much better scenarios for customizing interaction between
jDiv's and re-use of those jDiv's.


On May 1, 3:59 pm, Wes James <compte...@gmail.com> wrote:
> Right now with my time tracker i have:
>
> FormjDiv
> HoursjDiv
> Last20itemsjDiv
>
> When I submit FormjDiv it goes away (the lower part of the jDiv), but it
> refreshes HoursjDiv on the callback.  At the end of the code for HoursjDiv i
> have it trigger FormjDiv to open it up again with a new form (like click the
> first top button) and also to refresh contents in Last20itemsjDiv.
>
> There's probably a better way to do this, but this works for now.
>
> -wj
>

Ted G

unread,
May 4, 2009, 3:36:01 PM5/4/09
to web2py Web Framework
After some more digging, it would seem the problem with the file
upload failing may be that multipart forms cannot be submitted via
ajax. Given that file upload is a very common feature of forms, and
that forms are very common implementations of a partial (as shown in
the jDiv demos), not having file upload within a jDiv form would limit
it's usefulness.

jQuery already has an ajax form plugin that can handle file uploads in
ajax forms by using a hidden iframe. Since web2py is already using
jQuery would it make sense to incorporate this plugin as part of the
jDiv functionality for submitting forms? Incorporating this plugin
would also open the door to other form behavior customizations on the
client side such as progress meters, etc, which would also be very
useful.

Ted


On May 1, 4:29 pm, Ted G <tedg...@gmail.com> wrote:
> That is how I've resorted to for now as well (with the exception of
> the file upload part of my form not working as mentioned at start of
> this thread).
>
> Your example is a good one and similar to my own. Using your example,
> I want to be able to re-use the FormjDiv partial in other pages of the
> site. It is hard to re-use FormjDiv when it is coupled to HoursjDiv on
> the controller side (as a result of FormjDiv refreshing HoursjDiv).
> Ideally, I would want to have FormjDiv trigger a Javascript function,
> either a specific named one I provide, or have it always call a
> generic function - eg. jDivLoadSuccess(target) that I can implement on
> the client side to take action.
>
> In the generic case, thejDiv, upon successful submission would call
> jDivLoadSuccess(target) passing its own target_id. I could then use
> this to determine what action just took place and what action to take
> next. In your example, if this function was called as a result of
> target_FormjDiv submitting, then I would call jDivLoad
> ("target_HoursjDiv", myURL, null) to force a load of HoursJDiv. I
> could also then hide FormJDiv, refresh it, and any other actions I
> wish to take.
>
> Not being forced to couplejDivbehavior together on the server side,
> opens up much better scenarios for customizing interaction betweenjDiv'sand re-use of thosejDiv's.
>
> On May 1, 3:59 pm, Wes James <compte...@gmail.com> wrote:
>
> > Right now with my time tracker i have:
>
> > FormjDiv
> > HoursjDiv
> > Last20itemsjDiv
>
> > When I submit FormjDiv it goes away (the lower part of thejDiv), but it

mdipierro

unread,
May 4, 2009, 4:25:30 PM5/4/09
to web2py Web Framework
I agree.

Ted G

unread,
May 4, 2009, 5:28:03 PM5/4/09
to web2py Web Framework
A bit more follow up on this topic of taking action after an ajax load
call on the jDiv.

Right now the jDivLoad function in the provided web2py_ajax.html makes
the assumption that the command to be evaluated on completion of the
load will be provided by the web2py host application. As mentioned in
my other response to Wes, while I feel this is definitely a valuable
option for those who wish to control the flow of the client from the
host, in more complex apps where there is re-use of partials among web
pages, it may be preferable to control the flow of the client via
javascript in the client itself in order to have a looser coupling
between jDiv's.

Also, there are many cases where the function you wish to call upon
loading a jDiv is something other than the default provided by
jDivLoad (like auto selecting the first item in a list of items for
showing in another jDiv). Right now, this can be specified by jDiv.set
('command') on the host side prior to completing the request handler,
but, again, this requires that my request handler have knowledge of
the function in the view that will be called.

Since jDivLoad is wrapping up the jquery .ajax call for the
implementation of the load, can we have jDivLoad also accept
parameters for the jQuery .ajax beforeSend, success and complete
options and evaluate those commands along with the custom commands
that might be passed back from the host in the response? This would
allow the client more options for sophisticated behavior.

Ted


On May 1, 3:59 pm, Wes James <compte...@gmail.com> wrote:
> Right now with my time tracker i have:
>
> FormjDiv
> HoursjDiv
> Last20itemsjDiv
>
> When I submit FormjDiv it goes away (the lower part of the jDiv), but it
> refreshes HoursjDiv on the callback.  At the end of the code for HoursjDiv i
> have it trigger FormjDiv to open it up again with a new form (like click the
> first top button) and also to refresh contents in Last20itemsjDiv.
>
> There's probably a better way to do this, but this works for now.
>
> -wj
>

mdipierro

unread,
May 4, 2009, 9:37:32 PM5/4/09
to web2py Web Framework
Can you provide an example? I am not sure this cannot be done already.

Ted G

unread,
May 5, 2009, 3:22:38 AM5/5/09
to web2py Web Framework
Sure.

jDiv1 = Customer Detail/Form
jDiv2 = Invoice List
jDiv3 = Invoice Detail/Invoice Form

The above jDiv's are partials that are re-used in a number of
different views. jDiv2 is not always used in a view where jDiv3 is
used for example.

One of the views does use all of the above jDivs. In this view the
user may:

A. View/Edit the Customer Detail. This is accomplished using an edit
link with onclick="editCustomer()". editCustomer() calls jDivLoad for
jDiv1, providing the URL to the customer form request handler, thus
replacing the customer detail view with the customer edit form. Upon
submitting the form, jDiv1 is reloaded with the customer detail view,
showing any changes. While the reload can be accomplished by doing a
redirect to the customer detail after form accept, this is not the
behavior always required when this form is used within a jDiv. To
ensure the form can be re-used, I need to either pass additional
parameters to the customer form request handler and take the
appropriate action there after form submit based on what view the form
is being used in, or I can keep the form handler generic, and instead,
using javascript in the view that uses the form, call jDivLoad with
the appropriate URL after successful form submit.

B. When view is first loaded and invoice list jDiv2 is populated, the
first invoice is selected and shown in jDiv3. I could have jDiv2's
request handler force a load of jDiv3 with the first item in the list,
but this means jDiv2 must know about jDiv3 in jDiv2's request handler.
This is not always how jDiv2 is used in a view. Better solution is to
simply use jquery after jDiv2 is loaded, to select the first <a> in
the invoice list, set it's class to "selected" so that CSS can
highlight it, and then call jDivLoad with the appropriate URL to load
jDiv3. This makes sense as I would re-use that same javascript code
for the onclick event of each invoice item in the list, allowing the
user to select an invoice in the list, highlight it and see it
displayed in jDiv3.

C. Like the customer form, the invoice detail can be edited. When the
user chooses to edit, jDiv3 is reloaded with the form view. When the
form is submitted, jDiv3 is reloaded with the detail view. Again, the
invoice form is not always used in this manner (redirecting back to
the detail view), so it does not make sense to code this behavior in
the request handler for the invoice form. It makes more sense to call
jDivLoad as a result of clicking the edit link from a javascript
function in the view and display the form. Then once the form is
submitted, callback to a javascript function that does a jDivLoad to
reload the invoice detail for display.

So what I'm trying to illustrate in a very long winded way, is that
while there is a way to accomplish the three actions listed above by
using redirection and setting the trigger command for the jDiv in the
request handler, it means that I would have to have code in the
request handlers for each of the different use cases of the jDivs. It
would be preferable to keep the jDiv's generic and not dependent on
one another, and instead use javascript in the view to dictate how the
jDiv's behave with one another.

Which leads me finally, to my previous suggestion:

My understanding is that currently, the only way to have a javascript
function called upon completion of a jDivLoad is to specify that
function by using jDiv.set in the request handler of the URL being
loaded. To keep jDiv's loosely coupled from one another and thus more
re-useable, it would be preferable to have the option to also set the
completion callback when jDivLoad is being called directly from
javascript within the view. Currently jDivLoad only accepts target,
url and params as parameters. It then sets up the beforeSend, success
and complete options of the jquery .ajax call that is used to load the
URL for the jDiv.

Since jDivLoad is encapsulating the behavior of the jQuery .ajax call,
you cannot specify callbacks for beforeSend, success, or complete,
which you would normally use when using jquery .ajax. Ideally, if
jDivLoad extended the behavior of the jQuery .ajax by introducing its
custom behavior, but still allowed you to specify the callbacks, you
would have the option of controlling interaction of the jDivs with one
another completely through javascript without involving the request
handlers.

I hope I've explained this well enough.

Ted

Ted G

unread,
May 5, 2009, 7:17:12 PM5/5/09
to web2py Web Framework
I've modified jDivLoad to incorporate the jQuery form plugin in order
to enable file uploads within forms that are part of a jDiv. I thought
I would post the change below so that whoever is working on jDiv can
incorporate this functionality in a better way than the hack I've done
for my purposes. Note that as per other discussions in this thread,
I've also added callbacks that fire on jDiv load and after successful
ajax form submission. I've done this for my implementation so that I
can specify callbacks when calling jDivLoad from javascript in my
view, instead of relying on setting the callback using jDiv.set in the
request handler.

The important thing is that uploading of files within a jDiv now works
like a charm.

Ted

---- Modified jDivLoad ----


function jDivLoad(target,callback,params,onLoadCmd,onSubmitCmd) {
$(target).attr('status','loading').trigger('loading',callback);


$.ajax({'type':'post','url':callback, 'data':params,
'beforeSend':function(xhr){
xhr.setRequestHeader('web2py-jDiv-Name',target) },
'success':function(text){ $(target).html(text); },
'complete':function(xhr,text){
$(document).trigger('set_form_constraints');
$(target +' input[type="submit"]').click(function(){
loc=xhr.getResponseHeader('Location');
if((this.form.action=='' || this.form.action=='.') && loc)
this.form.action=loc;
$('form').ajaxSubmit({
'target':target,
'url':loc,
'type':'post',
'success':function(text){ if(onSubmitCmd) eval
(onSubmitCmd) },
});
return false;
});

command=xhr.getResponseHeader('web2py-jDiv-Commands');
if(command) eval(command);
if(onLoadCmd) eval(onLoadCmd);
$(target).attr('status','loaded').trigger('loaded',callback);
} });

mdipierro

unread,
May 5, 2009, 7:41:49 PM5/5/09
to web2py Web Framework
Fantastic! Thank you.

Massimo
Reply all
Reply to author
Forward
0 new messages