An autocomplete (suggest) component with reactive-web

131 views
Skip to first unread message

Grégoire Neuville

unread,
Nov 30, 2011, 4:09:48 PM11/30/11
to lif...@googlegroups.com
Hi all,

I added an Autocomplete component to reactive-web, following the way
html objects are represented in the html package. You can find it here
: https://gneu...@github.com/gneuvill/reactive.git in the
reactive-web sub-project, src/main/scala/reactive/web/html/components
folder.
I'm curious about any remarks, advice, critics about it.
Does it have any chance to make it to the real project ?

Thanks,
--
Grégoire Neuville

Naftoli Gugenheim

unread,
Dec 1, 2011, 2:19:31 AM12/1/11
to lif...@googlegroups.com
Oh wow, thanks!
Is it all contained in Autocomplete.scala?
I have a project that will need an autocomplete (or really an editable combobox) when I get up to it --- now I have one less thing holding it up.


2011/11/30 Grégoire Neuville <gregoire...@gmail.com>
--
Grégoire Neuville

--
Lift, the simply functional web framework: http://liftweb.net
Code: http://github.com/lift
Discussion: http://groups.google.com/group/liftweb
Stuck? Help us help you: https://www.assembla.com/wiki/show/liftweb/Posting_example_code

Grégoire Neuville

unread,
Dec 1, 2011, 6:00:13 AM12/1/11
to lif...@googlegroups.com
Hey,

Yes, it's all contained in Autocomplete.scala, and that certainly is
something that needs to be improved - I'm thinking about some
javascript and css code that is hard coded in it, although
parameterized.
Regarding the combobox idea : well, You'll see that what I've done is
much more prosaic, but it could nonetheless be a starting point to
more complex components, isn't it ?

Thanks,

G.N

--
Grégoire Neuville

Naftoli Gugenheim

unread,
Dec 2, 2011, 3:21:17 AM12/2/11
to liftweb
Perhaps one way to improve it, is that instead of making Autocomplete extend Repeater, have it extend RElem, and it should have a Repeater inside of it, side by side with the text input, etc.

How would you define the difference between a combo box and an autocomplete widget?


2011/12/1 Grégoire Neuville <gregoire...@gmail.com>

Grégoire Neuville

unread,
Dec 7, 2011, 3:26:00 PM12/7/11
to lif...@googlegroups.com
Hey,

> Perhaps one way to improve it, is that instead of making Autocomplete extend
> Repeater, have it extend RElem, and it should have a Repeater inside of it,
> side by side with the text input, etc.

Ok, I've done that. But the JsRaw stuff remains which I don't like
that much, what do you think ?
(I was thinking about getting rid of it by mimicking the
selectedIndex/selectedItem stuff in Select ...?)

>
> How would you define the difference between a combo box and an autocomplete
> widget?

Well, based on the - maybe false - assumption that an Autocomplete
widget has to deal with a potentially infinite list of items on the
one hand, and a ComboBox with a finite list of items on the other
hand, I've started coding a ComboBox component that extends
Autocomplete and overrides its internal Repeater with a Select. Let me
know what you think of this : is it going on the right direction ?

Thanks,

G.N

Naftoli Gugenheim

unread,
Dec 8, 2011, 7:29:10 PM12/8/11
to lif...@googlegroups.com
If the only difference is that autocomplete has the potential for an infinite number of choices and a combobox lacks that potential, it doesn't sound like it requires a new component. According to that, an autocomplete that happens to have a finite set of choices is a combobox already.


2011/12/7 Grégoire Neuville <gregoire...@gmail.com>

Naftoli Gugenheim

unread,
Dec 8, 2011, 8:04:45 PM12/8/11
to lif...@googlegroups.com
Why are you firing JsRaws out of input.keyUp.jsEventStream?
DomEventSource.jsEventStream is a javascript event stream that receives the relevant browser events (in this case, input's key up events).
Since you are using ?>> on the DomEventSource, a method that is defined in reactive.Forwardable, you are responding to events from the server (it delegates to foreach on line 116 in DOMEventSource.scala). That being the case, you can send javascript to the browser directly, without involving the jsEventStream you are responding to.
To make it work short term, just replace 'evSrc.jsEventStream fire' with 'Reactions queue.'

case KeyUp(38, _) if !inInput =>
Reactions queue JsRaw("""function(){
selected = $('.""" + selectedCssClass + """');
prev = selected.prev();
if (prev.size() == 0)
$('.""" + inputCssClass + """').focus();
else
prev.focus();
}()""")
      }


