actually the sorting is per owner which should be fine. I'll give it a try and get back to you.
Br's, marius On Jan 12, 4:46 pm, Marius <[email protected]> wrote: > On Jan 12, 4:20 pm, Adam Warski <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> wrote: > > >>>>>> On Jan 10, 5:20 pm, Adam Warski <[email protected]> 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: > > ... > > read more »
-- You received this message because you are subscribed to the Google Groups "Lift" 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/liftweb?hl=en.
