This page is no longer maintained — Please continue to the home page at www.scala-lang.org

[ANN] reactive-web

5 replies
Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
With gratitude to the One Who gives man knowledge and understanding, I am happy to announce a project I've been working on: reactive-web.
Reactive-web is new RIA framework, making it easy to write highly interactive web applications, similar to GWT, Flex, etc. It uses the Functional Reactive Programming library reactive, and requires Lift.
It allows your webapp to interact with the browser objects as if they were regular Scala objects inside the JVM, using FRP abstractions such as Event Streams and Signals. You can handle events from DOM objects in Scala code, and you can update DOM objects by mutating the corresponding object in Scala. More importantly though, you can define your view declaratively so that it will be updated automatically.
Updates to the browser in response to events that originated from the browser are returned from the same ajax request that the event was sent through. Events that originate from stimuli on the server can be sent through Lift's superb comet support.
Also, reactive has support for incremental updates to Seqs. So you have a BufferSignal, transform it in all sorts of ways, and render it via a Repeater element, and when the BufferSignal changes only the changes to the DOM will be rendered.
You can use it as a new coding style in regular Lift applications, for one page or your whole application; or you can use it to completely change the kind of webapps you write. You can even use it to build a rich desktop app, using an embedded Jetty instance.
It's at http://github.com/nafg/reactive. Note that it's a work in progress; there is still much to be done, but more in terms of completeness than in terms of plumbing.
Here is a snippet from the demo application. You can run it by cloning the repository (or just downloading the source), typing sbt "project demo" jetty from the project folder, and opening your browser to http://localhost:8080. You will see a text field whose contents are "bound" to the contents of a span, another span that's updated with the current time, and a row of numbers that randomly get added or removed.


package reactivepackage web package snippet
import _root_.scala.xml._
import net.liftweb.util.{Helpers, BindPlus}   import Helpers._  import BindPlus._import net.liftweb.http._



// Among other things, ReactiveSnippet has an implicit // val, currentPage, that among other things extends Observing.// Thus any listeners we have can be garbage collected (only) once // the snippet is garbage collected.class MainPage extends ReactiveSnippet {   //////////////////////////////////////////////////////////////////  // DEMONSTRATE REACTIONS TO CLIENT EVENTS   //////////////////////////////////////////////////////////////////     // Create a reactive text input. By default its value is updated  // when the browser fires a change event   val field = TextInput()    // Link the value property with the keyUp event   // so that it's updated on every keyUp event  field.value updateOn field.keyUp     // Set its width  field.size.value ()= 80
    // Create a Signal that binds the field's value   // Its value will be kept up to date automatically  val fieldValue = field.value.value map {v =>     "*" #> Text(v)  }     // Create a NodeSeq=>NodeSeq that renders fieldValue  // reactively in whatever element Cell is binded to.   val cell = Cell(fieldValue)       //////////////////////////////////////////////////////////////////  // DEMONSTRATE REACTIONS TO SERVER EVENTS   //////////////////////////////////////////////////////////////////     // Create an EventStream that fires timer ticks until  // the page is no longer alive   val clockES = new Timer(interval = 2000, cancel = ()=> !isPageAlive)     // Create a signal from the EventStream whose value, until  // the first tick is received, is 0L   val clockSig = clockES.hold(0L)    // Create a reactive Span Cell that displays the time in a scala.xml.Text   val clockSpan = Span(clockSig.map(t => Text((t/1000).toString)))  
  //////////////////////////////////////////////////////////////////   // DEMONSTRATE DELTA UPDATES  //////////////////////////////////////////////////////////////////     // Create an empty BufferSignal[Int]   val items = BufferSignal[Int]()    // Create a Repeater that binds each element in items   // to the html element with id="number"  // As numbers are inserted and removed from the BufferSignal,   // the corresponding html elements will be inserted and removed.  // This particular factory returns a NodeSeq=>NodeSeq that can   // be used in Lift binding.  val repeater: NodeSeq=>NodeSeq = Repeater {     items.map{      _ map { i =>        ("#number" #> i) : (NodeSeq=>NodeSeq)       }    }  }     // Count from 1 to infinity  var numbers = Stream.from(1)
  // On each clock tick do an insert or remove   for(tick <- clockES) {    println("Clock firing: " + tick)     // If items is empty then always do an append    // Otherwise do either an append or a remove,     // depending on the value of math.random    if(items.now.isEmpty || math.random > .4) {       // Get the first number in the Stream and append it to items      items.value += numbers.head       // And point to the rest of the Stream      numbers = numbers.tail     } else {      // Remove a random element from items       items.value.remove(math.random*items.now.length toInt)    }   }       /**   * The snippet function   */   def render =    "#field" #> field &     "#span" #> cell &    "#clock" #> clockSpan &     "#div" #> repeater}

