{"id":23507305,"url":"https://github.com/pj/scalaflux","last_synced_at":"2025-05-12T15:50:56.601Z","repository":{"id":20604527,"uuid":"23885531","full_name":"pj/scalaFlux","owner":"pj","description":"Small framework implementing the flux application pattern in Scala JS.","archived":false,"fork":false,"pushed_at":"2014-09-11T21:50:10.000Z","size":400,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-16T19:14:56.710Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-10T17:58:37.000Z","updated_at":"2022-07-06T08:12:08.000Z","dependencies_parsed_at":"2022-08-31T04:51:40.641Z","dependency_job_id":null,"html_url":"https://github.com/pj/scalaFlux","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2FscalaFlux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2FscalaFlux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2FscalaFlux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pj%2FscalaFlux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pj","download_url":"https://codeload.github.com/pj/scalaFlux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253768397,"owners_count":21961318,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-25T10:17:18.053Z","updated_at":"2025-05-12T15:50:56.575Z","avatar_url":"https://github.com/pj.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# scalaFlux\n\nscalaFlux is a mini-framework for creating single page applications based on Facebook's flux architecture using \n[ScalaJS](http://www.scala-js.org/), an implementation of the [Virtual Dom](https://github.com/Matt-Esch/virtual-dom) \nand some additions to the [scalatags](https://github.com/pj/scalatags) library to tie them together.\n\nThere is a (slightly broken) TodoMVC example in examples.todomvc.\n\n## App\n\nApps are created by subclassing scala_flux.App with an appropriate class for the application's state and implementing \nthe rootId method to get the id of the element to put the view into, the defaultView method to return the applications \ndefault view and the update method which dispatches based on the application event.\n\n```scala\n\n   // Type parameter is the applications state class\n   object TodoApp extends App[TodoState] {\n        // id of an element to put the view into.\n        def rootId = {\n           \"main\"\n        }\n        \n        // default view for the project.\n        def defaultView(state: TodoState) = {\n            TodoHTML.todoView(state)\n        }\n\n        // dispatch method, takes the current state and event\n        def update(currentState: TodoState, event: DetailsEvent[_]) = {\n             event match {\n                case InitEvent() =\u003e {\n                    println(\"Hello World\")\n                    \n                    // Return initial state\n                    ImmediateResponse(TodoState())\n                }\n             }\n        }\n    }\n                \n```\n\nThe application state class holds all of the state of the application and GUI.\n\ne.g.:\n\n```scala\n    case class TodoState(\n        val todos: List[Todo], \n        val filter: FilterState = All(), \n        val currentNumber: Int = 0,\n        val toggle: Boolean = false,\n        val editing: Option[String] = None)\n```\n\nWhen handling an event a new state is created and returned using a type called Response. See below for more information\nabout the different response types.\n\n```scala\n      case Delete(id) =\u003e\n        val todos = currentState.todos.filter(_.id != id)\n        val nextState = currentState.copy(todos = todos)\n\n        ImmediateResponse(nextState)\n```\n\n## View\n\nViews are created using the scalatags library, which generates a virtual dom tree that is diffed by the current tree\nby the framework.\n\n```scala\n    header(\n      id := \"header\",\n      h1(\"todos\"),\n      input(\n        id := \"new-todo\",\n        placeholder := \"What needs to be done?\",\n        autofocus := \"true\",\n        `type` := \"text\",\n        onkeypress := EnteredEvent(\"main\")\n      )\n    )\n```\n\nEvents are classes that wrap DOM events and get passed to the dispatcher. Usually they take a value of some kind to \nidentify the source of the event such as an id.  It's also possible to create subclasses of events:\n\n```scala\n    case class Filter(override val identifier: FilterState)\n         extends DetailsEvent(identifier)\n```\n\nThe todomvc example uses a mixture of these.\n\n## Custom Events\n\nIn addition to being matched on by the dispatcher event classes do some additional things - they act as hooks to modify \nthe underlying DOM node to do things like add event listeners and contain the actual event listener function.\n\nBy default the DetailsEvent base class simply dispatches itself in response to some event:\n\n```scala\n\n    abstract class DetailsEvent[E](val identifier: E) {\n      \n      var eventName: String = \"\"\n      var underlyingEvent: Event = null\n    \n      val listener = { jsEvent: Event =\u003e\n        underlyingEvent = jsEvent\n        App.currentApp.runUpdate(this)\n      }\n    \n      // Hook into the DOM\n      @JSExport\n      def hook(element: Element, name: String, previous: js.Any): Unit = {\n        jQuery(element).off(this.eventName)\n        jQuery(element).on(this.eventName, listener)\n      }\n    }\n```\n\nThey can also be used to call some function on a DOM node but not use an event listener:\n\n```scala\n\n    // Not an event exactly, just sets focus on an element in response to focus being set\n    // to true.\n    case class FocusHook(val focus: Boolean)\n      extends DetailsEvent(Unit) {\n    \n      @JSExport\n      override def hook(element: Element, name: String, previous: js.Any): Unit = {\n        if (focus) {\n          jQuery(element).focus()\n        }\n      }\n    }\n```\n\nEvents are interfaced to the library using a subclass of VirtualDom.AttrValue.\n\n## Responses and Response Streams\n\n**NB: This stuff is currently not working and needs to be a bit better thought out. **\n\nIn the example above we return a result from the dispatch as an ImmediateResponse object.  This is a result that \nimmediately returns a new state based on the existing state.  In many cases events don't return immediately or are a \nseries of asynchronous events that update the state as they complete. There are a number of different response types to\n handle these cases:\n\n- FutureResponse[E, T](future: Future[E], updater: (T, E) =\u003e T) - A future response takes a future that produces a value \nand an updater that takes that value and returns the update state. The Stream class has an example of a future response \nbased on a timeout.\n- SequenceResponse(responses: List[Response]) - A series of other responses that are applied in sequence. As an example \nthis could be something like show a spinner, start a web service request then return the updated state. This would also \ncover animations.\n- AsSoonAsResponse(responses: List[Response]) - In other cases you want to start a series of events and update the state as soon as they arrive\n e.g. a series of web requests to load a different part of a page.\n\n## Todo\n\n- The integration with the underlying virtual dom implementation is still pretty flaky, setting values on text boxes \ndoesn't seem to work properly.\n- Streams are ugly, use a higher level abstraction such as iteratees, frp or actors instead.\n- Proper management of concurrency i.e. cancelling in progress streams of events.\n- Setting values from the underlying event on events isn't ideal, maybe a better approach to this would be to wrap the \njs event somehow and provide unapply methods for matching.\n- Undo handling.\n- Events for routing.\n- More streams.\n- Page exit event?\n- Depend on JQuery or a polyfill for all low level interactions with the DOM.\n- Scaladocs.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpj%2Fscalaflux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpj%2Fscalaflux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpj%2Fscalaflux/lists"}