Hey guys,
I've recently been trying to work with the Ajax.InPlaceEditor and I
came across a few problems, namely:
1. I didn't see the need for a separate class for collection editors
2. I found it very hard to adapt for even server side verification -
the use of a Ajax.Updater means that the script assumes the input is
valid
3. I don't like dynamically building in elements to the dom: the
framework I'm building has clear distinctions between logic for
content generation (PHP), the layout and styling of pages (HTML/CSS)
and dynamically modifying and changing HTML (JS -> prototype). But
building the form elements as the Ajax.InPlaceEditor does, gives
control over the specific structure of part of the dom to javascript.
Menolikey!
4. Given the above, there's actually no need to have editors or all
the different form element types - this is fantastic
5. I prefer listeners rather than callbacks in this context - an
editor belongs to an element, so an element is updated by it - an
action on an element = events and listeners.
6. There's some ugly aspects to Ajax.InPlaceEditor - the fading in/out
of the hover class means it sometimes remains when the edit mode is
left (it did for me anyway).
7. I don't like having more than one inplaceeditor viewed at one time.
Fine, I could extend Ajax.InPlaceEditor to statically track on the
class all instances, and call Ajax.InPlaceEditor#leaveEditMode() on
all others everytime a new edit mode is entered - but this removes the
form from the dom. If an editor hidden in this way is brought back,
any changes to it are lost.

So given all this I went about a new way of InPlaceEditing. PHP
generates for me an associative array (= array indexed by strings) of
fieldsets, each of which contains assoc. arrays of fields, giving
details such as the name of the field, its current value, and what
form the inplaceeditor should take - for instance, this may be select,
and a further nested array of the select element's vocabulary.

I use the template parser smarty to then create the HTML. Crucially,
within a field container, there's an element containing the value,
which is first shown, and an element containing the form, which is
first hidden. Less work for javascript, and more work for smarty, but
work which it is better than javascript at doing. Also, once the page
is loaded, the whole thing runs faster, plus I always prefer giving a
task to server side code rather than client side whenever possible.

So from there things are simplified incredibly for JS - the bulk of
the work which Ajax.InPlaceEditor is already done, as to enter
editmode the two different elements (value/form) are just toggled in
visibility. Hiding the other editors comes at no cost - the elements
remain in the dom but hidden, so when brought back retain the changes
from the last time they were visible.

Finally, clicking submit waits for a response from the server. PHP
will set a specific header status (419) if the user's input was
invalid. Doing so will lead to a red border being added to the field
to clarify that a problem happened there, and a separate system from
the inplaceeditor I have implemented shows a "popup" div with the
error (even more ideally this would appear floated above the field
speech bubble style).

If the request was successful, the value is updated and indicated
(here pulsated) as you might expect.

This approach in my opinion delegates responsibility to the correct
aspects of a website and leads to less JS code. It's not the best
approach for someone who wants to be able to create an inplaceeditor
without touching any html - but Ajax.InPlaceEditor doesn't include CSS
anyway - and if you have one template to be included server side which
will print an inplaceeditor form whenever you need one, that's work
you need only do once.

I've posted the code below as an example, I'm sure if anyone else
wanted to use this they'd need to add more listeners/callbacks to be
able to specify it. But I thought the InPlaceEditor was one area which
the fantastic scriptaculous/prototype combo hadn't dealt with in the
ideal way so this is an attempt of mine to give something back to a
community that I've inadvertently taken a lot from!

By the way, Trackable and Configurable are implementations of the
excellent mixins by Andrew Dupont, and won't be pasted in here - if
you want them buy his book and get a fantastic read in the process!
Essentially Configurable is dealing with the extension of the options
variable by looking at all the superclasses (none here), then default
options then user specified options. Trackable is tracking all
instances of this class so each can be hidden whenever an edit mode is
entered.

// new in place editor for zehn
zehn.InPlaceEditor = Class.create(Configurable, Trackable, {
  initialize: function(element, options)
  {
    this.element = element;
    this.value_element = element.down('.value');
    this.form_element = element.down('.form');
    this.register(); // register with Trackable
    this.set_options(options); // set options with Configurable
    this.add_listeners();
  },
  add_listeners: function()
  {
    // add hover class name on value hover
    this.value_element.observe('mouseover', function()
    {
      this.value_element.addClassName(this.options.hoverClassName);
    }.bind(this));

    // remove hover class name on value mouse out
    this.value_element.observe('mouseout', function()
    {
      this.value_element.removeClassName(this.options.hoverClassName);
    }.bind(this));

    // when the value is clicked on, go into edit mode
    this.value_element.observe('click', this.enter_edit.bind(this));

    // when the save button is clicked, submit ajax request
    this.form_element.down('input[type=submit]').observe('click',
function(e)
    {
      e.stop();
      this.save();
    }.bind(this));

    // when the cancel link is clicked, leave edit mode
    this.form_element.down('a.cancel').observe('click', function(e)
    {
      e.stop();
      this.leave_edit();
    }.bind(this));
  },
  enter_edit: function()
  {
    this.constructor.instances.each(function(item)
{ item.leave_edit(); });
    this.value_element.hide();
    this.form_element.show();
  },
  leave_edit: function()
  {
    if(this.form_element.visible())
    {
      this.form_element.hide();
      this.value_element.show();
    }
  },
  save: function()
  {
    // submit ajax request to the server
    this._boundWrapperHandler = this.handle_response.bind(this);
    var options = Object.extend(this.options.ajaxOptions, {
      onComplete: this._boundWrapperHandler
    });
    Object.extend(options.parameters, {
      form: Object.toJSON(Form.serialize(this.form_element, true))
    });

    new Ajax.Request(this.options.submitURL, options);
  },
  handle_response: function(response)
  {
    var headerStatus = response.status.toString();
    if(headerStatus[0] == '2')
    {
      // make sure there's no error classes on the form elements
      this.form_element.
 
select('input[type=text],input[type=password],textarea,select')
        .each(function(item) { item.removeClassName('error'); });
      this.value_element.update(response.responseText);
      this.leave_edit();
      this.value_element.pulsate();

      // fire saved event
      this.value_element.fire("attribute:updated");
    }
    else
    {
      if(headerStatus == '419')
      {
        // if we have error add class of form element
        this.form_element.
 
select('input[type=text],input[type=password],textarea,select')
          .each(function(item) { item.addClassName('error'); });
      }
    }
  }
});
zehn.InPlaceEditor.DEFAULT_OPTIONS = {
  hoverClassName: 'editable-hover',
  onSaved: function() {},
  submitURL: '',
  ajaxOptions: {
    method: 'post'
  }
};
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to