Peter Szerzo
Cph Frontenders, July 12th, 2016
I write front-end at Airtame.
I go by @pickled-plugins on GitHub, @peterszerzo on Twitter. I'm online on peterszerzo.com.
Albatross is one of them.
It's an interactive map that hosts and documents everyday sounds. A sound diary, if you will.
It's on GitHub.
...for heaven's sake.
No code reviews. No white-board discussions.
And so...
The module name is messed up for ././src/Main/Messages.elm
According to the file's name it should be Main.Messages
According to the source code it should be Main.Messagess
Which is it?
This `case` does not have branches for all possibilities.
case msg of
WaterYourPlants -> "indeed"
TalkAboutYourFeelings -> "always"
You need to account for the following values:
HaveAGreatTime
Add a branch to cover this pattern!
for
loops, juuust some functions.model
).messages
), and the changes themselves (updates
).view
), wires up event listeners to these ways of changes.if
, try/catch
(they're awkward).fetch
or promises
(they bite and cause nightmares).typeof a === 'undefined' || a === null
.$('.everything').fadeOut()
. Elm says no can do!function greetFirst(names) {
var first = names[0];
if (typeof first !== 'undefined' && first !== null) {
return 'Hello, ' + first;
}
return 'Umm, your name again?';
}
greetFirst : List String -> String
greetFirst names =
List.head names
|> Maybe.map (\name -> "Hello, " ++ name)
|> Maybe.withDefault "Um, your name again?"
type Route = Home | About
type alias Sound =
{ id : String
, title : String
, trackUrl : String
, lat : Float
, lng : Float
}
type alias Model =
{ route : Route
, sounds : List Sound
, activeSoundId : Maybe String
, isMapReady : Bool
}
A function that returns two things:
init : (Model, Cmd Msg)
init =
( Model Home [] Nothing False
, Cmd.batch [fetchSoundsCommand, createMapCommand]
)
A message describes what can possibly happen with this UI.
type Msg =
ChangeRoute |
MapReady Bool |
SetActiveSound String |
ClearActiveSound |
FetchSoundsFail Http.Error |
FetchSoundsSucceed (List Sound)
The update function takes a message and the old state of the UI as arguments. It returns two things (like before):
update msg model =
case msg of
SetActiveSoundId id ->
({model | activeSoundId = id}, reloadAudioCommand)
...
The Elm program subscribes to events coming from the outside world (the time, websockets, messages from JS code).
subscriptions model =
Sub.batch
[ mapReady MapReady
, setActiveSound SetActiveSound
, clearActiveSound ClearActiveSound
]
Renders Html, wires up event handlers to messages.
view model =
div [class "wrapper", onClick ChangeRoute] [text "Click Me!"]
This is it!
main =
program
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
var domReady = require('domready');
var Elm = require('./Main.elm');
domReady(function() {
console.log('Hi, Mom!');
var elmApp = Elm.Main.embed(document.getElementById('elm-app'));
});
function createMap(handlers) {
var map = new mapboxgl.Map({container: 'app', style: 'mf'});
map.on('load', handlers.onCreated);
map.on('click', function(e) {
// Not asking questions, just calling the boss...
handlers.onClick(e.lngLat.lat, e.lngLat.lng);
});
}
domReady(function() {
console.log('Hi, Mom!');
var elmApp = Elm.Main.embed(document.getElementById('elm-app'));
elmApp.ports.createMap.subscribe(function() {
createMap({
onCreated: function() {
elmApp.ports.mapReady.send(true);
}
});
});
}
port mapReady : (Bool -> msg) -> Sub msg
subscriptions model = mapReady MapReady
update msg model =
case msg of
MapReady ->
({model | isMapReady = True}, renderSoundsCommand)
...
Yes. Eventually.
But I can write Elm-style JavaScript tomorrow.
Oh, and here are my slides: https://pickled-plugins.github.io/practical-elm-and-friends/
If you liked them, might you want to consider giving me feedback on my slide generator?