/** * A bit more declarative  */class MainPage2 extends MainPage {  override def render =     "#field" #> field &    "#span" #> Cell {       field.value.value map {v => "*" #> Text(v) }    } &     "#clock" #> Span {      clockSig map {t => Text(t/1000 toString)}     } &    "#div" #> Repeater {       items map {        _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}       }    }}
Ittay Dror 2
Joined: 2010-05-05,
User offline. Last seen 42 years 45 weeks ago.
Re: [ANN] reactive-web
Looks nice, but is there a way to define wicket/lift style templates? I really don't like defining gui in the code.

Ittay

Naftoli Gugenheim wrote:
AANLkTikFLtuCkvFvD2CdrOOxqjMVD3t151MFh4xHhWsm [at] mail [dot] gmail [dot] com" type="cite"> With gratitude to the One Who gives man knowledge and understanding, I am happy to announce a project I've been working on: reactive-web.
Reactive-web is new RIA framework, making it easy to write highly interactive web applications, similar to GWT, Flex, etc. It uses the Functional Reactive Programming library reactive, and requires Lift.
It allows your webapp to interact with the browser objects as if they were regular Scala objects inside the JVM, using FRP abstractions such as Event Streams and Signals. You can handle events from DOM objects in Scala code, and you can update DOM objects by mutating the corresponding object in Scala. More importantly though, you can define your view declaratively so that it will be updated automatically.
Updates to the browser in response to events that originated from the browser are returned from the same ajax request that the event was sent through. Events that originate from stimuli on the server can be sent through Lift's superb comet support.
Also, reactive has support for incremental updates to Seqs. So you have a BufferSignal, transform it in all sorts of ways, and render it via a Repeater element, and when the BufferSignal changes only the changes to the DOM will be rendered.
You can use it as a new coding style in regular Lift applications, for one page or your whole application; or you can use it to completely change the kind of webapps you write. You can even use it to build a rich desktop app, using an embedded Jetty instance.
It's at http://github.com/nafg/reactive. Note that it's a work in progress; there is still much to be done, but more in terms of completeness than in terms of plumbing.
Here is a snippet from the demo application. You can run it by cloning the repository (or just downloading the source), typing sbt "project demo" jetty from the project folder, and opening your browser to http://localhost:8080. You will see a text field whose contents are "bound" to the contents of a span, another span that's updated with the current time, and a row of numbers that randomly get added or removed.


package reactive package web package snippet
import _root_.scala.xml._
import net.liftweb.util.{Helpers, BindPlus}   import Helpers._   import BindPlus._ import net.liftweb.http._



// Among other things, ReactiveSnippet has an implicit // val, currentPage, that among other things extends Observing. // Thus any listeners we have can be garbage collected (only) once // the snippet is garbage collected. class MainPage extends ReactiveSnippet {   //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO CLIENT EVENTS   //////////////////////////////////////////////////////////////////      // Create a reactive text input. By default its value is updated   // when the browser fires a change event   val field = TextInput()      // Link the value property with the keyUp event   // so that it's updated on every keyUp event   field.value updateOn field.keyUp      // Set its width   field.size.value ()= 80
     // Create a Signal that binds the field's value   // Its value will be kept up to date automatically   val fieldValue = field.value.value map {v =>     "*" #> Text(v)   }      // Create a NodeSeq=>NodeSeq that renders fieldValue   // reactively in whatever element Cell is binded to.   val cell = Cell(fieldValue)         //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO SERVER EVENTS   //////////////////////////////////////////////////////////////////      // Create an EventStream that fires timer ticks until   // the page is no longer alive   val clockES = new Timer(interval = 2000, cancel = ()=> !isPageAlive)      // Create a signal from the EventStream whose value, until   // the first tick is received, is 0L   val clockSig = clockES.hold(0L)      // Create a reactive Span Cell that displays the time in a scala.xml.Text   val clockSpan = Span(clockSig.map(t => Text((t/1000).toString)))   
  //////////////////////////////////////////////////////////////////   // DEMONSTRATE DELTA UPDATES   //////////////////////////////////////////////////////////////////      // Create an empty BufferSignal[Int]   val items = BufferSignal[Int]()      // Create a Repeater that binds each element in items   // to the html element with id="number"   // As numbers are inserted and removed from the BufferSignal,   // the corresponding html elements will be inserted and removed.   // This particular factory returns a NodeSeq=>NodeSeq that can   // be used in Lift binding.   val repeater: NodeSeq=>NodeSeq = Repeater {     items.map{       _ map { i =>         ("#number" #> i) : (NodeSeq=>NodeSeq)       }     }   }      // Count from 1 to infinity   var numbers = Stream.from(1)
  // On each clock tick do an insert or remove   for(tick <- clockES) {     println("Clock firing: " + tick)     // If items is empty then always do an append     // Otherwise do either an append or a remove,     // depending on the value of math.random     if(items.now.isEmpty || math.random > .4) {       // Get the first number in the Stream and append it to items       items.value += numbers.head       // And point to the rest of the Stream       numbers = numbers.tail     } else {       // Remove a random element from items       items.value.remove(math.random*items.now.length toInt)     }   }         /**    * The snippet function    */   def render =     "#field" #> field &     "#span" #> cell &     "#clock" #> clockSpan &     "#div" #> repeater }

/**  * A bit more declarative  */ class MainPage2 extends MainPage {   override def render =     "#field" #> field &     "#span" #> Cell {       field.value.value map {v => "*" #> Text(v) }     } &     "#clock" #> Span {       clockSig map {t => Text(t/1000 toString)}     } &     "#div" #> Repeater {       items map {         _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}       }     } }
imaier
Joined: 2008-07-01,
User offline. Last seen 23 weeks 2 days ago.
Re: [ANN] reactive-web

Nice work, Naftoli!

On 1/24/11 9:10 AM, Naftoli Gugenheim wrote:
> With gratitude to the One Who gives man knowledge and understanding, I
> am happy to announce a project I've been working on: reactive-web.
>
> Reactive-web is new RIA framework, making it easy to write highly
> interactive web applications, similar to GWT, Flex, etc. It uses the
> Functional Reactive Programming library reactive, and requires Lift.
>
> It allows your webapp to interact with the browser objects as if they
> were regular Scala objects inside the JVM, using FRP abstractions such
> as Event Streams and Signals. You can handle events from DOM objects in
> Scala code, and you can update DOM objects by mutating the corresponding
> object in Scala. More importantly though, you can define your view
> declaratively so that it will be updated automatically.
>
> Updates to the browser in response to events that originated from the
> browser are returned from the same ajax request that the event was sent
> through. Events that originate from stimuli on the server can be sent
> through Lift's superb comet support.
>
> Also, reactive has support for incremental updates to Seqs. So you have
> a BufferSignal, transform it in all sorts of ways, and render it via a
> Repeater element, and when the BufferSignal changes only the changes to
> the DOM will be rendered.
>
> You can use it as a new coding style in regular Lift applications, for
> one page or your whole application; or you can use it to completely
> change the kind of webapps you write. You can even use it to build a
> rich desktop app, using an embedded Jetty instance.
>
> It's at http://github.com/nafg/reactive. Note that it's a work in
> progress; there is still much to be done, but more in terms of
> completeness than in terms of plumbing.
>
> Here is a snippet from the demo application. You can run it by cloning
> the repository (or just downloading the source), typing sbt "project
> demo" jetty from the project folder, and opening your browser to
> http://localhost:8080. You will see a text field whose contents are
> "bound" to the contents of a span, another span that's updated with the
> current time, and a row of numbers that randomly get added or removed.
>
>
> package reactive
> package web
> package snippet
>
> import _root_.scala.xml._
>
> import net.liftweb.util.{Helpers, BindPlus}
> import Helpers._
> import BindPlus._
> import net.liftweb.http._
>
>
>
>
> // Among other things, ReactiveSnippet has an implicit
> // val, currentPage, that among other things extends Observing.
> // Thus any listeners we have can be garbage collected (only) once
> // the snippet is garbage collected.
> class MainPage extends ReactiveSnippet {
> //////////////////////////////////////////////////////////////////
> // DEMONSTRATE REACTIONS TO CLIENT EVENTS
> //////////////////////////////////////////////////////////////////
> // Create a reactive text input. By default its value is updated
> // when the browser fires a change event
> val field = TextInput()
> // Link the value property with the keyUp event
> // so that it's updated on every keyUp event
> field.value updateOn field.keyUp
> // Set its width
> field.size.value ()= 80
>
> // Create a Signal that binds the field's value
> // Its value will be kept up to date automatically
> val fieldValue = field.value.value map {v =>
> "*" #> Text(v)
> }
> // Create a NodeSeq=>NodeSeq that renders fieldValue
> // reactively in whatever element Cell is binded to.
> val cell = Cell(fieldValue)
> //////////////////////////////////////////////////////////////////
> // DEMONSTRATE REACTIONS TO SERVER EVENTS
> //////////////////////////////////////////////////////////////////
> // Create an EventStream that fires timer ticks until
> // the page is no longer alive
> val clockES = new Timer(interval = 2000, cancel = ()=> !isPageAlive)
> // Create a signal from the EventStream whose value, until
> // the first tick is received, is 0L
> val clockSig = clockES.hold(0L)
> // Create a reactive Span Cell that displays the time in a scala.xml.Text
> val clockSpan = Span(clockSig.map(t => Text((t/1000).toString)))
>
> //////////////////////////////////////////////////////////////////
> // DEMONSTRATE DELTA UPDATES
> //////////////////////////////////////////////////////////////////
> // Create an empty BufferSignal[Int]
> val items = BufferSignal[Int]()
> // Create a Repeater that binds each element in items
> // to the html element with id="number"
> // As numbers are inserted and removed from the BufferSignal,
> // the corresponding html elements will be inserted and removed.
> // This particular factory returns a NodeSeq=>NodeSeq that can
> // be used in Lift binding.
> val repeater: NodeSeq=>NodeSeq = Repeater {
> items.map{
> _ map { i =>
> ("#number" #> i) : (NodeSeq=>NodeSeq)
> }
> }
> }
> // Count from 1 to infinity
> var numbers = Stream.from(1)
>
> // On each clock tick do an insert or remove
> for(tick <- clockES) {
> println("Clock firing: " + tick)
> // If items is empty then always do an append
> // Otherwise do either an append or a remove,
> // depending on the value of math.random
> if(items.now.isEmpty || math.random > .4) {
> // Get the first number in the Stream and append it to items
> items.value += numbers.head
> // And point to the rest of the Stream
> numbers = numbers.tail
> } else {
> // Remove a random element from items
> items.value.remove(math.random*items.now.length toInt)
> }
> }
> /**
> * The snippet function
> */
> def render =
> "#field" #> field &
> "#span" #> cell &
> "#clock" #> clockSpan &
> "#div" #> repeater
> }
>
>
> /**
> * A bit more declarative
> */
> class MainPage2 extends MainPage {
> override def render =
> "#field" #> field &
> "#span" #> Cell {
> field.value.value map {v => "*" #> Text(v) }
> } &
> "#clock" #> Span {
> clockSig map {t => Text(t/1000 toString)}
> } &
> "#div" #> Repeater {
> items map {
> _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}
> }
> }
> }
>

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: [ANN] reactive-web
You can use it with regular Lift templates. The example I posted uses a Lift template but has the gui a bit more explicit than necessary.Specifically, there are two generic ways to fill an html element dynamically: Cell, for elements whose contents change as one, and Repeater, for "rubber-stamped" content, where the DOM should only modified incrementally -- if a new contact gets added to the database then a corresponding element is inserted in the correct place; same for deletions and replaces. (Multiple edits are batched, and you can generate the deltas via a diffing algorithm if necessary.) From the sample I posted:

    "#span" #> Cell {
      field.value.value map {v => "*" #> Text(v) }
    }
