Yesterday I spent way too much time on another simple thing in EXT that I would have expected to work out of the box. What I needed was to upload a company logo to a profile and, of course, display that logo in the form. While it's very simple to display an image in a panel or box, it's a totally different thing to display the in image in the form and update it together with other form fields. What I needed is something like this:
and after form submit it should automatically load the new image:
For image upload I used the image upload plugin from EXT examples. To display the image I needed to extend the field component like this:
Ext.form.ImageField = Ext.extend(Ext.form.Field, { autoCreate: {tag: 'img'} ,setValue: function(new_value){ if (new_value == undefined || new_value == null) { this.el.dom.src = '/images/no_image.png'; } else { this.el.dom.src = '/images/thumbnail/' + new_value; } } ,initValue : function(){ this.setValue(this.value); } ,initComponent: function() { Ext.apply(this, { }); Ext.form.ImageField.superclass.initComponent.apply(this); } }); Ext.reg('image_field', Ext.form.ImageField);
This is a bit simplified version as I removed the rails specific code, but anyway the main points are here:
line 2 creates image tag (rather than the default text field tag)
line 5 defaults the image src property to a placeholder (in case there's no image uploaded)
line 7 - in case there is a valid image it will change the src path to that image - in my case it's routed to Images controller and action thumbnail which returns a downscaled version of the image.
line 2 creates image tag (rather than the default text field tag)
line 5 defaults the image src property to a placeholder (in case there's no image uploaded)
line 7 - in case there is a valid image it will change the src path to that image - in my case it's routed to Images controller and action thumbnail which returns a downscaled version of the image.
Other than this don't forget to set the FormPanel's fileUpload: true and refresh the form on submit success. I usually send the updated object JSON in the response to update action.:
def update contact = Contact.find(params[:id]) on_bind(contact) success = contact.save contact = Contact.find(contact.id) if success logger.info("saved") respond_to do |format| format.ext_json do json = contact.to_ext_json(:success => success, :methods => [:image_id], :includes => [:address], :messages => {:notice => l(:contact_updated), :warning => l(:contact_update_error)}) render :json => json #:json => {:success => success} headers["Content-Type" ] = "text/html" end end end
!!!Notice line 16 - you have to manually set the content type to "text/html" because rails will default to "text/javascript" which will not work with file uploads!!! It works with fileUpload: false but it doesn't with fileUpload: true. I don't really know if Rails or EXT is right here but did take me some time to figure this out.
this will produce the following JSON (I removed the unnecessary fields):
{"success": true, "messages": {"notice": "Contact was successfully updated.", "warning": "contact_update_error"}, "data": {"contact[image_id]": 15, "contact[active]": false, "contact[client_account_id]": 2, "contact[client_account_no]": "100", "contact[created_at]": "2009-07-07T10:31:54+08:00", "contact[credit_term_id]": 1}}Now in the forms success method I can use the result.data to update the form:
success: function(form, action) { form.setValues(action.result.data); },
Tried this however it doesn't work to well on a ViewPort layout nested within TabPanels, the ImageField always duplicates itself when you flip between the two. Otherwise just using it within a window it works fine.
ReplyDeleteThanks for the example, saved me some work.
ReplyDeleteOne small improvement, if you need to use more than 1 in a form, or as I did. different images in different forms in a tab panel the you need to change
autoCreate: {tag: 'img'}
to
defaultAutoCreate: { tag:'img' }
otherwise all the images share the same dom element and only 1 changes when the data for any form changes.
great
ReplyDelete