Ideally, however, that would be written as scala code. See if you can make an implicit from RElem to a new JsStub proxy with a 'focus' method. That would let you write, e.g., input.focus(). (See the bottom of http://www.reactive-web.co.cc/web/JsEventStream, and/or the test.) Something like:
implicit def relem2domelem(relem: RElem)(implicit page: Page) = jsStub[DOMElem]("document.getElementById("+relem.id+")")

trait DOMElem extends JsStub {
  def focus(): JsExp[JsVoid]
}

Bonus points for a WeakHashMap[RElem, DOMElem] cache.

Perhaps rather than DOMElem, it should be named consistent with W3C classes (if necessary namespaced in a subpackage or something?) ...

See also the existing window proxy.


Other tips:

Instead of
        override def renderer(implicit p: Page) =
    e => super.renderer(p)(e).copy(child = style :+ input.render :+ repeat.render)

  def baseElem = <div class={ autocompleteCssClass }/>

Just put everthing in the baseElem:

def baseElem = <div class={ autocompleteCssClass }>{style ++ input.render ++ repeat.render}</div>

Also, you might like List(9, 13, 39).contains(code) better than List(9, 13, 39).exists(_ == code). :)

Naftoli Gugenheim

unread,
Dec 8, 2011, 8:07:10 PM12/8/11
to lif...@googlegroups.com
Also, you may enjoy using TestScope to write tests --- see http://www.reactive-web.co.cc/web/TestScope.

Grégoire Neuville

unread,
Dec 12, 2011, 5:02:24 PM12/12/11
to lif...@googlegroups.com
Hi,

> If the only difference is that autocomplete has the potential for an
> infinite number of choices and a combobox lacks that potential, it doesn't
> sound like it requires a new component. According to that, an autocomplete
> that happens to have a finite set of choices is a combobox already.

