Coder Social home page Coder Social logo

fable-compiler / fable-arch Goto Github PK

View Code? Open in Web Editor NEW
60.0 10.0 14.0 6.63 MB

Framework for building applications based on the elm architecture.

Home Page: https://fable-compiler.github.io/fable-arch/

License: Apache License 2.0

F# 92.80% HTML 0.36% JavaScript 6.84%
fable virtual-dom elm-architecture react

fable-arch's Introduction

Fable-Arch

No more maintained. Please consider using Elmish which offer the same architecture.

Framework for building applications based on the elm architecture. Docs site

Installation

$ npm install --save virtual-dom fable-core
$ npm install --save-dev fable-arch

Usage

In an F# project (.fsproj)

  <ItemGroup>
    <Reference Include="node_modules/fable-core/Fable.Core.dll" />
  </ItemGroup>
  <Reference Include="Fable.Arch">
    <HintPath>node_modules\fable-arch\Fable.Arch.dll</HintPath>
  </Reference>

In an F# script (.fsx)

#r "node_modules/fable-core/Fable.Core.dll"
#r "node_modules/fable-arch/Fable.Arch.dll"

open Fable.Core
open Fable.Import
open Fable.Arch
open Fable.Arch.App
open Fable.Arch.App.AppApi
open Fable.Arch.Html

Rollup

If you are using Rollup as the bundler you need to tell it how to bundle virtual-dom.

Example:

  "rollup": {
    "dest": "public/bundle.js",
    "plugins": {
      "commonjs": {
        "namedExports": {
          "virtual-dom": [ "h", "create", "diff", "patch" ]
        }
      }
    }
  }

fable-arch's People

Contributors

alfonsogarciacaro avatar devinrhode2 avatar erikschierboom avatar mangelmaxime avatar markpattison avatar mastoj avatar mattsonlyattack avatar maxdeg avatar monkieboy avatar rfrerebe avatar tforkmann avatar waozi-dev avatar zaid-ajaj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fable-arch's Issues

Todomvc sample - missing file?

Sorry if this is a silly question...

At line 6 in this file in the Todomvc sample, it refers to a file "./public/index.css".

I can't see where this file comes from - it isn't in the sample, and it isn't being generated by fable or webpack.

Unable to view new docs site using Opera

Hello,

When I visit the website http://fable.io/fable-arch/ on Opera, I get a blank page.

I don't know if this is a specific issue for fable-arch or a more general fable-compiler/Fable issue.

Here is the exact error I see in the Javascript console:

Uncaught Error: Seq was empty
    at reduce (bundle.js:1573)
    at __exports.choice (bundle.js:8805)
    at __exports.anyOf (bundle.js:8814)
    at bundle.js:9064
    at bundle.js:9065
    at exports.WebApp (bundle.js:9238)
    at bundle.js:12361
reduce	@	bundle.js:1573
__exports.choice	@	bundle.js:8805
__exports.anyOf	@	bundle.js:8814
(anonymous)	@	bundle.js:9064
(anonymous)	@	bundle.js:9065
exports.WebApp	@	bundle.js:9238
(anonymous)	@	bundle.js:12361

Here are some details:

  • Operating system: Windows 7
  • Browser: 44.0.2510.73

Reproduction steps:

  1. Install Opera (latest version)
  2. Visit http://fable.io/fable-arch/
  3. Page does not load.

Workaround:

  1. Use Chrome instead of Opera.

Please let me know if you need additional information.

The `start` function returns another function, thus causing a compiler warning

let app = createApp initModel view update renderer
start app   // warning here

The last line produces a compiler warning saying "This is a function value, you're probably missing an argument". This is because the start function returns not unit, but another function of type AppMessage<_,_> -> unit. This is apparently not an idle mistake, but is used in DevTools, as pointed out by @mastoj.

The proposed fix is to have two flavors of start - one returning the message intake function, and one returning unit:

// use this for devTools and other similar clever things
let startAndExposeMessageSink ... =
    // the contents of today's `start` goes here

// use this for normal apps written by mere mortals
let start ... = 
    startAndExposeMessageSink ... |> ignore

Docs site updates

  • Add a sample to show async code
  • Add a navigation sample
  • Add a plugin sample
  • Find a way to add the React demo.
  • Add a new doc page explaining the glossary (App, Handler, etc.)
  • Standalone samples using iframe
  • Provide an easy way (template) in order to add a sample / demo

Performance optimization

As it is now the performance is sort of ok, but the implementation is 2-3 times slower than elm in https://github.com/mastoj/ui-perf, and that shouldn't be the case. I would expect the fable solution to be somewhat slower since it is more general purpose compared to elm.

  • Check where the bottle necks are, in fable-arch.app, fable-arch.virtualdom or in fable core library.
  • Fix an implementation that also supports requestanimationframe that is faster. This logic should probably move into the renderer and not in the app as it is now. The app sets the rendering rate not the renderer.

Update to use Fable 0.7 references system

Fable 0.7 introduce a new way to distribute Fable librarie references.

This could simplify the use to include Fable-Arch inside our applications because when using .fsproj we can't use anymore because Forge don't understand it for the moment.

  <ItemGroup>
    <Compile Include="node_modules/fable-import-virtualdom/Fable.Helpers.Virtualdom.fs" />
  </ItemGroup>

This shouldn't increase the size of the application because Rollup should:

[...] not only the bundle will skip unused modules (say Async if you only use native JS Promise) but also any function from imported modules that you don't actually need.

virtualNode hook?

Hi,

I may have missed it, but is there a way to call some code when a virtual node is inserted into the real DOM? In my case, I have created a simple div that I want to turn into a IDE text-editor alike using CodeMirror. For that I need to call a function of the library once my div has been inserted into the DOM but cannot find a proper way to do that.

Support VirtualDom widgets

As Fable.Arch stands today, there is no way to spin up external code, other than hacking around with element IDs and whatnot. No escape hatch.
VirtualDom has such escape hatch in the form of Widgets. I propose to add support for them to Fable.Arch's VirtualDom renderer.

I see an API along these lines:

let myView model =
    div [classy "my-component-wrapper"] 
        [Widget {
            init = fun handler -> 
                let element = Browser.document.createElement "div"
                let comp = SuperAwesomeExternalComponent.create element
                comp.onSomeEvent (fun e -> handler e.someData)
                element
            update = fun h e -> 
                null 
        } ]

The implementation seems not complicated at all.
I have a working prototype that I can clean up and submit as a PR.

Move from mailboxprocessor to standard js events

The mailboxprocessor is nice, but performance wise I think we could do much better with events. My thoughts are roughly this:

var elem = document.createElement("div"); // elem to hold state, will never be rendered
var handleEvent = function(e, post) {
    var data = e.details.data;
    var state = doSomeMagic(e.details, elem["state"]);
    elem["state"] = state;
}
var post = function(data) {
    var event  = new CustomEvent("ArchEvent", {details:{data: data}});
    elem.dispatchEvent(event);
}
elem.addEventListener("ArchEvent", function(e) { handleEvent(e,post)});

Here we create a element that will hold the state for things in the application, basically the same thing we use when we do the recursive call. We create a helper function for posting messages to that will be used to post messages on the element we created. That we just attach a event listener to the element and celebrates victory.

Upgrade to 0.7

Hi @mastoj and @MangelMaxime!

I've upgraded fable-arch to Fable 0.7, you can see the result in 0.7.0 branch. At the very least the React sample is working. You can try doing the following:

  • In repo root:
git checkout 0.7.0
npm i
node build
  • In samples/react/ dir:
npm run build
npm start

I must say fable-arch looks very neat, but the lack of comments and type annotations make it a bit difficult to follow. I tried to simplify the architecture for React, but I wasn't able because after many tries I couldn't understand the flow of the app. In any case the app works as it's now 😄 At the beginning I had the wrong impression the component was mounted multiple times or that setState was called twice every time, but after debugging the sample. It doesn't seem to be the case.

Can't combine classList and Class

If we have the following code:

ul
  [ Class "dropdown-menu"
    attribute "role" "menu"
    classList [ "is-shown", true ]
  ]
  [ dropdownItemMenu { Text = "Se déconnecter"; Action = SignOut }
  ]

Only the classList attribute will be used.

Output html:

<ul role="menu" class="is-shown"><li>Se déconnecter</li></ul>

Expected result would be:

<ul role="menu" class="dropdown-menu is-shown"><li>Se déconnecter</li></ul>

Should we a make the attribute complementary and not exclusive ?

I think it's a constraint that we have to work with but we should probably document it.

Because if we make them complementary with would not be able to overwrite attribute if we want to. etc.

