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.


Reply via email to