On Jan 12, 4:20 pm, Adam Warski <a...@warski.org> wrote: > Hello, > > this *almost* works :). > > I modified your code a bit and now I have: > > def ajaxButton(text: NodeSeq, formId: String, func: () => JsCmd, attrs: > (String, String)*): Elem = { > attrs.foldLeft(fmapFunc(contextFuncBuilder(func))(name => > <button onclick={makeAjaxCall(JsRaw( > LiftRules.jsArtifacts.serialize(formId).toJsCmd + " + " + > Str("&" + name + "=true").toJsCmd)).toJsCmd + > "; return false;"}>{text}</button>))(_ % _) > } > > Now the form submits and the right function is executed on the server, and > the form is redrawn in the browser. > > However, the problem is in the ordering of operations. > The sequence basically is: > (1) update some elements of the form > (2) execute the function > (3) update the rest of the elements of the form > > The problem of course is that (2) returns the new content of the form (a > SetHtml JsCmd), generated basing on state without all fields updated. > I don't quite yet get the rule deciding which fields get updated before > calling the function, and which after. > One thing I noticed is that if I move the field that is bound first (in > bind(...)) to be the last field, it gets moved from group (1) to (3). > > Also, I thought that maybe the ordering of POST values matters, but swapping > Str("&" + name + "=true").toJsCmd and > LiftRules.jsArtifacts.serialize(formId).toJsCmd doesn't have any effect. > > I tried the form many times and always get the same behaviour, so the (1) vs. > (3) division seems to be deterministic :)
I think there is a sorting involved when execution the user function but I'm not sure why. I need to give a second look into Lift's guts. I agree ordering is important here as you want your function executed after all form functions have been executed. > > Adam > > On Jan 11, 2010, at 10:58 PM, Marius wrote: > > > 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 > > ... > > read more »
-- 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.