means that when whenever the value of the field's value property changes, the element in the template with id="span" is updated by running the contents of that element from the template through the css selector expression "*" #> Text(v) where v is the current value of the field. Have to run, plan to continue this message...

On Mon, Jan 24, 2011 at 4:07 AM, Ittay Dror <ittay.dror@gmail.com> wrote:
Looks nice, but is there a way to define wicket/lift style templates? I really don't like defining gui in the code.

Ittay

Naftoli Gugenheim wrote:
With gratitude to the One Who gives man knowledge and understanding, I am happy to announce a project I've been working on: reactive-web.
Reactive-web is new RIA framework, making it easy to write highly interactive web applications, similar to GWT, Flex, etc. It uses the Functional Reactive Programming library reactive, and requires Lift.
It allows your webapp to interact with the browser objects as if they were regular Scala objects inside the JVM, using FRP abstractions such as Event Streams and Signals. You can handle events from DOM objects in Scala code, and you can update DOM objects by mutating the corresponding object in Scala. More importantly though, you can define your view declaratively so that it will be updated automatically.
Updates to the browser in response to events that originated from the browser are returned from the same ajax request that the event was sent through. Events that originate from stimuli on the server can be sent through Lift's superb comet support.
Also, reactive has support for incremental updates to Seqs. So you have a BufferSignal, transform it in all sorts of ways, and render it via a Repeater element, and when the BufferSignal changes only the changes to the DOM will be rendered.
You can use it as a new coding style in regular Lift applications, for one page or your whole application; or you can use it to completely change the kind of webapps you write. You can even use it to build a rich desktop app, using an embedded Jetty instance.
It's at http://github.com/nafg/reactive. Note that it's a work in progress; there is still much to be done, but more in terms of completeness than in terms of plumbing.
Here is a snippet from the demo application. You can run it by cloning the repository (or just downloading the source), typing sbt "project demo" jetty from the project folder, and opening your browser to http://localhost:8080. You will see a text field whose contents are "bound" to the contents of a span, another span that's updated with the current time, and a row of numbers that randomly get added or removed.


