Adam I was thinking of a slightly different approach that does not involve hidden fields:
Say you have your current form with SHtml.text, checkboxes or whatever have you: then your ajax buttons (outside the form) like: def ajaxButton(text: NodeSeq, formId: String, func: () => JsCmd, attrs: (String, String)*): Elem = { attrs.foldLeft(fmapFunc(contextFuncBuilder(SFuncHolder(func))) (name => <button onclick={makeAjaxCall(JsRaw (LiftRules.jsArtifacts.serialize(formId) + "&" + name.encJs + "=_")).toJsCmd + "; return false;"}>{text}</button>))(_ % _) } I haven't tested though but you get the idea ... When we do the ajax call, we serialize the form and add the name parameter as well. This will cause your field functions to be called, and at the end you ajaxButton function to be called. Inside func function your RequestVar should be preserved due to contextFuncBuilde call. Please let me know if this works. If it does we should probably add it to SHtml. Br's, Marius On Jan 11, 10:54 pm, Adam Warski <a...@warski.org> wrote: > Hello, > > trying the solution a bit more I came into another problem which I can't > solve elegantly. > > The solution below works nicely for an "add" button, but a "delete" button > causes more problems: the problem is that with "delete", you must know which > element should get deleted. > > In a no-ajax solution, it is enough to do: > > elements.flatMap { element: Element => > bind("element", element Template, > "name" -> element.name.toForm, > "delete" -> submit("Delete", () => { elements -= element }) > ) > > } > > which is very nice and easy, as the element to delete gets captured in a > closure. > But with ajax, and a hidden field used to hold the name of the operation to > dispatch, this gets pretty complex: I now need to somehow encode the element > to delete (or create a map from some unique identifier to closures which hold > the delete methods), so that I can set this as a value of the hidden field. > Then in the function passed to SHtml.hidden, I need to decode it back to find > the right element. Isn't it a bit of what Lift already does when creating > forms? > > But I still have the feeling that maybe I'm approaching the whole problem > from the wrong end, after all, I just want to create an ajax-enabled list of > input fields with add and delete operations :) > > Adam > > > > > On Jan 11, 1:09 pm, Adam Warski <a...@warski.org> wrote: > >> Hello, > > >> this almost works :). > > >> Right now in my form I have a hidden element where the type of the > >> operation to execute will be set: > >> <input type="hidden" id="operation_id" name="operation_id" value="" /> > >> (the name is needed for jquery to set the value, and the id so that I can > >> later read the value using S). > > >> I bind the button as following: > > >> "addElement" -> <button onclick={((JqId(Str("operation_id")) >> > >> JqAttr("value", Str("add"))) > >> & SHtml.submitAjaxForm("elements_edit")).toJsCmd+" return > >> false;"}>{Text("Add element")}</button>, > > >> and add a hidden field to the whole form to do the processing: > > >> bind( > >> ... > >> ) ++ SHtml.hidden(() => { > >> val operationId = S.param("operation_id") > >> operationId.map { opId => opId match { > >> case "add" => elements += new Element > >> case _ => println("Unknown operation: " + opId) > >> } } > >> reDraw > >> }) > > >> where elements is a RequestVar object. > > >> However for some reason, when I click the button, in the callback I get a > >> new elements RequestVar (so it's initialized to an initial value) and > >> moreover, nothing gets redrawn on the page. What is also quite weird is > >> that the RequestVar is re-initialized, but the snippet instance stays the > >> same. Any ideas? :) > > > Yes I think so. You have a regular form with fields like: (SHtml.text. > > SHtml.hidden etc. right? RequestVars are kept around into a "snapshot- > > restorer" only for ajax functions (like for ajaxText etc) and not for > > regular fields forms. Thus in the Scala function of a SHtml.ajaxText > > you'll see the RequestVar set when the page was rendered because its > > state is preserved on purpose by lift. However for regular fields this > > is not happening. So there is a difference between an ajax-form and > > ajax fields. Even your form is sent via ajax, the fields are regular > > fields not ajax fields (... ajax fields do not even need to live > > inside a form). Therefore for your case the RequestVar's are not > > preserved and they are renewed when the request comes in. You can keep > > around that state using a SessionVar. > > >> I thought that my use-case would be quite common in lift: I just want to > >> add a couple of buttons to my form which execute different actions. > > > Which is very easy to do. But your problem not is how you maintain > > your specific state (the elements) > > >> Thanks for the help! > >> Adam > > >> On Jan 10, 2010, at 6:03 PM, Marius wrote: > > >>> Sorry I think the syntax would be (I haven't tested it though): > > >>> <button onclick={((JqId("hidden_field_id") >> JqAttr("value", "add")) > >>> & SHtml.submitAjaxForm(form_ID)).toJsCmd}>add</button> > >>> <button onclick={((JqId("hidden_field_id") >> JqAttr("value", > >>> "edit")) & SHtml.submitAjaxForm(form_ID)).toJsCmd}>edit</button> > >>> <button onclick={((JqId("hidden_field_id") >> JqAttr("value", > >>> "delete")) & SHtml.submitAjaxForm(form_ID)).toJsCmd}>delete</button> > > >>> Br's, > >>> Marius > > >>> On Jan 10, 6:58 pm, Marius <marius.dan...@gmail.com> wrote: > >>>> On Jan 10, 5:20 pm, Adam Warski <a...@warski.org> wrote: > > >>>>> Hello, > > >>>>>> ajaxButton("Press me would ya'?", SHtml.submitAjaxForm > >>>>>> (form_ID).toJsCmd, (some) => { > > >>>>>> do your stuff here > > >>>>>> }) > > >>>>> Looking at the source code I think this might work, but I'm having > >>>>> trouble constructing the correct expression to pass to ajaxButton. The > >>>>> method signature requires a Call instance, and SHtml.submitAjaxForm > >>>>> results in a command (JsCmd). Is it possible to somehow combine the two? > > >>>> I was referring to this signature: > > >>>> def ajaxButton(text: NodeSeq, jsExp: JsExp, func: String => JsCmd, > >>>> attrs: (String, String)*): Elem > > >>>> and not > > >>>> def ajaxButton(text: NodeSeq, jsFunc: Call, func: () => JsCmd, attrs: > >>>> (String, String)*): Elem > > >>>> jsExp will be called before sending the actual ajax. But this may be a > >>>> bit problematic if your jsExp sends the ajaxForm the ajax request is > >>>> processed asynchronously at js level meaning the the button ajax > >>>> request may be send before the actual ajax form processing is done. > >>>> I'm not sure if this fits your needs > > >>>>> Or maybe there is some other, lift-idomatic way to solve my problem? > >>>>> I want to create a form with a list of elements, with three ajax > >>>>> buttons: add, remove and save (adding/removing a row and persisting the > >>>>> list) > > >>>> The first solution I described involving hidden fields will definitely > >>>> work. I don't think you need to do 2 ajax calls (one for the form and > >>>> one for the button) but piggy back the button information into a > >>>> hidden field and only submit the form: > > >>>> Perhaps something like: > > >>>> <button onclick={(JqId("hidden_field_id") >> JqAttr("value", "add")) + > >>>> + SHtml.submitAjaxForm(form_ID).toJsCmd}>blah</button> > > >>>>> By the way, I think there's a small inconsistency; there are 7 > >>>>> overloaded variants for ajaxButton: > > >>>>> ajaxButton(text: NodeSeq, func: () => JsCmd, attrs: (String, String)*): > >>>>> Elem > >>>>> ajaxButton(text: String, func: () => JsCmd, attrs: (String, String)*): > >>>>> Elem > > >>>>> ajaxButton(text: NodeSeq, jsFunc: Call, func: () => JsCmd, attrs: > >>>>> (String, String)*): Elem > >>>>> ajaxButton(text: String, jsFunc: Call, func: () => JsCmd, attrs: > >>>>> (String, String)*): Elem > > >>>>> ajaxButton(text: NodeSeq, jsExp: JsExp, func: String => JsCmd, attrs: > >>>>> (String, String)*): Elem > > >>>>> and the last one doesn't have a text: String counterpart. Also the > >>>>> javadoc for the last variant is missing information on what's "jsExp" > >>>>> and what's the argument passed to "func". My guess would be that jsExp > >>>>> is evaluated and the result passed to func on the server? > > >>>> Yes jsExp is being evaluated and its result is being sent to server. > >>>> Please open a defect for this inconsistency. > > >>>>> -- > >>>>> Adam > >>> -- > >>> You received this message because you are subscribed to the Google Groups > >>> "Lift" group. > >>> To post to this group, send email to lift...@googlegroups.com. > >>> To unsubscribe from this group, send email to > >>> liftweb+unsubscr...@googlegroups.com. > >>> For more options, visit this group > >>> athttp://groups.google.com/group/liftweb?hl=en. > > -- > > You received this message because you are subscribed to the Google Groups > > "Lift" group. > > To post to this group, send email to lift...@googlegroups.com. > > To unsubscribe from this group, send email to > > liftweb+unsubscr...@googlegroups.com. > > For more options, visit this group > > athttp://groups.google.com/group/liftweb?hl=en.
-- You received this message because you are subscribed to the Google Groups "Lift" group. To post to this group, send email to lift...@googlegroups.com. To unsubscribe from this group, send email to liftweb+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/liftweb?hl=en.