Sunday, July 19, 2009

Ext JS Image Field

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.
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);
                  },

4 comments:

  1. 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.

    ReplyDelete
    Replies
    1. Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging. If anyone wants to become a Front end developer learn from Javascript Training in Chennai . or learn thru Javascript Training in Chennai. Nowadays JavaScript has tons of job opportunities on various vertical industry. JavaScript Training in Chennai

      Delete
  2. Thanks for the example, saved me some work.

    One 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.

    ReplyDelete