package reactive package web package snippet
import _root_.scala.xml._
import net.liftweb.util.{Helpers, BindPlus}   import Helpers._   import BindPlus._ import net.liftweb.http._



// Among other things, ReactiveSnippet has an implicit // val, currentPage, that among other things extends Observing. // Thus any listeners we have can be garbage collected (only) once // the snippet is garbage collected. class MainPage extends ReactiveSnippet {   //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO CLIENT EVENTS   //////////////////////////////////////////////////////////////////      // Create a reactive text input. By default its value is updated   // when the browser fires a change event   val field = TextInput()      // Link the value property with the keyUp event   // so that it's updated on every keyUp event   field.value updateOn field.keyUp      // Set its width   field.size.value ()= 80
     // Create a Signal that binds the field's value   // Its value will be kept up to date automatically   val fieldValue = field.value.value map {v =>     "*" #> Text(v)   }      // Create a NodeSeq=>NodeSeq that renders fieldValue   // reactively in whatever element Cell is binded to.   val cell = Cell(fieldValue)         //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO SERVER EVENTS   //////////////////////////////////////////////////////////////////      // Create an EventStream that fires timer ticks until   // the page is no longer alive   val clockES = new Timer(interval = 2000, cancel = ()=> !isPageAlive)      // Create a signal from the EventStream whose value, until   // the first tick is received, is 0L   val clockSig = clockES.hold(0L)      // Create a reactive Span Cell that displays the time in a scala.xml.Text   val clockSpan = Span(clockSig.map(t => Text((t/1000).toString)))   
  //////////////////////////////////////////////////////////////////   // DEMONSTRATE DELTA UPDATES   //////////////////////////////////////////////////////////////////      // Create an empty BufferSignal[Int]   val items = BufferSignal[Int]()      // Create a Repeater that binds each element in items   // to the html element with id="number"   // As numbers are inserted and removed from the BufferSignal,   // the corresponding html elements will be inserted and removed.   // This particular factory returns a NodeSeq=>NodeSeq that can   // be used in Lift binding.   val repeater: NodeSeq=>NodeSeq = Repeater {     items.map{       _ map { i =>         ("#number" #> i) : (NodeSeq=>NodeSeq)       }     }   }      // Count from 1 to infinity   var numbers = Stream.from(1)
  // On each clock tick do an insert or remove   for(tick <- clockES) {     println("Clock firing: " + tick)     // If items is empty then always do an append     // Otherwise do either an append or a remove,     // depending on the value of math.random     if(items.now.isEmpty || math.random > .4) {       // Get the first number in the Stream and append it to items       items.value += numbers.head       // And point to the rest of the Stream       numbers = numbers.tail     } else {       // Remove a random element from items       items.value.remove(math.random*items.now.length toInt)     }   }         /**    * The snippet function    */   def render =     "#field" #> field &     "#span" #> cell &     "#clock" #> clockSpan &     "#div" #> repeater }

