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

6 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. The article's discussion of Ext JS image fields shows how a custom component can solve a practical form-handling problem while keeping image updates integrated with the rest of the form. The approach of extending the field component, setting a placeholder for missing images, updating the image source, and refreshing form values after a successful upload is particularly useful for understanding older JavaScript UI development patterns.

      For developers who want to strengthen the JavaScript concepts behind such implementations, JavaScript Course in Chennai can help build familiarity with custom objects, functions, form handling, and dynamic UI behavior. These fundamentals are useful when working with component-based interfaces and client-side updates.

      Delete
    2. The example also demonstrates how a successful server response can be used to update the form through returned JSON data. Developers can build on these client-side skills through JavaScript Training in Chennai, particularly when working with asynchronous requests and dynamic form interactions.

      Delete
    3. Although the example uses Ext JS, the underlying idea of creating reusable UI components is still relevant to modern frontend development. Similar principles can be applied when building structured applications with contemporary frameworks, while practical work such as NextJS Projects can provide opportunities to apply JavaScript concepts in newer web development environments.

      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