Using google map in elm is a bit tricky. This tutorial will show how to initialize the map, edit the map, and get the map data through port.
Preview:https://chmar77.github.io/elm-google-map-tutorial/
Requirement
This tutorial aims for those who already familiar with elm.
The tool that is needed:
- elm:
npm install -g elm
- elm-live:
npm install -g elm-live
Setting up project
- Create following folder structure
elm-google-map├── src│ ├── Main.elm│ ├── Map.elm│ └── Port.elm└── index.html
- In index.html, copy the following code
<!DOCTYPE html><html><head><metaname="viewport"content="width=device-width, initial-scale=1"><title>Elm Google Map</title><style>html,body{margin:0;padding:0;}#map{height:300px;width:300px;}#edit-map{height:300px;width:300px;}.hidden{visibility:hidden;height:0;width:0;}</style></head><body><scriptsrc="dist/elm.js"></script><script>varapp=Elm.Main.fullscreen();</script></body></html>
- Run:
elm-package install
- Copy the following code in Main.elm
moduleMainexposing(..)importHtmlexposing(..)importHtml.Attributesexposing(..)importHtml.Eventsexposing(..)main:ProgramNeverModelMsgmain=Html.program{init=init,update=update,view=view,subscriptions=subscriptions}typealiasModel={title:String}init:(Model,CmdMsg)init=({title="Elm Google Map"},Cmd.none)typeMsg=NoOpupdate:Msg->Model->(Model,CmdMsg)updatemsgmodel=casemsgofNoOp->(model,Cmd.none)view:Model->HtmlMsgviewmodel=div[][h1[][text"Elm Google Map"]]subscriptions:Model->SubMsgsubscriptionsmodel=Sub.none
- In elm-package.json, change the source-directories from "." to "src"
"source-directories": [ "src" ],
Run
elm-live src/Main.elm --output=dist/elm.js
in the root folder of our project and go tohttp://localhost:8000/ to see our initialized projectIf you see 'Elm Google Map' then it means everything is working fine
Creating Map Module
- Copy the following code:
moduleMapexposing(Model,JsObject,init,modify,toJsObject)typeModel=Internal{latitude:Float,longtitude:Float}init:Modelinit=Internal{latitude=11.55408504200135,longtitude=104.910961602369}modify:Float->Float->Model->Modelmodifylatitudelongtitude(Internalmodel)=Internal{model|latitude=latitude,longtitude=longtitude}typealiasJsObject={lat:Float,lng:Float}toJsObject:Model->JsObjecttoJsObject(Internalmodel)={lat=model.latitude,lng=model.longtitude}
Here we are trying to make our module hidden. Record that uses the Model type, when accessing or modifying will need the function from the module. It cannot access the record directly.
Port Module
portmodulePortexposing(..)importMap-- Outgoing PortportinitializeMap:Map.JsObject->CmdmsgportinitializeEditMap:Map.JsObject->CmdmsgportmoveMap:Map.JsObject->Cmdmsg-- Incoming PortportmapMoved:(Map.JsObject->msg)->Submsg
Here we divides out port into 2 parts, incoming and outgoing.
Javascript part
- add google map script below elm.js script
<script src="https://maps.googleapis.com/maps/api/js?key={{your-api-key}}"></script>
- pass the following code below
var app = Elm.Main.fullscreen();
in index.html
app.ports.initializeMap.subscribe(function(pos){console.log("Initialize Map")varmapDiv=document.getElementById('map');console.log(pos);if(mapDiv){// MapvarmyLatlng=newgoogle.maps.LatLng(pos);varmapOptions={zoom:15,center:myLatlng};vargmap=newgoogle.maps.Map(mapDiv,mapOptions);// Markervarmarker=newgoogle.maps.Marker({position:myLatlng,title:"Hello World!"});marker.setMap(gmap);// Listening for map move eventapp.ports.moveMap.subscribe(function(pos){console.log("received",pos);varmyLatlng=newgoogle.maps.LatLng(pos);gmap.setCenter(myLatlng);marker.setPosition(myLatlng)});}else{console.log("Cant find map dom");}});
Here we just create an event listening to initializeMap port from elm. The moveMap event will be used later on when we try to edit the map.
Back to Main Module
- We can import out Map and Port Module
importMapimportPort
- Update our Model, and init
typealiasModel={title:String,map:Map.Model}
init:(Model,CmdMsg)init=({title="Elm Google Map",map=Map.init},Map.init|>Map.toJsObject|>Port.initializeMap)
- Finally update our view
view:Model->HtmlMsgviewmodel=div[][h1[][textmodel.title],p[][text<|"Current pointer"++(toString<|Map.toJsObjectmodel.map)],div[][div[id"map"][]]]
- If your are not running elm-live, run
elm-live src/Main.elm --output=dist/elm.js
again and go tohttp://localhost:8000/ - If you are able to see the map, then everything is working as expected
Editing Map
- First add additional javascript below the previous one in index.html
app.ports.initializeEditMap.subscribe(function(pos){console.log("Initialize Edit Map")varmapDiv=document.getElementById('edit-map');console.log(pos);if(mapDiv){// MapvarmyLatlng=newgoogle.maps.LatLng(pos);varmapOptions={zoom:15,center:myLatlng};vargmap=newgoogle.maps.Map(mapDiv,mapOptions);// Markervarmarker=newgoogle.maps.Marker({position:myLatlng,title:"Hello World!"});marker.setMap(gmap);gmap.addListener('drag',function(){varnewPos={lat:gmap.getCenter().lat(),lng:gmap.getCenter().lng()};marker.setPosition(newPos);app.ports.mapMoved.send(newPos);});}else{console.log("Cant find edit map dom");}});
- Back in Main.elm, add State type to our Model so that we can edit the map
typealiasModel={title:String,map:Map.Model,state:State}typeState=View|Editinit:(Model,CmdMsg)init=({title="Elm Google Map",map=Map.init,state=View},Map.init|>Map.toJsObject|>Port.initializeMap)
- Then add edit button and editView in the view function
view:Model->HtmlMsgviewmodel=div[][h1[][textmodel.title],p[][text<|"Current pointer"++(toString<|Map.toJsObjectmodel.map)],div[][div[id"map"][],button[onClickEditMap][text"Edit"]],editViewmodel]
- Copy the following code for the editView
editView:Model->HtmlMsgeditViewmodel=div[class<|casemodel.stateofView->"hidden"Edit->""][hr[][],div[id"edit-map"][],button[onClickSaveEditMap][text"Done"]]
- We also want to listen to the drag event when editing. We can do that by modifying our subscription
subscriptions:Model->SubMsgsubscriptionsmodel=Port.mapMovedOnEditMapDrag
- As you can see about, we create a few new msg and we will have to create and handle them
typeMsg=NoOp|EditMap|OnEditMapDragMap.JsObject|SaveEditMap
- In our update function
update:Msg->Model->(Model,CmdMsg)updatemsgmodel=casemsgofNoOp->(model,Cmd.none)EditMap->({model|state=Edit},model.map|>Map.toJsObject|>Port.initializeEditMap)OnEditMapDrag{lat,lng}->({model|map=Map.modifylatlngmodel.map},Cmd.none)SaveEditMap->({model|state=View},model.map|>Map.toJsObject|>Port.moveMap)
This is it
The source code is here:https://github.com/chmar77/elm-google-map-tutorial
Preview:https://chmar77.github.io/elm-google-map-tutorial/
That's the end of the tutorial. If something is not working for you, or needs explanation of certain things you do not understand, comment down below.
Thanks for reading !!!
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse