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: 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 [email protected].
>>> To unsubscribe from this group, send email to
>>> [email protected].
>>> 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 [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.
>
>
--
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.