In Part 1 of this series, we looked at how to make the right choice of skill for the basis of up-skilling, we looked at starting an Elm project, and how essential items, like routing and theming, were thought about and addressed, before starting any coding for the project itself.
In this final part, we will look at the open source contribution opportunities that came up during the project, how unknowns were discussed with the Elm community, and how certain items were selected for contributing back.
Miscellaneous hurdles relating to the project will be discussed and how they were overcome will be highlighted. The current state of the live project will be displayed, future work on the project will be addressed from an Elm perspective, and some final thoughts on Elm and this up-skilling path will be provided.
When the mathieul/brunch-with-elm-bootstrap repo was found, it seemed like a perfect start, but it did not have the necessary routing required for the yoga site. Once I got the routing package installed and working locally, a PR was created to initiate discussions with the repo maintainer.
Adding routing package: Bogdanp/elm-route · #3
This discussion proved worthwhile, as the package got included. Furthermore, valuable information on the Elm architecture was provided by the repository maintainer, and inclusion of JSON for posts was requested, motivating me to gain experience through providing that also.
Before the Bell Template was selected as the theme of choice, I had been working with Business Casual, an older, more minimal approach. This theme included a carousel, and I found that the rundis/elm-bootstrap package did not yet include one, so I created a feature request.
Implement support for Carousel · Issue #28
This was received very well, and an overview was provided to me on the requirements, should I take on the task of implementation. This is one of the times I decided not to submit a PR. This functionality was not essential to the project at the time, and the work required was quite involved for my skillset at that time, so it would have taken too long.
Thankfully, I no longer need this feature since swapping themes, and somebody else has now taken on this task, so, hopefully, it will be included soon, should we need it in future. Regardless of my project requirements, submitting this feature request was still very productive, as it motivated others and put this feature on the repository contributors' radar.
As a work-around, I converted the Bootstrap menu from Bell Template to Elm using a very useful VSCode plugin: HTML to Elm. This worked fine, but was not directly using the Bootstrap package and had messy code, among other issues. I created the following support request after several attempts.
Support Request : Converting html nav element to Navbar · Issue #53
This was one of the most rewarding issues. In the code below, it not only pointed me towards finding a working solution; it prompted me to join the Elm Slack channel, for a more immersive integration into the Elm community.
navigation : Model -> Html Msg
navigation model =
Navbar.config NavbarMsg
|> Navbar.lightCustomClass "navbar-inverse"
|> Navbar.withAnimation
|> Navbar.collapseSmall
|> Navbar.container
|> Navbar.brand [ href "/" ] [
img [ alt "", src "img/logo-nav.png" ][]
]
|> Navbar.items
[ Navbar.itemLink (linkAttrs AboutR) [ text "About" ]
, Navbar.itemLink (linkAttrs ScheduleR) [ text "Schedule" ]
, Navbar.itemLink (linkAttrs InstructorsR) [ text "Instructors" ]
, Navbar.itemLink (linkAttrs ContactR) [ text "Contact" ]
]
|> Navbar.customItems [ socialMenu ]
|> Navbar.view model.navbarState
Converting Bell Template nav to Elm Bootstrap
When you need fast results, finding a repo that does what you want, or almost does what you want, is a great start. Following that, contributing back to that repository, or the wider eco-system, when you find any issues that need work, is a great way to give back and to grow.
Learning when to contribute is key, and sometimes not contributing is the best option, in order to best focus your efforts to what is attainable and beneficial to your project and the wider ecosystem. It is a great start to just comment on issues, create issues, talk to people (Slack channels), get opinions and suggestions, and get involved!
I usually decide whether to contribute to an issue by asking myself:
I chose GitHub to host the yoga site, as it is free and directly connected to the master
branch in the repository.
However, there is a big issue: GitHub does not support SPAs!
This being an Elm app where it is effectively a single page application loading different content per route, it meant that this would not work out of the box since we need to redirect all routes through the entry point of the application.
There is a work-around – rafrex/spa-github-pages
– but, to be clear, it is a hack. It routes all requests through 404.html
using JS.
This has SEO implications where Google may not index your page properly, but I settled for indexing the query that the work-around creates instead (see sitemap.xml
), and now Google indexes the pages, which resolve then to the correct route anyway. Not ideal, but fine for now, in my case.
There are 2 issues with the current implementation I would like to improve.
First, I would like to implement Google Maps with some Elm package. All I could find was a repo using Polymer Components: rtfeldman/elm-google-maps
, which is more involved than my simple use case. I am using a JS interop (more about this later) implementation at the moment instead, mostly outside of Elm.
Secondly, I would like to load the Google Maps script per route rather than for all pages. At the moment, I am using a DOM Mutation listener in JS to detect if we have a HTML element with #map
id on the page with child nodes, and, if so, we execute the Google maps script.
var observeDOM = (function(){
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver,
eventListenerSupported = window.addEventListener;
return function(obj, callback){
if( MutationObserver ){
// define a new observer
var obs = new MutationObserver(function(mutations, observer){
if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
callback();
});
// have the observer observe foo for changes in children
obs.observe( obj, { childList:true, subtree:true });
}
else if( eventListenerSupported ){
obj.addEventListener('DOMNodeInserted', callback, false);
obj.addEventListener('DOMNodeRemoved', callback, false);
}
};
})();
// Observe a specific DOM element:
var main = document.getElementById('elm-main');
observeDOM(main ,function(){
var map = document.getElementById('map');
if (map && main.contains(map) && !map.hasChildNodes()) {
initMap();
}
});
It is extra memory consumption to have such a listener, so addressing the above issue would reduce overhead in this way.
Thankfully, Elm plays nice with JS. This was essential to me in converting the Bootstrap theme to Elm.
Bell Template came with every Javascript developer's favourite library, jQuery!
I converted some of the jQuery to other options, such as the sticky plugins, which were not mobile friendly, and provided the solution back to the creators. There is still more jQuery I would like to either convert to Elm or use a more modern JS equivalent e.g. – the stellar.js plugin for adding scrolling effects.
A general code clean up and refactor is required, as this project was patched together quickly to get things working. The code is therefore likely to be inefficient and repetitive.
For example, I would like to refactor the schedule table into a function that generates the timetables based on time and location params, rather than the current, statically created table. Same for the current method of defining class features, needs moar cowbell!
The final and most important improvement required is internationalisation. As we are based in Japan, and want to attract both Japanese and English speakers, the website should be in both languages.
I found the only runtime internationalisation package I could find: ChristophP/elm-i18next
. The others I found were build time internationalisation, and I need to switch during runtime.
I got some help from the maintainer in issue #3: Routing Based Translations, and I now have the website serving all strings in Japanese via JSON using this library. We just need to be able to swap the translation JSON file based on route. I even got PR #3 in this repo on improving the running of test cases contributed in the process.
We are up and running now on AhimsaYoga.JP with a new website using the Elm framework, Bootstrap 4 template and hosted on GitHub via the SPA work-around.
So, follow us on Facebook, Instagram and Twitter.
I am looking forward now to building upon this solid base, for this site and future Elm apps.
I like Elm a lot, the compiler is super helpful, and the community has been amazing – so responsive and helpful to any issues I have had.
I just hope Elm gets more popular, as finding packages to suit your needs can be somewhat tricky at the moment. It does not have a super-power company like Facebook or Google backing it, which is great in that it is a proper community software, rather than corporate open-source software. On the other hand, however, maybe that is what it takes to get traction these days, sadly.
My next steps in Elm will be to get back to fundamentals, reading the Elm docs per core package and take the Elm Beyond the Basics course by KnowThen.
Many things were learned on this up-skilling journey, not just Elm. I also learned more about JS along the way, due to the JS interop and about hosting SPAs in GitHub, etc.
I hope people find it useful to think about up-skilling in this multi-faceted way, as this process certainly benefited: