Hi Lispers,
Ok, starting to get the hang of this, but I'm having a small issue
maybe you guys could help me with.
I'm playing with some AJAX-stuff and .. hm .. not sure how to explain
this proper, but ..
I have an INPUT-element (HTML) that can be used for both input of data
(client-initiated) and output of data (server-initiated), and I'd like
to block the observer so it does not react on input from the client and
tries to re-update the element ..
..or maybe "blocking" the observer isn't right at all, maybe I'm going
about this the wrong way..? Here is some code:
(defobserver value ((value-attribute ValueAttribute))
(cells::trc "ValueAttribute changing from" old-value :to new-value)
(terpri)
;; Update the value-attribute of the HTML-element on the client-side.
(setAttribute (parent-of value-attribute)
(name-of value-attribute)
"value"
(princ-to-string (value-of value-attribute))))
(defmethod onkeyup ((value-attribute ValueAttribute) (parent Container)
&rest event-args)
(format t "onkeyup: ~A~%" (fifth event-args))
(setf (value-of value-attribute)
(first event-args)))
`onkeyup' is called from the client-side and since (setf value-of ..)
triggers the observer for the slot `value', it tries to "redraw" what's
already on the client-side.
--
Lars Rune Nøstdal
http://lars.nostdal.org/
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date:
Message-ID: <44899E83.8080107@gmail.com>
Lars Rune N�stdal wrote:
> Hi Lispers,
> Ok, starting to get the hang of this, but I'm having a small issue
> maybe you guys could help me with.
>
> I'm playing with some AJAX-stuff and .. hm .. not sure how to explain
> this proper, but ..
Fair warning: I have never programmed a Web app, barely done anything
with HTML, and understood almost nothing of what you wrote. :) But this
(check me: input form field initialized from a database record field,
then overwritten by user so data must flow the other way back to the
DB?) happens all the time in standalone apps, so...
>
> I have an INPUT-element (HTML) that can be used for both input of data
> (client-initiated) and output of data (server-initiated), and I'd like
> to block the observer so it does not react on input from the client and
> tries to re-update the element ..
>
> ..or maybe "blocking" the observer isn't right at all, maybe I'm going
> about this the wrong way..?
As I said, this comes up all the time, and what you tried is the
tempting automatic flow:
DB(existing value -> Input(as initial value) -> DB(new value)
Your code is what I think about writing even after ten years of using
Cells. So how do we break the cycle? There are several ways I have
handled this.
1. In the slot definition, specify:
(value ...etc... :unchanged-if 'string=)
You will still make the return trip to set the input field
unnecessarily, but that will be an NOP because the new value will be
seen as unchanged, stopping propagation (including calling observers).
The default test is EQL and it is easy in even simple code for 'string=
but un-EQL strings to arise and start cycling.
To avoid even the beginnings of a roundtrip, hey, have the observer take
a peek at where it is about to write and decide if it needs to. Not as
much fun as letting Cells worry about it, but this one observer (I
gather) is handling all the HTML input fields, so...?
Now I revert to my earlier disclaimer about being Web-ignorant: I think
The Right Answer overall will be decided by the exact nature of the Web
stuff, so I will just continue dumping alternatives
2. Does the DB flow to the form only when the form is opened? If so, the
HTML input fields can have a something another Cells user just
requested: a rule that runs once at initialization time and then becomes
a /Cells/ input. ie, the slot starts as c-formula but then becomes
c-input. (No dependencies are recorded during the initial rule
invocation.) Only the HTML input field has an observer writing back to
the corresponding DB field. Now if only cells had some frickin decent
doc I could tell you the syntax. Maybe:
(c-formula (:inputp t)
<code to lookup value for this field>)
If that is what you settle on and it does not work I will research further.
3. if you are in a multi-user environment or for any other reason need
to update the form programmatically at the same time the user is typing,
well, things will be pretty exciting for the user <g>, and it may be
time to consider the failsafe: do not try to use Cells dataflow where
the application really needs to decide the dataflow. An application flow
will have to be concocted, hopefully facilitated by Cells, but always
keep in mind that the functionality might dictate more savvy than the
Cells changed/not-changed propagation smarts.
hth, ken
> Here is some code:
>
>
> (defobserver value ((value-attribute ValueAttribute))
> (cells::trc "ValueAttribute changing from" old-value :to new-value)
> (terpri)
> ;; Update the value-attribute of the HTML-element on the client-side.
> (setAttribute (parent-of value-attribute)
> (name-of value-attribute)
> "value"
> (princ-to-string (value-of value-attribute))))
>
> (defmethod onkeyup ((value-attribute ValueAttribute) (parent Container)
> &rest event-args)
> (format t "onkeyup: ~A~%" (fifth event-args))
> (setf (value-of value-attribute)
> (first event-args)))
>
>
> `onkeyup' is called from the client-side and since (setf value-of ..)
> triggers the observer for the slot `value', it tries to "redraw" what's
> already on the client-side.
>
--
Cells: http://common-lisp.net/project/cells/
"I'll say I'm losing my grip, and it feels terrific."
-- Beaming husband to scowling life, New Yorker cartoon
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date:
Message-ID: <4489AFBE.2020509@gmail.com>
Ken Tilton wrote:
> Lars Rune N�stdal wrote:
> > Hi Lispers,
> > Ok, starting to get the hang of this, but I'm having a small issue
> > maybe you guys could help me with.
> >
> > I'm playing with some AJAX-stuff and .. hm .. not sure how to explain
> > this proper, but ..
>
> Fair warning: I have never programmed a Web app, barely done anything
> with HTML, and understood almost nothing of what you wrote. :) But this
> (check me: input form field initialized from a database record field,
> then overwritten by user so data must flow the other way back to the
> DB?) happens all the time in standalone apps, so...
>
> >
> > I have an INPUT-element (HTML) that can be used for both input of data
> > (client-initiated) and output of data (server-initiated), and I'd like
> > to block the observer so it does not react on input from the client and
> > tries to re-update the element ..
> >
> > ..or maybe "blocking" the observer isn't right at all, maybe I'm going
> > about this the wrong way..?
>
> As I said, this comes up all the time, and what you tried is the
> tempting automatic flow:
>
> DB(existing value -> Input(as initial value) -> DB(new value)
>
> Your code is what I think about writing even after ten years of using
> Cells. So how do we break the cycle? There are several ways I have
> handled this.
>
> 1. In the slot definition, specify:
>
> (value ...etc... :unchanged-if 'string=)
>
> You will still make the return trip to set the input field
> unnecessarily, but that will be an NOP because the new value will be
> seen as unchanged, stopping propagation (including calling observers).
> The default test is EQL and it is easy in even simple code for 'string=
> but un-EQL strings to arise and start cycling.
>
> To avoid even the beginnings of a roundtrip, hey, have the observer take
> a peek at where it is about to write and decide if it needs to. Not as
> much fun as letting Cells worry about it, but this one observer (I
> gather) is handling all the HTML input fields, so...?
>
> Now I revert to my earlier disclaimer about being Web-ignorant: I think
> The Right Answer overall will be decided by the exact nature of the Web
> stuff, so I will just continue dumping alternatives
>
> 2. Does the DB flow to the form only when the form is opened? If so, the
> HTML input fields can have a something another Cells user just
> requested: a rule that runs once at initialization time and then becomes
> a /Cells/ input. ie, the slot starts as c-formula but then becomes
> c-input. (No dependencies are recorded during the initial rule
> invocation.) Only the HTML input field has an observer writing back to
> the corresponding DB field. Now if only cells had some frickin decent
> doc I could tell you the syntax. Maybe:
>
> (c-formula (:inputp t)
> <code to lookup value for this field>)
Hmm, Cells needed a patch, in constructors.lisp:
(defmacro c-formula ((&rest keys &key lazy &allow-other-keys) &body forms)
(assert (member lazy '(nil t :once-asked :until-asked :always)))
`(make-c-dependent
:code ',forms
:value-state :unevaluated
:rule (c-lambda ,@forms)
,@keys))
The &allow-other-keys is new. Then this should work (after fixing the
line breaks -- or just get from CVS in two minutes):
(defpackage #:tu-rule-once-then-input (:use :cl :utils-kt :cells :tu-cells))
(in-package #:tu-rule-once-then-input)
#|
Often in interactive applications one needs to do interesting things to
come up
with an initial value for a field which then is to be edited by a user, or
for some other reason regularly fed as a C-INPUT.
|#
(defvar *db-entry*)
(defun get-age (id)
(bwhen (props (cdr (assoc id *db-entry* :test 'string=)))
(getf props :age)))
(defmodel kenny-view ()
((age :accessor age :initform (c-formula (:inputp t)
(- (get-age "555-55-5555")
(^grecian-formula-amt))))
(grecian-formula-amt :accessor grecian-formula-amt
:initform (c-in 5))))
(defobserver age ((self kenny-view))
(setf (getf (cdr (assoc "555-55-5555" *db-entry* :test 'string=))
:age) new-value))
#+test
(let ((*db-entry* (copy-list '(("555-55-5555" . (:name "ken" :age 54))
("666-66-6666" . (:name "satan" :age
most-positive-fixnum))))))
(cells-reset)
(let ((kv (make-instance 'kenny-view)))
(print `(:age-init ,(age kv)))
(assert (= 49 (age kv)))
(incf (grecian-formula-amt kv) 10) ;; try looking younger
(assert (= 15 (grecian-formula-amt kv)))
(assert (= 49 (age kv))) ;; unchanged -- the age rule is gone
(print `(:happy-birthday ,(incf (age kv))))
(assert (= 50 (age kv)(get-age "555-55-5555")))
;
; just showin' off...
(assert (= 51 (1+ (age kv))(incf (age kv))(get-age "555-55-5555")))))
i'll have to take a look at this sometime tomorrow (or maybe even
sunday .. *shrug*) - because weekend, sunny weather and beer has
knocked me out for now
oh, what the heck - just a quick note (i haven't read all of your
replies proper yet), but i'll just add that maybe mentioning this as
"web" or "HTML" is irrelevant, and one should just consider this as a
regular entry-widget like GtkEntry .. one can both read data from this
widget and send data to it, and that is what i'm doing .. so the text
in the entry-widget can change in two ways:
* by the user pressing a key causing the text to change
* by something on the server changing the text in it
now, when something on the server changes the slot containing the text
of the widget, i'd like something observe when this happens so that i
can update the text on the client-side also
but, the other way around, when something on the client changes the
text (the user presses a key) i'd still like the slot containing the
text of the widget to be updated and also any cells depending on this
ruled cell/slot, of course - but the observer should not update the
text on the client-side; because it already contains the current data
and might even contain newer data as a _new_ event (data) has been sent
inbetween the time of the original event, and reception of the response
to that event
ie .. using `:unchanged-if #'string=' will not work here, because the
data _is_ actually changed, but the change should not propagate back to
the widget which caused the change ..
ok, i might be ramblin' here; i should get back to this tomorrow or
sunday
--
Lars Rune Nøstdal
http://lars.nostdal.org/
btw. .. this migth not work, but here it is:
http://nostdal.org:6123/hw/
html-template:
http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/templates/hello-world.txt
server-application-code (large parts of it is still very, very messy):
http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/hello-world.lisp
you should be able see what i'm trying to describe actually happening
here (check "AJAX debug output"):
updateElements, response_text:
'[["setAttribute","entry-a",["value","1"]],["setElement","sum","44"]]'
sndReq: '[["handleWidgetEvent", "entry-a", "onkeyup", "1", "false",
"false", "false", "1"]]'
"sndReq .." means user has pressed a key (the key "1")
"updateElements .." both updates the elements (or widgets) `entry-a'
and `sum', even though `entry-a' should not be updated
--
Lars Rune Nøstdal
http://lars.nostdal.org/
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date:
Message-ID: <%1rig.51$qx6.24@fe09.lga>
Lars Rune N�stdal wrote:
> btw. .. this migth not work, but here it is:
> http://nostdal.org:6123/hw/
>
> html-template:
>
> http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/templates/hello-world.txt
>
> server-application-code (large parts of it is still very, very messy):
>
> http://nostdal.org:6060/~lars/programming/lisp/symbolicweb/tests/hello-world.lisp
>
> you should be able see what i'm trying to describe actually happening
> here (check "AJAX debug output"):
>
> updateElements, response_text:
> '[["setAttribute","entry-a",["value","1"]],["setElement","sum","44"]]'
> sndReq: '[["handleWidgetEvent", "entry-a", "onkeyup", "1", "false",
> "false", "false", "1"]]'
>
> "sndReq .." means user has pressed a key (the key "1")
> "updateElements .." both updates the elements (or widgets) `entry-a'
> and `sum', even though `entry-a' should not be updated
>
<g> You did not tell me what event triggered what you described (I guess
you selected all and then typed "1" in entry-a?); you did not tell me
that the log shows newer entries at the top; and this is not using
Cells, so I will guess: you want to know if Cells can do better? You
also did not demonstrate input fields being set other than to initialize
them -- after then all data flows from user entry. So...
... now I am completely lost. What I do understand I think is handled by
what I have offered in earlier articles (the unchanged-if bit as well as
the formula-input hybrid Cell) so I do not think I do understand.
But it was fun seeing a Lisp web app. I really have not played with this
stuff. Thx! :) So this AJAX stuff simulates a classic event-driven GUI
framework? Something like Cells-Gtk or early Celtk would be a good model
for what realistically would be a serious (2-3 week?) Cells project to
wrap the CL Web app framework of choice (open source ACL Web Objects?)
with a thin Cell-savvy framework. The Cell-savvy framework pretends it
is the browser. It is not, of course, but it sends things to the real
browser (only as needed!) and other glue pipes AJAX events (if I have
that right) into the "faux browser". Then it all Just Works.
Keep talking, I am tempted to do it myself. High time I got into the Web
app game.
kenny
--
Cells: http://common-lisp.net/project/cells/
"I'll say I'm losing my grip, and it feels terrific."
-- Smiling husband to scowling wife, New Yorker cartoon
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date:
Message-ID: <e2qig.273$uk5.225@fe12.lga>
Lars Rune N�stdal wrote:
> i'll have to take a look at this sometime tomorrow (or maybe even
> sunday .. *shrug*) - because weekend, sunny weather and beer has
> knocked me out for now
>
> oh, what the heck - just a quick note (i haven't read all of your
> replies proper yet), but i'll just add that maybe mentioning this as
> "web" or "HTML" is irrelevant, and one should just consider this as a
> regular entry-widget like GtkEntry .. one can both read data from this
> widget and send data to it, and that is what i'm doing .. so the text
> in the entry-widget can change in two ways:
>
> * by the user pressing a key causing the text to change
> * by something on the server changing the text in it
I understand. And we do not need client server speak, right? I can have
a subprocess or separate process on my system writing to the DB at the
same time I am editing a DB entry using a GUI app, yes?
>
> now, when something on the server changes the slot containing the text
> of the widget, i'd like something observe when this happens so that i
> can update the text on the client-side also
>
> but, the other way around, when something on the client changes the
> text (the user presses a key) i'd still like the slot containing the
> text of the widget to be updated and also any cells depending on this
> ruled cell/slot, of course -
yep.
> but the observer should not update the
> text on the client-side; because it already contains the current data
So far "unchanged-if" works, but...
> and might even contain newer data as a _new_ event (data) has been sent
> inbetween the time of the original event, and reception of the response
> to that event
well, you lost me there. Let's slow down a little.
I start editing my personnel file. The DB shows "Kenneth" as my first
name when I open the file. The record becomes a person-record instance,
person-record being a cells-driven class.
Your first thought is an observer on the person-record first-name
attribute to setf the corresponding field of some person-view instance.
person record may not have a named attribute for first-name, it may have
a "fieldname-value-pairs" attribute. No matter, I think.
Now, using my GUI app, I move the keyboard focus to the first-name field
and start typing. Like any good GUI, it preselects all the text. When I
hit "K" to simply type in "Kenny", my new name, the GUI subsitutes K for
all of Kenneth. Do you want the text "K" now propagated to the
person-record? How about written to the persistent store so others will
see the change? or (for each of those) is there a discrete action to
move the data out of the form and then commit to persistent store?
Moving on...
>
> ie .. using `:unchanged-if #'string=' will not work here, because the
> data _is_ actually changed,
Where? In the view? How? because I am still typing? Changed in the DB
because some other process wrote to the DB?
> but the change should not propagate back to
> the widget which caused the change ..
>
> ok, i might be ramblin' here; i should get back to this tomorrow or
> sunday
>
yeah, i am lost. I now see updates coming in a stream to the DB, a user
flailing madly away at a keyboard warding off incoming data like Yoda
with a light saber, and poor little Cells trying to negotiate an
acceptable outcome.
btw, I /have/ done this sort of thing. Great fun, but one does have to
have clear precisely what goes where and when. The worst case scenario
seems to be a field update arriving while the user is typing an update
to that field. But I am not sure, and on this end the John Powers is not
helping. :)
ke...
--
Cells: http://common-lisp.net/project/cells/
"I'll say I'm losing my grip, and it feels terrific."
-- Smiling husband to scowling wife, New Yorker cartoon
Ken Tilton wrote:
> To avoid even the beginnings of a roundtrip, hey, have the observer take
> a peek at where it is about to write and decide if it needs to. Not as
> much fun as letting Cells worry about it, but this one observer (I
> gather) is handling all the HTML input fields, so...?
yes, this is almost what i'm looking for .. but instead of looking
where it is about to _write_, i'm interested in where the data _came
from_
maybe a quick solution would be to "tag" a cell .. when setf'ing a
cell, can i somehow tag it with something that will stick on in while
it's sent through the cell-network (i'm not sure how this stuff is
implemented)? i could then tag it with `:from-client' so the
observer-code will later know that it should not redraw the widget on
the client-side .. i'm not sure this is the "right way" or whatever -
it might be simple to implement though .. *browses cells-internals*
in any case..we might be talking past each other; because i do not
understand everything you're telling me..
--
Lars Rune Nøstdal
http://lars.nostdal.org/
Lars Rune Nøstdal wrote:
> maybe a quick solution would be to "tag" a cell .. when setf'ing a
i meant tagging a value
From: Ken Tilton
Subject: Re: Question about Cells; defobserver for both I and O
Date:
Message-ID: <MoDig.84$qx6.68@fe09.lga>
Lars Rune N�stdal wrote:
> Ken Tilton wrote:
>
>>To avoid even the beginnings of a roundtrip, hey, have the observer take
>>a peek at where it is about to write and decide if it needs to. Not as
>>much fun as letting Cells worry about it, but this one observer (I
>>gather) is handling all the HTML input fields, so...?
>
>
> yes, this is almost what i'm looking for .. but instead of looking
> where it is about to _write_, i'm interested in where the data _came
> from_
>
> maybe a quick solution would be to "tag" a cell .. when setf'ing a
> cell, can i somehow tag it with something that will stick on in while
> it's sent through the cell-network (i'm not sure how this stuff is
> implemented)? i could then tag it with `:from-client' so the
> observer-code will later know that it should not redraw the widget on
> the client-side .. i'm not sure this is the "right way" or whatever -
> it might be simple to implement though .. *browses cells-internals*
>
> in any case..we might be talking past each other; because i do not
> understand everything you're telling me..
>
Yes, and it goes both ways since I do not fully understand the tools you
are using -- but we will converge on a solution, i think.
If you are just worried about unnecessary trips to the client because of
the obvious waste of bandwidth, then I think the 'unchanged-if' bit will
work fine. There /is/ an unnecessary visit, but it all happens on the
server where a "client" instance is playing the part of the real client.
This is like Celtk or Cells-Gtk, where CLOS widgets mirror one-for-one
widgets in the C framework. First, the CLOS widget at make-instance time
tells, say, Tk to make a widget. Then, as slots of the CLOS widget
change (accomplishing nothing on screen) observers on each field issue
Tk "configure" commands for the mirrored slot (again, one CLOS slot per
Tk configuration option).
I get a kick out of this because Cells is performing two distinct, hefty
jobs here. First, the usual Cells thang of supporting the declarative
style, propagating state around the application model. Second, it is
propagating state to and from (by mechanisms not yet discussed) Tk,
keeping the CLOS mirror-model in synch with the Tk model actually
constituting the GUI.
Your idea of tagging the values is reasonable if nothing else will work,
but then the advantage of Cells starts to fade -- I hate for
productivity tools to make me work harder. :)
Getting back to your project: I think what might be ging on here is that
your design thinking is like the original LTk design. That does have
mirror classes, but not mirror slots. An apparent setf of the mirror
slot actually talked directly to Tk and configured the C instance.
Reading such an apparent slot on the Lisp side /each time/ went across
to TK to ask what value the C instance had for that configuration
option. ie, LTk instances were nicely lightweight. But when I did
Cells-Ltk (Celtk), I knew I wanted to do the usual Cells thang on the
Lisp side and just drive Tk as a slave to handle the GUI for me.
With my approach, things happen very fast in the Tk mirror-world I have
in Lisp, so something like an unnecessary roundtrip (halted when the
'unchanged' test returns t) is not worth bothering about.
I am quite interested in doing this with Ajax, could be another killer
app for Cells. need a snappy name, tho. Carjax? Have to justify that R
somehow.
kzo
--
Cells: http://common-lisp.net/project/cells/
"I'll say I'm losing my grip, and it feels terrific."
-- Smiling husband to scowling wife, New Yorker cartoon