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
--
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
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
> 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
case KeyUp(38, _) if !inInput =>Reactions queue JsRaw("""function(){selected = $('.""" + selectedCssClass + """');prev = selected.prev();if (prev.size() == 0)$('.""" + inputCssClass + """').focus();elseprev.focus();}()""")}
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>
> 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 ?
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
Good, I'll give it a try. Thanks again.
--
Grégoire Neuville
> Ideally, however, that would be written as scala code.Agreed. I struggled a while to achieve this, to no avail.
>See if you can makeOk, I'm willing to try that. Regarding the W3C classes, I assume you
> 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.
>
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) ?
Didn't work : each item occurrence was doubled or tripled in the
>
> 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>
>
candidates list.
Hi,
I agree. But, as far as I understand, a combobox also looks and
> 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.
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 ?
>> > 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.
>> 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.
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.
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.
No wonder you didn't : http://www.w3schools.com/html5/tag_datalist.asp
> Thanks.
Thanks for the clarification.
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 ?
> 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).
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
> 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 !
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.
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,