I agree. But, as far as I understand, a combobox also looks and
behaves a bit differently from an autocomplete. While the latter
allows you to reveal the whole list of items with a button (which
implies a Select, doesn't it ?), the former doesn't ; the new
component would simply provide a different name to highlight this
distinction. Does that sound hairsplitting ?

Grégoire Neuville

unread,
Dec 12, 2011, 5:35:21 PM12/12/11
to lif...@googlegroups.com
Thanks for all the tips. I comment through the mail.

On 9 December 2011 02:04, Naftoli Gugenheim <nafto...@gmail.com> wrote:
> Why are you firing JsRaws out of input.keyUp.jsEventStream?
> DomEventSource.jsEventStream is a javascript event stream that receives the
> relevant browser events (in this case, input's key up events).
> Since you are using ?>> on the DomEventSource, a method that is defined in
> reactive.Forwardable, you are responding to events from the server (it
> delegates to foreach on line 116 in DOMEventSource.scala). That being the
> case, you can send javascript to the browser directly, without involving the
> jsEventStream you are responding to.
> To make it work short term, just replace 'evSrc.jsEventStream fire' with
> 'Reactions queue.'
>
> case KeyUp(38, _) if !inInput =>
> Reactions queue JsRaw("""function(){
> selected = $('.""" + selectedCssClass + """');
> prev = selected.prev();
> if (prev.size() == 0)
> $('.""" + inputCssClass + """').focus();
> else
> prev.focus();
> }()""")
>       }
>

Didn't work keeping the JsRaw. This did :

case KeyUp(38, _) if !inInput => Reactions queue

"""selected = $('.""" + selectedCssClass + """');
prev = selected.prev();
if (prev.size() == 0)
$('.""" + inputCssClass + """').focus();
else
prev.focus();"""

>
>


> Ideally, however, that would be written as scala code.

Agreed. I struggled a while to achieve this, to no avail.

>See if you can make
> an implicit from RElem to a new JsStub proxy with a 'focus' method. That
> would let you write, e.g., input.focus(). (See the bottom
> of http://www.reactive-web.co.cc/web/JsEventStream, and/or the test.)
> Something like:
> implicit def relem2domelem(relem: RElem)(implicit page: Page)
> = jsStub[DOMElem]("document.getElementById("+relem.id+")")
>
> trait DOMElem extends JsStub {
>   def focus(): JsExp[JsVoid]
> }
>
> Bonus points for a WeakHashMap[RElem, DOMElem] cache.
>
> Perhaps rather than DOMElem, it should be named consistent with W3C classes
> (if necessary namespaced in a subpackage or something?) ...
>
> See also the existing window proxy.
>

Ok, I'm willing to try that. Regarding the W3C classes, I assume you
are referring to
http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
, am I right ? In case I am, is it not problematic to make any RElem
focusable ? Only a few of official HTMLElement actually are, and one
can make some (every ?) others so by appending the 'tabindex'
attribute to them. Should I try to model such refinements or stick
with you solution (and thus the DOMElem or HTMLElement name) ?

>
> Other tips:
>
> Instead of
>         override def renderer(implicit p: Page) =
>
>     e => super.renderer(p)(e).copy(child = style :+ input.render :+
> repeat.render)
>
>
>
>   def baseElem = <div class={ autocompleteCssClass }/>
>
>
> Just put everthing in the baseElem:
>
>
> def baseElem = <div class={ autocompleteCssClass }>{style ++ input.render ++
> repeat.render}</div>
>

Didn't work : each item occurrence was doubled or tripled in the
candidates list.

>
> Also, you might like List(9, 13, 39).contains(code) better than List(9, 13,
> 39).exists(_ == code). :)

Nice. Thanks.

--
Grégoire Neuville

Grégoire Neuville

unread,
Dec 12, 2011, 5:38:00 PM12/12/11
to lif...@googlegroups.com
> Also, you may enjoy using TestScope to write tests ---
> see http://www.reactive-web.co.cc/web/TestScope.

Good, I'll give it a try. Thanks again.

--
Grégoire Neuville

Naftoli Gugenheim

unread,
Dec 13, 2011, 4:16:23 AM12/13/11
to lif...@googlegroups.com


2011/12/12 Grégoire Neuville <gregoire...@gmail.com>

> Ideally, however, that would be written as scala code.

Agreed. I struggled a while to achieve this, to no avail.

I meant what I followed it with:
 
>See if you can make
> an implicit from RElem to a new JsStub proxy with a 'focus' method. That
> would let you write, e.g., input.focus(). (See the bottom
> of http://www.reactive-web.co.cc/web/JsEventStream, and/or the test.)
> Something like:
> implicit def relem2domelem(relem: RElem)(implicit page: Page)
> = jsStub[DOMElem]("document.getElementById("+relem.id+")")
>
> trait DOMElem extends JsStub {
>   def focus(): JsExp[JsVoid]
> }
>
> Bonus points for a WeakHashMap[RElem, DOMElem] cache.
>
> Perhaps rather than DOMElem, it should be named consistent with W3C classes
> (if necessary namespaced in a subpackage or something?) ...
>
> See also the existing window proxy.
>

Ok, I'm willing to try that. Regarding the W3C classes, I assume you
are referring to
http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
, am I right ? In case I am, is it not problematic to make any RElem
focusable ? Only a few of official HTMLElement actually are, and one
can make some (every ?) others so by appending the 'tabindex'
attribute to them. Should I try to model such refinements or stick
with you solution (and thus the DOMElem or HTMLElement name) ?

I was only referring to naming, but in any case, is that true in practice that only certain elements have a .focus() method? If so, we need to take that into account. I think the more type safety the better. 


>
> Other tips:
>
> Instead of
>         override def renderer(implicit p: Page) =
>
>     e => super.renderer(p)(e).copy(child = style :+ input.render :+
> repeat.render)
>
>
>
>   def baseElem = <div class={ autocompleteCssClass }/>
>
>
> Just put everthing in the baseElem:
>
>
> def baseElem = <div class={ autocompleteCssClass }>{style ++ input.render ++
> repeat.render}</div>
>

Didn't work : each item occurrence was doubled or tripled in the
candidates list.


That's odd. Can you try to figure out why? In other words, how is the Repeater accessing its sibling elements. That doesn't seem to make sense. There must be a bug somewhere, either in your code or mine. :)

Naftoli Gugenheim

unread,
Dec 13, 2011, 4:22:04 AM12/13/11
to lif...@googlegroups.com


2011/12/12 Grégoire Neuville <gregoire...@gmail.com>

Hi,

> If the only difference is that autocomplete has the potential for an
> infinite number of choices and a combobox lacks that potential, it doesn't
> sound like it requires a new component. According to that, an autocomplete
> that happens to have a finite set of choices is a combobox already.

I agree. But, as far as I understand, a combobox also looks and
behaves a bit differently from an autocomplete. While the latter
allows you to reveal the whole list of items with a button (which
implies a Select, doesn't it ?), the former doesn't ; the new
component would simply provide a different name to highlight this
distinction. Does that sound hairsplitting ?

Can we try to get very precise over here? Are you saying that the difference is specifically whether or not there's a button to open the candidates?
The more clarity we have in defining what we want, the better we can design it, with G-d's help. For instance, depending on how fundamental the differences are, one possible way to model it is to have a single trait that encompasses the commonality, and a separate factory singleton for each variant.
Another question: does this relate to, and do we want to take advantage of or be compatible with, html5's datalist (which I didn't manage to get working in Chrome)?

Thanks.

Grégoire Neuville

unread,
Dec 13, 2011, 3:12:12 PM12/13/11
to lif...@googlegroups.com
Hi,

>> > Ideally, however, that would be written as scala code.
>>
>> Agreed. I struggled a while to achieve this, to no avail.
>
>
> I meant what I followed it with:

Yeah I got it. Actually I was saying that I had tried a few things on
my own until I understood that the existing Focus event (as all
DomEvent s, right ?) in reactive-web is only meant to be exploited on
the server side and cannot trigger anything on the browser.

>>
>> Ok, I'm willing to try that. Regarding the W3C classes, I assume you
>> are referring to
>> http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
>> , am I right ? In case I am, is it not problematic to make any RElem
>> focusable ? Only a few of official HTMLElement actually are, and one
>> can make some (every ?) others so by appending the 'tabindex'
>> attribute to them. Should I try to model such refinements or stick
>> with you solution (and thus the DOMElem or HTMLElement name) ?
>
>
> I was only referring to naming, but in any case, is that true in practice
> that only certain elements have a .focus() method? If so, we need to take
> that into account. I think the more type safety the better.

I guess it is true, yes. The ECMAScript specification I pointed to
shows that only certain elements bear the focus method but not their
parent HTMLElement. And, practically, a div or a span do not catch
the focus event unless you append a tabindex attribute to them. The
question is thus : does this tabindex trick works for all html
elements ? I'll try to find some info about that.

>>
>> Didn't work : each item occurrence was doubled or tripled in the
>> candidates list.
>>
>
> That's odd. Can you try to figure out why? In other words, how is the
> Repeater accessing its sibling elements. That doesn't seem to make sense.
> There must be a bug somewhere, either in your code or mine. :)