/**  * A bit more declarative  */ class MainPage2 extends MainPage {   override def render =     "#field" #> field &     "#span" #> Cell {       field.value.value map {v => "*" #> Text(v) }     } &     "#clock" #> Span {       clockSig map {t => Text(t/1000 toString)}     } &     "#div" #> Repeater {       items map {         _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}       }     } }

Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: [ANN] reactive-web


On Mon, Jan 24, 2011 at 10:09 PM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
You can use it with regular Lift templates. The example I posted uses a Lift template but has the gui a bit more explicit than necessary.Specifically, there are two generic ways to fill an html element dynamically: Cell, for elements whose contents change as one, and Repeater, for "rubber-stamped" content, where the DOM should only modified incrementally -- if a new contact gets added to the database then a corresponding element is inserted in the correct place; same for deletions and replaces. (Multiple edits are batched, and you can generate the deltas via a diffing algorithm if necessary.) From the sample I posted:

    "#span" #> Cell {
      field.value.value map {v => "*" #> Text(v) }
    }
means that when whenever the value of the field's value property changes, the element in the template with id="span" is updated by running the contents of that element from the template through the css selector expression "*" #> Text(v) where v is the current value of the field. Have to run, plan to continue this message..

Anyway...So the signature of that factory is Cell(binding: Signal[NodeSeq=>NodeSeq]): NodeSeq=>NodeSeq, which means that the signal's value is a binding function. Of course the signal can be composed from several other signals: "#span" #> Cell {  for {    text <- field.value.value    time <- clockSignalThatTicksEvery5Seconds  } yield "*" #> ("You said " + text + " at " + time) }Note that every five seconds the span's contents will be replaced via comet, but whenever you edit the text field it will trigger an ajax request and the update javascript will be returned from that ajax request.
Similarly you can use Repeater with a gui defined in the template. Take the Repeater in the example code. Here is the relevant part of the template:div id="div"><span> [[ <span id="number" /> ]] </span></div>
And in the declarative render function:
    "#div" #> Repeater {      items map {        _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}      }    }
To break this down: The Repeater factory returns a NodeSeq=>NodeSeq which is used in Lift CSS-selector-based binding (or old-style binding). The factory takes in a SeqSignal[NodeSeq=>NodeSeq]. Since Items is a SeqSignal[Int], we call map to get a signal that transforms Ints into bind functions. However SeqSignal[Int] extends Signal[Seq[Int]], so we have to nest another call to map; the first maps the signal, the second maps the elements of the signal. Anyway, for each element i, for each value of the SeqSignal, we return a binding function that will operate on all of the div's contents. Thus the output will be something like  [[ 1 ]]  [[ 2 ]] etc. So far no gui in code, do you agree?I'm splitting the rest into another message in case my battery dies...

.

On Mon, Jan 24, 2011 at 4:07 AM, Ittay Dror <ittay.dror@gmail.com> wrote:
Looks nice, but is there a way to define wicket/lift style templates? I really don't like defining gui in the code.

Ittay

Naftoli Gugenheim wrote:
With gratitude to the One Who gives man knowledge and understanding, I am happy to announce a project I've been working on: reactive-web.
Reactive-web is new RIA framework, making it easy to write highly interactive web applications, similar to GWT, Flex, etc. It uses the Functional Reactive Programming library reactive, and requires Lift.
It allows your webapp to interact with the browser objects as if they were regular Scala objects inside the JVM, using FRP abstractions such as Event Streams and Signals. You can handle events from DOM objects in Scala code, and you can update DOM objects by mutating the corresponding object in Scala. More importantly though, you can define your view declaratively so that it will be updated automatically.
Updates to the browser in response to events that originated from the browser are returned from the same ajax request that the event was sent through. Events that originate from stimuli on the server can be sent through Lift's superb comet support.
Also, reactive has support for incremental updates to Seqs. So you have a BufferSignal, transform it in all sorts of ways, and render it via a Repeater element, and when the BufferSignal changes only the changes to the DOM will be rendered.
You can use it as a new coding style in regular Lift applications, for one page or your whole application; or you can use it to completely change the kind of webapps you write. You can even use it to build a rich desktop app, using an embedded Jetty instance.
It's at http://github.com/nafg/reactive. Note that it's a work in progress; there is still much to be done, but more in terms of completeness than in terms of plumbing.
Here is a snippet from the demo application. You can run it by cloning the repository (or just downloading the source), typing sbt "project demo" jetty from the project folder, and opening your browser to http://localhost:8080. You will see a text field whose contents are "bound" to the contents of a span, another span that's updated with the current time, and a row of numbers that randomly get added or removed.


package reactive package web package snippet
import _root_.scala.xml._
import net.liftweb.util.{Helpers, BindPlus}   import Helpers._   import BindPlus._ import net.liftweb.http._



// Among other things, ReactiveSnippet has an implicit // val, currentPage, that among other things extends Observing. // Thus any listeners we have can be garbage collected (only) once // the snippet is garbage collected. class MainPage extends ReactiveSnippet {   //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO CLIENT EVENTS   //////////////////////////////////////////////////////////////////      // Create a reactive text input. By default its value is updated   // when the browser fires a change event   val field = TextInput()      // Link the value property with the keyUp event   // so that it's updated on every keyUp event   field.value updateOn field.keyUp      // Set its width   field.size.value ()= 80
     // Create a Signal that binds the field's value   // Its value will be kept up to date automatically   val fieldValue = field.value.value map {v =>     "*" #> Text(v)   }      // Create a NodeSeq=>NodeSeq that renders fieldValue   // reactively in whatever element Cell is binded to.   val cell = Cell(fieldValue)         //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO SERVER EVENTS   //////////////////////////////////////////////////////////////////      // Create an EventStream that fires timer ticks until   // the page is no longer alive   val clockES = new Timer(interval = 2000, cancel = ()=> !isPageAlive)      // Create a signal from the EventStream whose value, until   // the first tick is received, is 0L   val clockSig = clockES.hold(0L)      // Create a reactive Span Cell that displays the time in a scala.xml.Text   val clockSpan = Span(clockSig.map(t => Text((t/1000).toString)))   
  //////////////////////////////////////////////////////////////////   // DEMONSTRATE DELTA UPDATES   //////////////////////////////////////////////////////////////////      // Create an empty BufferSignal[Int]   val items = BufferSignal[Int]()      // Create a Repeater that binds each element in items   // to the html element with id="number"   // As numbers are inserted and removed from the BufferSignal,   // the corresponding html elements will be inserted and removed.   // This particular factory returns a NodeSeq=>NodeSeq that can   // be used in Lift binding.   val repeater: NodeSeq=>NodeSeq = Repeater {     items.map{       _ map { i =>         ("#number" #> i) : (NodeSeq=>NodeSeq)       }     }   }      // Count from 1 to infinity   var numbers = Stream.from(1)
  // On each clock tick do an insert or remove   for(tick <- clockES) {     println("Clock firing: " + tick)     // If items is empty then always do an append     // Otherwise do either an append or a remove,     // depending on the value of math.random     if(items.now.isEmpty || math.random > .4) {       // Get the first number in the Stream and append it to items       items.value += numbers.head       // And point to the rest of the Stream       numbers = numbers.tail     } else {       // Remove a random element from items       items.value.remove(math.random*items.now.length toInt)     }   }         /**    * The snippet function    */   def render =     "#field" #> field &     "#span" #> cell &     "#clock" #> clockSpan &     "#div" #> repeater }

/**  * A bit more declarative  */ class MainPage2 extends MainPage {   override def render =     "#field" #> field &     "#span" #> Cell {       field.value.value map {v => "*" #> Text(v) }     } &     "#clock" #> Span {       clockSig map {t => Text(t/1000 toString)}     } &     "#div" #> Repeater {       items map {         _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}       }     } }


Naftoli Gugenheim
Joined: 2008-12-17,
User offline. Last seen 42 years 45 weeks ago.
Re: [ANN] reactive-web


On Tue, Jan 25, 2011 at 2:05 AM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:


On Mon, Jan 24, 2011 at 10:09 PM, Naftoli Gugenheim <naftoligug@gmail.com> wrote:
You can use it with regular Lift templates. The example I posted uses a Lift template but has the gui a bit more explicit than necessary.Specifically, there are two generic ways to fill an html element dynamically: Cell, for elements whose contents change as one, and Repeater, for "rubber-stamped" content, where the DOM should only modified incrementally -- if a new contact gets added to the database then a corresponding element is inserted in the correct place; same for deletions and replaces. (Multiple edits are batched, and you can generate the deltas via a diffing algorithm if necessary.) From the sample I posted:

    "#span" #> Cell {
      field.value.value map {v => "*" #> Text(v) }
    }
means that when whenever the value of the field's value property changes, the element in the template with id="span" is updated by running the contents of that element from the template through the css selector expression "*" #> Text(v) where v is the current value of the field. Have to run, plan to continue this message..

Anyway...So the signature of that factory is Cell(binding: Signal[NodeSeq=>NodeSeq]): NodeSeq=>NodeSeq, which means that the signal's value is a binding function. Of course the signal can be composed from several other signals: "#span" #> Cell {  for {    text <- field.value.value    time <- clockSignalThatTicksEvery5Seconds  } yield "*" #> ("You said " + text + " at " + time) }Note that every five seconds the span's contents will be replaced via comet, but whenever you edit the text field it will trigger an ajax request and the update javascript will be returned from that ajax request.
Similarly you can use Repeater with a gui defined in the template. Take the Repeater in the example code. Here is the relevant part of the template:div id="div"><span> [[ <span id="number" /> ]] </span></div>
And in the declarative render function:
    "#div" #> Repeater {      items map {        _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}      }    }
To break this down: The Repeater factory returns a NodeSeq=>NodeSeq which is used in Lift CSS-selector-based binding (or old-style binding). The factory takes in a SeqSignal[NodeSeq=>NodeSeq]. Since Items is a SeqSignal[Int], we call map to get a signal that transforms Ints into bind functions. However SeqSignal[Int] extends Signal[Seq[Int]], so we have to nest another call to map; the first maps the signal, the second maps the elements of the signal. Anyway, for each element i, for each value of the SeqSignal, we return a binding function that will operate on all of the div's contents. Thus the output will be something like  [[ 1 ]]  [[ 2 ]] etc. So far no gui in code, do you agree?I'm splitting the rest into another message in case my battery dies...

Now, some things by their very nature need to be in code. For instance, do you have a better idea of how the TextInput should be defined? Without these "DOM proxies" how would you listen to events or property changes? How would you make a property have a dynamic value? I haven't yet put in the class attribute, but one may want to write something like:val ti = TextInput()ti.classes.value ()= ti.value.value map {v => if(valid(v) Nil else List("invalid") } for a text input that continuously shows its valid state.I am all ears for ways to make the API better.

 


.

On Mon, Jan 24, 2011 at 4:07 AM, Ittay Dror <ittay.dror@gmail.com> wrote:
Looks nice, but is there a way to define wicket/lift style templates? I really don't like defining gui in the code.

Ittay

Naftoli Gugenheim wrote:
With gratitude to the One Who gives man knowledge and understanding, I am happy to announce a project I've been working on: reactive-web.
Reactive-web is new RIA framework, making it easy to write highly interactive web applications, similar to GWT, Flex, etc. It uses the Functional Reactive Programming library reactive, and requires Lift.
It allows your webapp to interact with the browser objects as if they were regular Scala objects inside the JVM, using FRP abstractions such as Event Streams and Signals. You can handle events from DOM objects in Scala code, and you can update DOM objects by mutating the corresponding object in Scala. More importantly though, you can define your view declaratively so that it will be updated automatically.
Updates to the browser in response to events that originated from the browser are returned from the same ajax request that the event was sent through. Events that originate from stimuli on the server can be sent through Lift's superb comet support.
Also, reactive has support for incremental updates to Seqs. So you have a BufferSignal, transform it in all sorts of ways, and render it via a Repeater element, and when the BufferSignal changes only the changes to the DOM will be rendered.
You can use it as a new coding style in regular Lift applications, for one page or your whole application; or you can use it to completely change the kind of webapps you write. You can even use it to build a rich desktop app, using an embedded Jetty instance.
It's at http://github.com/nafg/reactive. Note that it's a work in progress; there is still much to be done, but more in terms of completeness than in terms of plumbing.
Here is a snippet from the demo application. You can run it by cloning the repository (or just downloading the source), typing sbt "project demo" jetty from the project folder, and opening your browser to http://localhost:8080. You will see a text field whose contents are "bound" to the contents of a span, another span that's updated with the current time, and a row of numbers that randomly get added or removed.


package reactive package web package snippet
import _root_.scala.xml._
import net.liftweb.util.{Helpers, BindPlus}   import Helpers._   import BindPlus._ import net.liftweb.http._



// Among other things, ReactiveSnippet has an implicit // val, currentPage, that among other things extends Observing. // Thus any listeners we have can be garbage collected (only) once // the snippet is garbage collected. class MainPage extends ReactiveSnippet {   //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO CLIENT EVENTS   //////////////////////////////////////////////////////////////////      // Create a reactive text input. By default its value is updated   // when the browser fires a change event   val field = TextInput()      // Link the value property with the keyUp event   // so that it's updated on every keyUp event   field.value updateOn field.keyUp      // Set its width   field.size.value ()= 80
     // Create a Signal that binds the field's value   // Its value will be kept up to date automatically   val fieldValue = field.value.value map {v =>     "*" #> Text(v)   }      // Create a NodeSeq=>NodeSeq that renders fieldValue   // reactively in whatever element Cell is binded to.   val cell = Cell(fieldValue)         //////////////////////////////////////////////////////////////////   // DEMONSTRATE REACTIONS TO SERVER EVENTS   //////////////////////////////////////////////////////////////////      // Create an EventStream that fires timer ticks until   // the page is no longer alive   val clockES = new Timer(interval = 2000, cancel = ()=> !isPageAlive)      // Create a signal from the EventStream whose value, until   // the first tick is received, is 0L   val clockSig = clockES.hold(0L)      // Create a reactive Span Cell that displays the time in a scala.xml.Text   val clockSpan = Span(clockSig.map(t => Text((t/1000).toString)))   
  //////////////////////////////////////////////////////////////////   // DEMONSTRATE DELTA UPDATES   //////////////////////////////////////////////////////////////////      // Create an empty BufferSignal[Int]   val items = BufferSignal[Int]()      // Create a Repeater that binds each element in items   // to the html element with id="number"   // As numbers are inserted and removed from the BufferSignal,   // the corresponding html elements will be inserted and removed.   // This particular factory returns a NodeSeq=>NodeSeq that can   // be used in Lift binding.   val repeater: NodeSeq=>NodeSeq = Repeater {     items.map{       _ map { i =>         ("#number" #> i) : (NodeSeq=>NodeSeq)       }     }   }      // Count from 1 to infinity   var numbers = Stream.from(1)
  // On each clock tick do an insert or remove   for(tick <- clockES) {     println("Clock firing: " + tick)     // If items is empty then always do an append     // Otherwise do either an append or a remove,     // depending on the value of math.random     if(items.now.isEmpty || math.random > .4) {       // Get the first number in the Stream and append it to items       items.value += numbers.head       // And point to the rest of the Stream       numbers = numbers.tail     } else {       // Remove a random element from items       items.value.remove(math.random*items.now.length toInt)     }   }         /**    * The snippet function    */   def render =     "#field" #> field &     "#span" #> cell &     "#clock" #> clockSpan &     "#div" #> repeater }

/**  * A bit more declarative  */ class MainPage2 extends MainPage {   override def render =     "#field" #> field &     "#span" #> Cell {       field.value.value map {v => "*" #> Text(v) }     } &     "#clock" #> Span {       clockSig map {t => Text(t/1000 toString)}     } &     "#div" #> Repeater {       items map {         _ map { i => ("#number" #> i) : (NodeSeq=>NodeSeq)}       }     } }



Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland