Caution, long reply! Sorry...

Sounds like maybe IPE isn't for you. First of all, I prefer generating forms client-side using helpful tools such as Builder.node, and more specialized wrappers that I write myself, and leaving the server to handle only data. I think it is a much cleaner design philosophy, and assuming your site is one that will take advantage of user cache, is much quicker and less demanding of your server and makes debugging a hell of a lot easier. Secondly, you may want to look at my Ajax.Tree class at http://colin.mollenhour.com/ajaxtree if you are dealing with hierarchical data a lot. That code is not the latest as I've been working on implementing sorting into it as well so let me know if it looks like something you're interested in. I've used it in many different ways so far and the class/constructor design has proven itself every time. It handles dynamic addition, deletion of data of multiple types in any place very well, including proper event cleanup to prevent memory leaks.

Back to InPlaceEditor... IMO it badly needs to be rewritten. It is so.. non-extensible. To do anything beyond a single text field requires you to Object.extend and override a bunch of functions, many of which will only have one or two lines of code that are different from the original. There needs to be hooks for such actions as onCreate[Form|Field], onSubmit, onComplete that make proper usage of bind and pass useful variables. The loadExternalText feature is VERY specific and also needs more customization ability (i.e. user created insertion handlers). I'm not really talking about adding many lines of code, just rearranging placement of hooks to where they make sense, make functions overridden by the options parameter rather than having to override Ajax.IPE.prototype.blah, using bind so that this. properties are accessible, and for god's sake, please do not use ajaxOptions for both the loadExternalText AND the onSubmit! If you use an ajaxOptions.onComplete for loadExternalText it will use it for onSubmit as well, forcing you to use the same parameters for both loading text and saving text! I wrestled IPE for hours the other day and all I wanted was to add two select boxes to the form and fill them with a loadExternalText hook. I think when IPE was written, there were great ideas for making it extensible, just lots of oversights that you wouldn't notice until you actually tried to extend it. As it is, extending IPE requires way too many lines of code that are redundant and confusing as hell. I will post one of my several extensions as an example. It is not clean at all since I had to study the IPE code forever to make it work. If anyone can point out an easier way to do something in it I am all ears. Maybe it will give you some ideas as well, but be prepared for some confusing code!

Thanks,
Colin

It creates a form that looks like this:
clickable text:
<span>textarea contents <i>Assigned Task:</i> slave selected innerHTML"</span>
when clicked becomes:
<form><textarea><select[master]><select[slave]><ok><cancel></form>
where master is a parent in a heirarchy that is used to drill down to the value in slave, these select boxes are filled by onEnterEditMode (the master select options will practically always be cached so it loads really fast) what is ugly here is that I had to assign a true value to loadTextUrl and then override loadExternalText with an empty function. Also, to make this work I overrode Ajax.InPlaceEditor.prototype.enterEditMode with a modified function that will call this.options.onEnterEditMode after the form has been created. Yes, if IPE is updated I will have to make sure this function stays compatible with the updates. :(

----------CODE---------
         ....
               Tree.makeNode.bind(this)(element,
[ Tree.addInPlaceEditor.bind(this)(data.text || '---',this.page,{
                           size:90,
                           loadTextURL: true, /* prevent error */
                           onEnterEditMode: function(){
var masterSelect = Builder.node('select',{name:'trade_id'}); var slaveSelect = Builder.node('select',{name:'task_id'}); this.doubleCombo = new Ajax.DoubleCombo(masterSelect, slaveSelect, QCCAdmin.tradesTasksPage, QCCAdmin.taskComboOptions); this.form.insertBefore(slaveSelect,this.editField.nextSibling); this.form.insertBefore(masterSelect,this.editField.nextSibling); this.form.insertBefore(Builder.node('br'),this.editField.nextSibling);
                               this.doubleCombo.master.disabled = true;
                               this.editField.disabled = true;
                           new Ajax.Request( QCCAdmin.page, {
parameters: 'action=getInfo&id='+this.element.id,
                                   onComplete: function(xhr,json){
if(!json) json = json_decode(xhr.responseText); if(json && json.error){ alert(json.error); return; } Element.removeClassName(this.form, this.options.loadingClassName);
                                       this.editField.disabled = false;
this.doubleCombo.master.disabled = false; this.editField.value = json.template_name; fillSelectCached(this.doubleCombo.master,'trades',QCCAdmin.tradesTasksPage,{
                                           onComplete:function(xhr2,json2){
if(json2 && json2.error){ alert(json2.error); return; }
                                               if(json.trade_id){
selectValue(this.doubleCombo.master,json.trade_id); this.doubleCombo.options.onComplete = function(xhr3,json3){ if(json3 && json3.error){ alert(json3.error); return; }
                                                       if(json.task_id){
selectValue(this.doubleCombo.slave,json.task_id);
                                                       }
this.doubleCombo.doHighlight(this.doubleCombo.slave); Field.scrollFreeActivate(this.editField);
                                                   }.bind(this);
this.doubleCombo.onChange();
                                               }
                                           }.bind(this)
                                       });
                                   }.bind(this)
                               });
                         }
                       }) ],
[ Tree.addClickLink.bind(this)('../images/icons/add.png','',this.options.showForm.bindAsEventListener(this,data)), Tree.addClickLink.bind(this)('../images/icons/delete.png','Delete',Tree.deleteNode.bind(this)) ]
               );
this.disposables[this.disposables.length-1].loadExternalText = function(){};
         .....

/* and the permanently modified onEnterEditMode function */
/* Changes:
made hiding of external control an option ("hideExternalControl")
relocated useless hook for onEnterEditMode to after createForm, now uses options
do not scrollFreeActivate if loadTextURL is called
*/
Ajax.InPlaceEditor.prototype.enterEditMode = function(evt) {
   if (this.saving || this.editing) return;
   this.editing = true;
   if(this.options.externalControl && this.options.hideExternalControl){
       Element.hide(this.options.externalControl);
   }
   Element.hide(this.element);
   this.createForm();
   this.element.parentNode.insertBefore(this.form, this.element);
if(this.options.onEnterEditMode) this.options.onEnterEditMode.bind(this)();
   if(!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
   // stop the event to avoid a page refresh in Safari
   if(evt){ Event.stop(evt); }
   return false;
}


Michael Schuerig wrote:


I'm trying to edit a hierarchical structure in-place. I'm not using the Ajax.InPlaceEditor directly, but rather have derived a form editor from it that retrieves forms from the (rails) server. This stuff works mostly, but still needs some serious cleanup and generalization.

What's excluded from "works mostly" and is a real bummer, is adding new objects. The UI is simply made of nested UL elements where the lowest list item in each list is an "Add Foo" link. When it is clicked, my InPlaceFormEditor retrieves a suitable edit form from the server and inserts it into the second but last position. As this item doesn't have a persistent id yet, I set a special one. Then, when the new item is saved, the ajax response contains a snippet of JavaScript that sets the id properly.

Somewhere around this point, things are breaking down for me. Handling of ids, and wiring up newly inserted items with an in-place editor of their own, is getting just too messy. There must be a nice and clean solution, or so I hope.
Michael



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby on 
Rails: Spinoffs" 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/rubyonrails-spinoffs?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to