Scala beginner here ! So the bug is most probably in *my* code. That
being said, I'll try to track it down even in yours, but it could be
beyond my understanding as for now.

Grégoire Neuville

unread,
Dec 13, 2011, 3:46:51 PM12/13/11
to lif...@googlegroups.com
Re,

>> I agree. But, as far as I understand, a combobox also looks and
>> behaves a bit differently from an autocomplete. While the latter
>> allows you to reveal the whole list of items with a button (which
>> implies a Select, doesn't it ?), the former doesn't ; the new
>> component would simply provide a different name to highlight this
>> distinction. Does that sound hairsplitting ?
>
>
> Can we try to get very precise over here? Are you saying that the difference
> is specifically whether or not there's a button to open the candidates?

Well, I based my assumptions on http://en.wikipedia.org/wiki/Combo_box
. So in my view, yes, the button and the Select make the difference.

> The more clarity we have in defining what we want, the better we can design
> it, with G-d's help. For instance, depending on how fundamental the
> differences are, one possible way to model it is to have a single trait that
> encompasses the commonality, and a separate factory singleton for each
> variant.

Ok with me.

> Another question: does this relate to, and do we want to take advantage of
> or be compatible with, html5's datalist (which I didn't manage to get
> working in Chrome)?

Don't know much about html5 yet. I shall make up for my ignorance and
tell you my thinking.

>
> Thanks.

Naftoli Gugenheim

unread,
Dec 15, 2011, 9:58:18 PM12/15/11
to lif...@googlegroups.com


2011/12/13 Grégoire Neuville <gregoire...@gmail.com>

Yeah  I got it. Actually I was saying that I had tried a few things on
my own until I understood that the existing Focus event (as all
DomEvent s, right ?) in reactive-web is only meant to be exploited on
the server side and cannot trigger anything on the browser.

I would term it a bit differently.
DomEvent instances are a scala representation of the event object in the browser.
However their types refer to the javascript event object too. So a DomEventSource[Focus] has a jsEventStream which allows you to attach focus event listeners that run in the browser, from scala code; and it has an eventStream that (if used) listens to above events and sends them to the server via ajax, where they're encoded in Focus instance.
 

Naftoli Gugenheim

unread,
Dec 15, 2011, 10:00:53 PM12/15/11
to lif...@googlegroups.com


2011/12/13 Grégoire Neuville <gregoire...@gmail.com>


Well, I based my assumptions on http://en.wikipedia.org/wiki/Combo_box
. So in my view, yes, the button and the Select make the difference.

Is that one or two differences? Why is a combo box's list more of a select than an autocomplete's list?

Grégoire Neuville

unread,
Dec 17, 2011, 3:52:23 PM12/17/11
to lif...@googlegroups.com
> html5's datalist (which I didn't manage to get
> working in Chrome)?

No wonder you didn't : http://www.w3schools.com/html5/tag_datalist.asp

> Thanks.

Grégoire Neuville

