DEV Community

Cover image for Tour of an Open-Source Elm SPA

Tour of an Open-Source Elm SPA

Richard Feldman on May 08, 2017

People often ask me if I can point them to an open-source Elm Single Page Application so they can peruse its code. Ilias van Peer linked me to the...
happysalada profile image

Could detail a little bit your build process ?
(I couldn't find any real ressources on the build process for elm apps)
Checking your app you use elm-live, but I couldn't find how elm-live would do gzipping.
Does this mean in production, you rely on the server to gzip files ?

Also, since the css was provided to you, you didn't need to build it or do anything with it. There could be another advantage of a build tool. Do you use elm-live at NoRedInk too ? (I couldn't find anywhere, where you could customize, minify and gzip the css with elm-live)

marcelolaza profile image
Marcelo Lazaroni

Awesome! And you wrote the whole thing in 5 hours!

This is very informative. I think this definitely deserves a mention on Elm roadmap under "How do I make a single page app".

Great Job.

rtfeldman profile image
Richard Feldman Author

haha I didn't actually write the whole thing in 5 hours 😄 - I just didn't want to publish it until it was done, so I developed it locally and then copied everything over at the last minute after creating the repo. 😉

(It actually took closer to a week.)

sandeepdatta profile image
Sandeep Datta

Richard, can you add an explanation for why you rearchitected everything (removed the Requests, Views and Data folders) and put everything in the root folder? I have seen the video in which you say you regret separating out views etc. but you do not give any reasons. Please consider adding some explanation here instead. Thanks.

rtfeldman profile image
Richard Feldman Author

I started to write this up, but realized I'd much prefer to explain the changes in a talk rather than a post, so that's what I'm going to be doing at Oslo Elm Day in February!

I'll circle back to this post and update it to point to the talk once it's out.

carlfredrikhero profile image
sandeepdatta profile image
Sandeep Datta

Looking forward to it, thanks again!

dnk8n profile image
Dean Kayton

Very cool. Would it be possible to play with this locally? Can I set up a RealWorld backend and have this talk to it?

I tried, but couldn't figure out how to configure the backend url/endpoint. Is there a settings file I need to place somewhere?

slashmili profile image
Milad • Edited

Thanks for sharing! It's really great and it's already answered a lot of my questions :)

Just one thing that I noticed is that Main.elm file is getting long and things like ProfileLoaded and etc are residing there.

Is there a way to forward updates to its own sub module?

wintvelt profile image

This is awesome Richard. Especially your setup with variations for fast-slow-no connection with Task is great. Thanks for sharing!

dalen profile image
Erik Dalén

Thanks for the nice example, I'm borrowing some ideas for my open source Elm SPA I'm developing as I'm still a Elm newbie.

But I'm wondering about the use of Task,map instead of Cmd.batch, it seems that causes all requests to be done in sequence instead of in parallel. For example the list of articles and the list of tags are loaded after each other instead of in parallel. Is there anyway to fix that and keep using Tasks?

rtfeldman profile image
Richard Feldman Author

Not yet, but I expect there will be something in Http to parallelize HTTP requests in the future.

Since it's just a performance optimization and performance is fine as-is, I thought it'd be prematurely optimization to go out of my way to use Cmd.batch instead, but in a world where I can parallelize via Http I'd reach for that instead of Task.map2 assuming it would be an easy upgrade. :)

jdunruh profile image
John Unruh

Thanks for posting this. I am currently working on an SPA, and I have run into scaling problems with my update function and model. Your example shows exactly how to split update and the model across pages, and how to solve some other problems I have seen. This is the example needed to go past the good but simple intro material and to a real application.

ben profile image
Ben Halpern

This is so amazing, Richard.

yevheniygloba profile image

Hello, thanks a lot for the great article.
I have a question regarding handling a lot of messages in the Main file for update section: type Msg = SetRoute (Maybe Route) | HomeLoaded (Result PageLoadError Home.Model) ...

Imagine that you have a lot of these messages (in our example we have 32 messages, and 31 of them are like HomeLoaded (Result PageLoadError Home.Model) .. and HomeMsg Home.Msg)
And here we have a problem with building our app - it takes up to 50 seconds to build app.

Could you help us? Could you give us some help?

rtfeldman profile image
Richard Feldman Author • Edited

There's a bug in the 0.18 compiler that has a big performance regression when you have case-expressions with many branches (as I recall, 32 is where it kicks in). The bug will be fixed in 0.19, but in the meantime you can split some of the messages out into a separate union type just to split the one big case-expression into two case-expressions.

Does that make sense? Happy to elaborate if it's unclear!

arkadefr profile image

Hello, excellent article and example to learn ELM.
I'm a bit confused by the separation of Views and Page. Some Page have only html and are not Views. There's also a Page module included in the Views module. Can you explain a bit more this separation?

dmattia profile image
David Mattia

I can try to help here.

Richard's architecture assumes there is a clear mapping between a "Route" and a "Page". Main.setRoute takes in a Route and attempts to find a Page to set on the main state. At any given time, the PageState in the model determines how the page should look at a high level. So at a high level, "/login" maps to Route.Login in the router, which maps to Page.Login in setRoute.

However, there is a considerable amount of reused view logic that is shared between pages (or even other projects). This logic is moved into Views.* modules.

The Views.Page module could probably be named something else like View.CurrentPage, but the purpose of its frame function is to create the complete view for a user (combines the main content area from the Page's view method, the header, and the footer).

globus68 profile image

Hi Richard!

We are using your Elm SPA Example as basis for an application. We have a problem we need some guidance on: We need to trigger an API request after a sub page has been succesfully initialized and loaded.
( PrognosisLoaded (Ok subModel), _ ) ->
{ model | pageState = Loaded (Prognosis subModel) } => Cmd.none
As we cannot see a way to do this within the Prognosis subPage, our first attempt was to fire a command in place of the Cmd.none in Main.elm. However the message handling the command will be attached to the Main.elm module making it hard to update the subModel of the Prognosis subPage.

How can this be accomplished?

Best regards

PS! I read in the mailing list of your upcoming Elm book (which I of course has ordered), that you just have been married. My best wishes to both of you!

warren2k profile image
Warren Wise

You, sir, are a gem!

kuon profile image
Nicolas Goy

This is great.

I was thinking about doing the init thing for pages that return a task, but I was a bit stuck because I use remotedata, and it's sendRequest wrapper.

mchen7588 profile image

will you still be updating this article or posting a new article on the Elm 0.19 version of this app?

paulen_8 profile image
🌹 • Edited

Would love to see that as well if Richard gets a chance.
Appreciate all your awesome contributions to the community Richard!

spookylukey profile image
Luke Plant

Thanks so much for this, great resource, in fact a bit of a life-saver for my current project!

thetrompf profile image
Brian K. Christensen

Really great article. This is really inspiring and you had some very fine solutions to some potentially hard problems. Good job.

fallenprogrammr profile image
Amit Patankar

This is badass, 🙏

barbagrigia profile image
Vlad Trukhin

Great article & great job!

marceloandrade profile image
Marcelo Andrade R.

Thank you very much

4m3r profile image

Hey Richard what do you think what to know and explore before going to learn Elm ? Thx

janiczek profile image
Martin Janiczek

Can't wait for the tests :)

fyodorio profile image

I think you should update the article, takin into account the repo updates. They're not in sync anymore(

Forem Open with the Forem app