For elm 0.16, I just defined some ports
(that are just Signal
of data) without main function and
used Signal.map
to process data back and forth.
It is just for data processing called from nodejs (not from browser),
so I do not depend on Html
module.
Now elm 0.17 has Cmd
and Sub
instead of Signal
,
I cannot figure out how to do the same...
Can anybody give me an simplest example to do data
processing via ports exposed by worker
initialization
with elm 0.17?
Here is my simplest example with Elm 0.16...
elm 0.16 code:
module Main where
import Signal
import String exposing (isEmpty, reverse)
-- input ports
port jsToElm : Signal String
-- output ports
port elmToJs : Signal String
port elmToJs
= Signal.map String.reverse jsToElm
javascript (es6) code:
/** main */
const Elm = loadElm('./index.js') // custom function to eval Elm code
const app = Elm.worker( Elm.Main, { jsToElm: ''})
app.ports.jsToElm.send('test')
app.ports.elmToJs.subscribe(( txt ) => {
console.log( txt )
})
Update
There is now a package that gives you the ability to create a worker. See lukewestby/worker
Original Answer
I was able to get a working example by creating an Html.App program. I can't find any way around the requirement of having a main
function that results in a program that includes a view
function, and I'm not alone in that confusion.
My example here is browser based, hopefully this fits back into your node-based version; I just haven't used the node version before.
Main.elm:
port module Main exposing (..)
import Json.Decode
import Json.Encode
import Html exposing (..)
import Html.App
import String exposing (isEmpty, reverse)
-- input ports
port jsToElm : (String -> msg) -> Sub msg
-- output ports
port elmToJs : String -> Cmd msg
type alias Flags = { jsToElm : String }
main =
Html.App.programWithFlags
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
type alias Model = { text : String }
init : Flags -> (Model, Cmd Msg)
init flags =
(Model flags.jsToElm, Cmd.none)
type Msg
= Reverse String
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Reverse str ->
let reversed = reverse str
in ({ model | text = reversed }, elmToJs reversed)
view : Model -> Html.Html Msg
view model =
text <| "text is: " ++ model.text
subscriptions : Model -> Sub Msg
subscriptions _ =
jsToElm Reverse
index.html:
<script type="text/javascript" src="Main.js"></script>
<script type="text/javascript">
var app = Elm.Main.fullscreen({
jsToElm: 'first'
});
window.setTimeout(function() {
app.ports.jsToElm.send('test');
}, 1);
app.ports.elmToJs.subscribe(function ( txt ) {
console.log( txt )
});
</script>
A couple takeaways:
update
function when you want to send information back to Javascript.Program
and the only way to do that (that I could find) was to include [Html.App
]. I have a hunch that in the future, the core Router
type can be used to make a "headless" worker, but it seems like for now we're stuck with requiring the Html.App program and having to render a view.setTimeout
call when sending "test"
to the port in javascript. I'm not sure why this kludge is necessary but others have seen it before as well