unread,
Dec 17, 2011, 4:16:11 PM12/17/11
to lif...@googlegroups.com
>
> I would term it a bit differently.
> DomEvent instances are a scala representation of the event object in the
> browser.
> However their types refer to the javascript event object too. So a
> DomEventSource[Focus] has a jsEventStream which allows you to attach focus
> event listeners that run in the browser, from scala code; and it has an
> eventStream that (if used) listens to above events and sends them to the
> server via ajax, where they're encoded in Focus instance.
>

Thanks for the clarification.

Grégoire Neuville

unread,
Dec 17, 2011, 5:24:56 PM12/17/11
to lif...@googlegroups.com
>> Well, I based my assumptions on http://en.wikipedia.org/wiki/Combo_box
>> . So in my view, yes, the button and the Select make the difference.
>
>
> Is that one or two differences? Why is a combo box's list more of a select
> than an autocomplete's list?
>

Ok, I'll be as concrete as I can :

** Autocomplete (suggest)

<input />
<div class="candidates">
<div class"candidate">Foo1</div>
<div class"candidate">Foo2</div>
</div>

Here the candidates list is supposed to be refreshed with each
additional letter entered in the input. The arrow keys allow one to
select a candidate.
Once in the list (visually), the right arrow or enter key sets the input value.

** ComboBox

<input />
<select id="candidates">
<option class"candidate" value="Foo1">Foo1</div>
<option class"candidate" value="Foo2">Foo2</div>
</select>

OR

<input /><button class="showAllCandidates" />
<div class="candidates">
<div class"candidate">Foo1</div>
<div class"candidate">Foo2</div>
</div>

Here the candidates list is immutable. The button is meant to reveal
it all (is it necessary if we have a select ?)
The arrow keys allow one to select a candidate.
Once in the list (visually), the right arrow or enter key sets the input value.

** HTML5 DataList

<input list="candidates" />
<datalist id="candidates">
<option class"candidate">Foo1</div>
<option class"candidate">Foo2</div>
</datalist>

Here the candidates list is immutable. The down or up arrow keys
reveal the whole list.
Once in the list (visually), the right arrow or enter key sets the input value.


**** What's common

- an input that holds the eventual value.
- a list of html elements that each holds a potential value for the input.

** What's different

- the status of the list of items : mutable (determined by the current
input value) VS immutable (once and for all put in the html code)
- the html code representing the list of items

Does that clarify things ?

Naftoli Gugenheim

unread,
Dec 18, 2011, 2:13:55 AM12/18/11
to lif...@googlegroups.com
2011/12/17 Grégoire Neuville <gregoire...@gmail.com>
Indeed, very clear, but --- correct me if I'm wrong --- there should be more differences, because the html is different in two respects: whether the list is implemented with a div or a select, and whether or not there's a button. Another difference is when the list appears. The latter depends on the previous difference, but I think they count as two. Also, rather than div vs. select, which is an implementation detail, specify what's the intended difference of effect (scrollbar's vs. none?).

Now that we've been precise, I'd like to question whether that calls for two different traits (separate factory objects is certainly fine). The room for objection is based on three questions: (1) what if one wants a different combination of feature sets (e.g., a button to reveal the list but the list is filtered); (2) whether those differences are fundamental or incidental (just configuration); and (3) whether those questions are either-or or can be generalized in a way that's more flexible and provides more options.
Note that I'm not saying I disagree, I'm asking your opinion on these points (per difference).

Addressing the differences:
1) Mutable list vs. immutable list: I think it's less "mutable list" and more "the list is a function of the text entered." The typical case is that the function is _ startsWith _, but that's not necessarily true (for instance, it be _ containsSlice _, or take into account camel-case abbreviations); and "immutable list" is then {_ => true}.
2) div vs. select: Why? Is there a visual difference between the two that can't be implemented in one or the other?


A separate point:
I wrote the reactive.web.html package earlier on in writing reactive-web. Someone pointed out that they're not so designer friendly --- they render an Elem from scratch, so there's no way to merge them with the template. Also, it's nice that TextInput#size is a PropertyVar so you can forward a changing Signal to it, but most people don't make their text boxes change size dynamically! And you generally don't want to have to write its size in the code.
Since then I wrote more functional primitives in the reactive.web package. For instance, rather than rendering a TextInput, write a text input element in the template, and attach to it a value attribute that has an onchange or onkeyup event attached to it. I think a good rule of thumb might be that as much as possible, the html package --- or at least the object oriented style it uses --- should simply combine functionality that's in the web package for convenience. On the other hand, the web package should probably not be html-specific (for instance, everything there should be equally useful for dynamic SVG).
So I guess in summary: (1) The web package is for things that apply to browser doms and the html package is for things that are html specific. (2) Objected-oriented design should be there as a convenience for combining things that are available in functionally designed pieces.
If you're not comfortable with the above then don't worry about it; things can always be factored out later if necessary. But at least keep in mind that it's the ideal.

