Yes please a small app would be best. Please use 2.0-SNAPSHOT. Also please see this thread: http://groups.google.com/group/liftweb/browse_thread/thread/75750c42ec3a2d7d?hl=en#
Br's, Marius On Jan 13, 10:07 am, Adam Warski <[email protected]> wrote: > Hello, > > the problem is that some form elements are updated, then the function is > called, and then the rest of the form elements are updated. > My code is more or less something like this: > > def editElement(elementTemplate: NodeSeq): NodeSeq = { > container.elements.flatMap { element: Element => > bind("element", elementTemplate, > "name" -> { println("bind element name " + element.name); > SHtml.text(element.name.is, > (s: String) => {println("set element name " + s); > element.name(s); }) } > ) > } > } > > bind("cont", containerTemplate, > "name" -> { println("bind container name " + conf.name); > SHtml.text(cont.name.is, > (s: String) => {println("set container name " + s); > cont.name(s); }) }, > "elements" -> editElement _, > "addElement" -> ajaxButton(Text("Add"), "edit_form"id, () => { > println("executing function"); cont.addElement; reDraw }) > ) > > And the output, when I submit a form where 1 element is already added is: > > 09:04:23,450 INFO [STDOUT] set container name a > 09:04:23,474 INFO [STDOUT] executing function > 09:04:23,474 INFO [STDOUT] bind container name a > 09:04:23,475 INFO [STDOUT] bind element name 0 > 09:04:23,476 INFO [STDOUT] bind element name 1 > 09:04:23,478 INFO [STDOUT] set element name 0a > > So as you can see first cont.name is set, then the button function is > executed, and later the element.names are set. > > I can try creating a small lift app which would demo this if you'd like. > Also, I'm using 1.1-M8. Maybe I should try some newer version? (2.0-SNAPSHOT? > 1.1-SNAPSHOT?) > > Adam > > On Jan 12, 2010, at 6:03 PM, Marius wrote: > > > I must have misread your post. I did test the ajaxButton above (with > > your corrections) and the behaviour is correct. Form field functions > > are invoked first and then your ajax function provided to ajaxButton. > > Thus this is a good way for adding submit functions for ajax form > > without the need of using hidden fields and I'll promote this for > > addition in Shtml (probably with slight modifications). I think the > > method name should be "ajaxSubmit" > > > I don't quite get why you're saying this is a problem. What does step > > 3 needs to accomplish? . all form fields functions are called (except > > if you have a Shtml.submit because form serialization does not include > > submits). Then you function is invoked and the response is sent to > > client. > > > You mentioned that you just need to add multiple buttons for a ajax > > form ... this version of ajaxButton does just that. Can you please > > clarify your used case for for for those 3 steps? ... > > > Br's, > > Marius > > > 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 :) > > >> 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", > > ... > > 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.
