Question

OK, so first off, let's start with me acknowledging that the bind( ... ) way of binding Lift forms is so last week! :) I do know that, and I just haven't gone back to update this code yet. Also, I trust now that there's some really slick Lifty way to do this. That's what I seek. I'm stumped as to even how to hack something together. That said...

I have a list of Items that I initially display non-editable, and the title of each Item is an ajax-enabled link that calls to the server and replaces that line-item with an editable form of the Item (I use SetHtml to swap the form in at the < li> that listed that Item). "parent" Items List view looks something like this

< form data-lift="form.ajax">
< div data-lift="creategamewizard?multipart=true" id="wizardform">
< ul>
< li>Item 1< /li>
< li>Item 2< /li>
< /ul>
some more form elements
< button>Submit< /button>
< input type="hidden" id='298356928734' />
< /div>
< form>

This ajax submit (via the hidden field) calls processSubmit().

The SetHtml that swaps in the editableItem form looks something like this.
NOTE: At the end of the following listing, the "save" binding has no server-side code tied to it because the "parent" submit button is already on the page, and when I put another hidden field in this binding or tried to tie any code directly to the Edit Item Save button, that code and the "parent" submit got triggered. So the approach below was to try to use the "parent" submit for both the parent submit as well as the Edit Item submit.

<a href="javascript://" onclick={ajaxOnClickHandler(editItemClickHandler(item.id.get))}>{item.title.get}</a>

def ajaxOnClickHandler(jsHandler: ()=>JsCmd) =
{
    SHtml.onEvent( e => jsHandler()).toJsCmd+";return false;"
}
def editItemClickHandler(itemId: String): ()=>JsCmd = ()=>
{
    trace.logAjaxComm("ExistingItem.Edit()")
    JsCmds.SetHtml("LiId"+itemId, getEditableItem(promo) )
}
def getEditableItem(itemId) =
{
    bind( ...
        "promotitle" -> SHtml.text(editablePromo.get.promotitle.is,
        (s:String) => {
            trace.logUIDataManipulation("Saving new promo Title["+s+"]");
            editablePromo.get.promotitle(s)
        }, "id" -> "promotitle"),
        "save" -> SHtml.button("Save", ()=> {})
    )
}

Then when the user selects an Item, and the editable Item form is plugged in, there's "another" submit button that should ajax submit the form data for that item, and then swap back in the (now updated) nonEditable version of the data.

The problem for me is the submission. In addition to the Edit Item form above, I've got a ajaxified submit button on the "parent" non-editable list page to handle submitting some fields below the list. The Edit Item "save"-> binding adds a button, which should do (and in fact does) nothing for itself, but it does trigger the "Parent" submit button. And I route that submit to do the save of the Edit Item form.

The non-editable Item and the editable item code swaps fine, but changes made in the editable Item form is not saved, and I figured out that that was happening because the elements in the editable Item form are not being submitted at all, following is an example of a log message I don't see at all...

bind( ... "promotitle" -> SHtml.text(editablePromo.get.promotitle.is,
    (s:String) => {
        trace.logUIDataManipulation("Saving new promo Title["+s+"]");
        editablePromo.get.promotitle(s)
    }, "id" -> "promotitle")
)

In a normal ajaxified form, all element handlers are called (if there are changes to the field, I guess...) in order of rendering, with the submit/hidden elements' handlers being called last (if they're last in the bind list.

so finally, let's get around to my question: if you're doing in-place editing like this, how do I manage 2 submit buttons (the one for the non-editable list page plus the additional one that gets added when editing an item)? I'm sure I don't need to refresh the page, but I can't figure out how you'd do this with Ajax. Maybe alternatively, the in-place editable form can be submitted as a non-submit ajax action, ie. somehow that doesn't trigger the parent submit?

Was it helpful?

Solution

For anyone tripping over this question, I figured I'd share the solution I eventually found...

1)The problem was that the submit (for AJAX this is the hidden html tag) happened before the editable Item's field handlers were called. So when the AJAX update that collapsed the editable Item back into just a non-Editable list item, the data hadn't yet been updated. So what was displayed in non-editable form didn't show the update, yet if I refreshed the page in the browser, the update had been saved to the database and now showed properly.

2)The reason for the mal-ordering is that Lift assigns each form tag's server-side handler an id (which are "monotonically increasing" with an additional string added to the end). That's fine until you do an ajax live-update of a form and add fields (as I did when I inserted the Editable Item fields). These newly-added fields were assigned server-side ids that came after the hidden field that got generated as part of the initial page rendering.

3)The solution was to explicitly shove the hidden field into a much higher id using S.formGroup. See here for more details...

The example from the last link below is as follows (and differs from mine in that it uses SHtml.submit, whereas I use SHtml.hidden). It adds the constant 1,000 to the submit button's server-side handler id:

"type=submit" #> S.formGroup(1000) { SHtml.submit("Submit", process) }

Discussion of a problem that is essentially the same as mine: https://groups.google.com/forum/#!topic/liftweb/MYJQeVlOYFM
Description of id assignment and S.formGroup under heading "Server side function order.": https://www.assembla.com/spaces/liftweb/wiki/cool_tips
And lastly, linked to from the last link is some example code: https://groups.google.com/forum/#!topic/liftweb/E9z7PVhogQw

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top