Thanks!

Grégoire Neuville

unread,
Dec 18, 2011, 7:03:59 PM12/18/11
to lif...@googlegroups.com
Hi,

> Indeed, very clear, but --- correct me if I'm wrong --- there should be more
> differences, because the html is different in two respects: whether the list
> is implemented with a div or a select, and whether or not there's a button.
> Another difference is when the list appears. The latter depends on the
> previous difference, but I think they count as two. Also, rather than div
> vs. select, which is an implementation detail, specify what's the intended
> difference of effect (scrollbar's vs. none?).

Ok, here is the updated "spec" :


** Autocomplete (suggest)

<input />
<div class="candidates">
<div class"candidate">Foo1</div>
<div class"candidate">Foo2</div>
</div>

- The candidates list is supposed to be refreshed with each


additional letter entered in the input.

- The candidates list appears when it is non empty.
- The candidates list can be constrained by a max attribute which
limits the number of candidates displayed ; if more of them are
available, the list is scrollable.
- The arrow keys allow one to select a candidate.
- Once in the list (visually), the right arrow or enter key sets the
input value.

** ComboBox

<input />
<select id="candidates">
<option class"candidate" value="Foo1">Foo1</div>
<option class"candidate" value="Foo2">Foo2</div>
</select>

OR

<input /><button class="showAllCandidates" />
<div class="candidates">
<div class"candidate">Foo1</div>
<div class"candidate">Foo2</div>
</div>

- The candidates list isn't dependent on the input value
- The button is meant to reveal it all (is it necessary if we have a select ?)
- The candidates list also appears when text is entered in the input
that matches the beginning of certain candidates ; the first of them
is then highlighted.
- The candidates list is scrollable
- The arrow keys allow one to select a candidate.
- Once in the list (visually), the right arrow or enter key sets the
input value.

** HTML5 DataList

<input list="candidates" />
<datalist id="candidates">
<option class"candidate">Foo1</div>
<option class"candidate">Foo2</div>
</datalist>

- The whole behaviour isn't based on javascript. It's pure html 5 and
thus browser dependent (as for now).


**** What's common

- an input that holds the eventual value.
- a list of html elements that each holds a potential value for the input.

- the automatic display of the list if the text entered in the input
actually matches one or more candidates.

**** What's different

- the status of the list of items : determined or not by the text
entered in the input
- the possibility to manually display the whole list of items


- the html code representing the list of items

**** What would be nice ?

http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/ComboBox/ComboBox.aspx

An ASP thing that nonetheless presents two interesting behaviours
concerning the input :

- One cannot enter words in the input that are not in the list
- the characters already entered in the input are followed by the
highlighted complementary characters of the firs matching candidate.

More on the rest later (sorry, not much time to deal with our concerns
these days).

Grégoire Neuville

unread,
Jan 4, 2012, 5:28:38 PM1/4/12
to lif...@googlegroups.com
Hey,

I'm back ! (with a vengeance)

> Indeed, very clear, but --- correct me if I'm wrong --- there should be more
> differences, because the html is different in two respects: whether the list
> is implemented with a div or a select, and whether or not there's a button.
> Another difference is when the list appears. The latter depends on the
> previous difference, but I think they count as two.

Does the updated "spec" sums the commonalities and differences up more
adequately ?

> Also, rather than div
> vs. select, which is an implementation detail, specify what's the intended
> difference of effect (scrollbar's vs. none?).

Yeah. Actually I was thinking about the reuse of existing code ; but
you're right, this is more of an implementation detail.

>
> Now that we've been precise, I'd like to question whether that calls for two
> different traits (separate factory objects is certainly fine). The room for
> objection is based on three questions: (1) what if one wants a different
> combination of feature sets (e.g., a button to reveal the list but the list
> is filtered); (2) whether those differences are fundamental or incidental
> (just configuration); and (3) whether those questions are either-or or can
> be generalized in a way that's more flexible and provides more options.
> Note that I'm not saying I disagree, I'm asking your opinion on these points
> (per difference).

I would frankly bend toward the configuration perspective. By
configuration, I mean parameters to pass to different factory methods
(very much like your existing Select component).

>
> Addressing the differences:
> 1) Mutable list vs. immutable list: I think it's less "mutable list" and
> more "the list is a function of the text entered." The typical case is that
> the function is _ startsWith _, but that's not necessarily true (for
> instance, it be _ containsSlice _, or take into account camel-case
> abbreviations); and "immutable list" is then {_ => true}.

Why not a String => Seq[T] function as a parameter ? The "immutable
list" would then be {_ => theWholeListOfTs}

> 2) div vs. select: Why? Is there a visual difference between the two that
> can't be implemented in one or the other?