Add sample for complex sub applications

Today Dave Thomas was asking about:

Is there an equivalent to App.map in fable-arch?
http://package.elm-lang.org/packages/elm-lang/html/1.0.0/Html-App#map

Here was my answer but it's not really refined we need to make a real sample of it.

/// Actions from my Main app
type Actions
    = ShowIndex
    | ShowSignIn
    | ShowDashboard
    | SignInActions of SignIn.Actions // Here is a an actions to map sub applications actions

/// Update sample
let update model action =
    let model', action' =
      match action with
      | ShowIndex | ShowSignIn ->
        Model.Generate (Some SignIn, model.UserSession, signIn = SignIn.Model.Initial), []
      | _ ->
        // Here we are mapping the sub app actions to the main Application
        let (res, action) = SignIn.update model.SignIn.Value act
        let action' = mapActions SignInActions action
        { model with SignIn = Some res}, action'
      | _ ->
        model, []

    model', action'

/// Here is a sample for the View
let view model =
    let html', hasMenu =
      match model.CurrentRoute with
      | Some r ->
        match r with
        | Index | SignIn ->
          Html.map SignInActions (SignIn.view model.SignIn.Value), true
        | _ ->
          div [] [ text "404" ], false
      | None ->
        div [] [ text "404" ], false

@mastoj I am more and more thinking about making a mini website demonstration with all the possible case usage for a classic web site.

So here are the specs:

  • Ajax
  • Routing
  • Sub apps to handle pages of the app
  • Menu

I can make the job to create the sample :). Do you see any others specs to add ?

Another questions is does the documentation generation support multiple files projects ? Because for this samples, I will need to write several files otherwise it's would not readable.

And finally, should we go for .fsx files or .fsproj ? :)

Support dispatching messages from a hook to main app

Continuing on with the discussion and proposals in #67

This would enable us writing custom view components* that are able to dispatch/send messages back to the app disptach loop.

Example code of how this would look like (notice the (dispatch: 't -> unit) parameter)

type Messages =
    | Clicked 
div 
  [ hook 
      "my-hook"
      (HookHelper.CreateHook(fun node propName (dispatch: 't -> unit) -> 
              node.addEventListener("click", fun () -> dispatch Click)
      ))
  ] [ text "I dispatch 'Click' messages" ]

I would say that the hook function should be hidden away some module like Fable.Arch.Internal as this shouldn't be used by the end user but only but implementers who write these custom components because otherwise we are breaking the whole Elm architecture by introducing side effects (playing with the DOM directly)

What do you guys think?

components: in the sense of a stateless component that has custom props and custom inner behaviour hidden from the user.

Maybe incorrect code for the update function of hello-world example?

Hello,
The new docs site look really cool! I noticed something in the hello-world sample and please correct me if I am wrong. if Model = string then the update must have signature Model -> Action -> Model but the update function in docs seems to return a tuple Model * 'a list

// isn't this update : Model -> Action -> Model * 'a list?
let update model action =
  match action with
  | ChangeInput str -> str, []

Shouldn't it be just the following?

let update model action =
  match action with
  | ChangeInput str -> str // not str, []

Navigation module don't update on load

If we are using the navigation module and loading an application with a initial state in the URL.
Then the application, init is not matching the url state.

If we trigger an update in the application or in the URL then the state is matching.

Reproduction code will come with the docs site update.

<textarea> resets caret position in MS Edge

Issue: In Microsoft Edge, a <textarea> element whose content is bound to model in some way doesn't keep caret position between model updates.

Works fine in Chrome and FireFox. I can't be sure that the issue is with Fable.Arch and not with VirtualDom or with MS Edge. But since both VirtualDom and Edge are widely used, I think I can reasonable expect bugs like this to get fixed quickly.

App code:

open Fable.Core.JsInterop
open Fable.Arch
open Fable.Arch.Html
open Fable.Arch.App

let initModel = ""

let view model =
    div []
        [ textarea [onKeyup (fun e -> unbox<string> e?target?value)] [text model]
          span [] [text model] ]

let update model msg = msg, []

createApp initModel view update Virtualdom.createRender
|> withStartNodeSelector "#root"
|> start
|> ignore

Behavior in Chrome: (normal)
fable-textarea-chrome gif

Behavior in MS Edge: (buggy)
fable-textarea-edge gif

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.