Again, my thinking was here "we could reuse your already existing
Select component here, and that would imply the non need of a
supplementary button". The entailed visual differences would
nonetheless not be anything we couldn't address through
css/javascript, wouldn't it ?

To sum up :

- the status of the list of items : determined or not by the text
entered in the input

=> could be the String => Seq[T] function as a parameter

- the possibility to manually display the whole list of items
=> a boolean parameter that triggers the rendering of the adequate button

- the html code representing the list of items

=> a Repeater parameter ?

- the scrollabilty of the list
=> an Int parameter ? (the number of items from which the list
becomes scrollable ?)

The several factory methods would play appropriately with these
parameters, providing default values where necessary ?

But you've been speaking about potentially using several traits ? What
were you thinking about ? I see that you modelled the concrete (html)
components (such as the Select again) of reactive-web as plain
classes. Do you envisage our Autocomplete stuff at a higher level ?

>
>
> A separate point:
> I wrote the reactive.web.html package earlier on in writing reactive-web.
> Someone pointed out that they're not so designer friendly --- they render an
> Elem from scratch, so there's no way to merge them with the template. Also,
> it's nice that TextInput#size is a PropertyVar so you can forward a changing
> Signal to it, but most people don't make their text boxes change size
> dynamically! And you generally don't want to have to write its size in the
> code.
> Since then I wrote more functional primitives in the reactive.web package.
> For instance, rather than rendering a TextInput, write a text input element
> in the template, and attach to it a value attribute that has an onchange or
> onkeyup event attached to it. I think a good rule of thumb might be that as
> much as possible, the html package --- or at least the object oriented style
> it uses --- should simply combine functionality that's in the web package
> for convenience. On the other hand, the web package should probably not be
> html-specific (for instance, everything there should be equally useful for
> dynamic SVG).
> So I guess in summary: (1) The web package is for things that apply to
> browser doms and the html package is for things that are html specific.
> (2) Objected-oriented design should be there as a convenience for combining
> things that are available in functionally designed pieces.
> If you're not comfortable with the above then don't worry about it; things
> can always be factored out later if necessary. But at least keep in mind
> that it's the ideal.

Thanks for the enlightening piece of history, I'll try to stick with
what it implies.

One last point : in my view, there is only one thing that doesn't
already exists in reactive-web to implement an autocomplete correctly
: the ability to trigger focus events on the browser from the server.
We talked about that already, and you graciously showed me a possible
solution. But since then and the rest of our discussion, I'm puzzled.
In particular :

- Is this ability proper to html (I know one can melt javascript code
with SVG, but are SVG elements focusable ; a quick search seems to
indicate they're not) ?
- in the particular case of html, I told you that per default not all
elements are focusable and that those that are not have to bear a
tabindex attribute to become so. How to model that ? I confess I would
need a hint or two here...

Thanks (and a happy new year),

G.N

Naftoli Gugenheim

unread,
Jan 6, 2012, 2:47:32 AM1/6/12
to lif...@googlegroups.com
Sorry, I'm way to tired right now to give a proper reply. If you need more help keep bugging me, although I'm not sure when I'll be online next (I wasn't for a while).
Regarding focusing, I too am not sure of the best approach, although it should be based on JsStub --- have you seen it? Perhaps something like this:

object RElem {
  implicit def toDomMethods(r: RElem)(implicit p: Page): DomMethods = jsProxy[DomMethods]("document.getElementById('"+r.id+"')")
}
trait DomMethods extends JsStub {
  def focus(): JsExp[JsVoid]
}

Then you can call mySelect.focus(). Not sure if you have to do it in a Javascript { ... } block --- probably you do.


2012/1/4 Grégoire Neuville <gregoire...@gmail.com>

Naftoli Gugenheim

unread,
Jan 18, 2012, 4:55:21 AM1/18/12
to lif...@googlegroups.com
I think a better approach for focusing, and calling javascript methods on the dom, in response to events is as follows:
Currently, (at least after the next publish) you can add a client-side event listener like this:

val clicks = DomEventSource.click
clicks foreach { x: $[JsObj] =>
  Return(17)
}.$

That is, DomEventSource has a javascript foreach method that delegates to DomEventSource#jsEventStream#foreach, which takes a JsExp[JsObj =|> JsVoid]. And a scala JsExp[T <: JsAny] => JsStatement can be automatically converted into a JsExp[JsObj =|> JsVoid] that renders the JsStatements in the function.

So G-d willing, I need to do two things:
1) Make a JsStub for dom elements, that at least has the focus method; and
2) Allow (T <: JsStub) => JsStatement to be converted as well to a JsExp[JsObj =|> JsVoid].

Grégoire Neuville

unread,
Jan 18, 2012, 8:03:04 AM1/18/12
to lif...@googlegroups.com
Hi,

> Sorry, I'm way to tired right now to give a proper reply.

No pb. You're kind enough to help me : this is something at least !

If you need more
> help keep bugging me, although I'm not sure when I'll be online next (I
> wasn't for a while).
> Regarding focusing, I too am not sure of the best approach, although it
> should be based on JsStub --- have you seen it? Perhaps something like this:
>
> object RElem {
>   implicit def toDomMethods(r: RElem)(implicit p: Page): DomMethods =
> jsProxy[DomMethods]("document.getElementById('"+r.id+"')")
> }
> trait DomMethods extends JsStub {
>   def focus(): JsExp[JsVoid]
> }
>
> Then you can call mySelect.focus(). Not sure if you have to do it in a
> Javascript { ... } block --- probably you do.

I did that but stumble across an issue : the id produced in
jsProxy[DomMethods]("document.getElementById('"+r.id+"')") doesn't
match those available in the browser's dom (leading to a javascript
'null' error when calling focus()) when I try to call focus on one of
the repeater's child. So I'm in the process of trying to reproduce
this behaviour in a scala test, to no avail thus far. I'll put
together that test case on github and ask back for your opinion.

Thanks !

Grégoire Neuville

unread,
Jan 18, 2012, 8:10:33 AM1/18/12
to lif...@googlegroups.com
On 18 January 2012 10:55, Naftoli Gugenheim <nafto...@gmail.com> wrote:
> I think a better approach for focusing, and calling javascript methods on
> the dom, in response to events is as follows:
> Currently, (at least after the next publish) you can add a client-side event
> listener like this:
>
> val clicks = DomEventSource.click
> clicks foreach { x: $[JsObj] =>
>   Return(17)
> }.$
>
> That is, DomEventSource has a javascript foreach method that delegates to
> DomEventSource#jsEventStream#foreach, which takes a JsExp[JsObj =|> JsVoid].
> And a scala JsExp[T <: JsAny] => JsStatement can be automatically converted
> into a JsExp[JsObj =|> JsVoid] that renders the JsStatements in the
> function.
>
> So G-d willing, I need to do two things:
> 1) Make a JsStub for dom elements, that at least has the focus method; and

Don't forget that virtually all HTMLElements are focusable, provided
they bear a "tabindex" attribute correctly valued. I'm quite puzzled
with the static (i.e encoded in scala types) modelling of such a
behaviour.

> 2) Allow (T <: JsStub) => JsStatement to be converted as well to a
> JsExp[JsObj =|> JsVoid].

Thanks for the explanation.

Naftoli Gugenheim

unread,
Sep 5, 2012, 10:19:23 PM9/5/12
to lif...@googlegroups.com
Hi, sorry I haven't followed up on this sooner. I'm working on it now, with G-d's help.
I'm interesting in your input on some cleanup I just committed: https://github.com/nafg/reactive/commit/250346a3d03534a2346dceaf10c5d60c971689c2


On Wed, Nov 30, 2011 at 4:09 PM, Grégoire Neuville <gregoire...@gmail.com> wrote:
Hi all,

I added an Autocomplete component to reactive-web, following the way
html objects are represented in the html package. You can find it here
: https://gneu...@github.com/gneuvill/reactive.git in the
reactive-web sub-project, src/main/scala/reactive/web/html/components
folder.
I'm curious about any remarks, advice, critics about it.
Does it have any chance to make it to the real project ?

Thanks,

Grégoire Neuville

unread,
Sep 16, 2012, 4:51:41 PM9/16/12
to lif...@googlegroups.com
Hello,

My turn to apologize for the delay ; what you committed seems all
right to me (but frankly, it's quite a while since I have a look at
that code, so I'm afraid my view is of little value...)

On another matter, as an exercise, I'm trying to transcribe
reactive-core into Java (using the functionaljava library) ; I thus
need to dig into your code and am amazed by what you wrote ! In
particular, how on earth did you get this idea of using the
ChildEventSource class to implement the behaviour of the functional
methods map, flatmap, foldLeft, etc... ? And more generally, and if I
may ask, what documentation did you use on the subject of frp ? (I
initially tried to implement something from the "Deprecating the
observer pattern" paper but got stuck nearly immediatly...)

Regards,

G.N
> --
> --
> Lift, the simply functional web framework: http://liftweb.net
> Code: http://github.com/lift
> Discussion: http://groups.google.com/group/liftweb
> Stuck? Help us help you:
> https://www.assembla.com/wiki/show/liftweb/Posting_example_code
>
>
>



--
Grégoire Neuville
Reply all
Reply to author
Forward
0 new messages