GithubHelp home page GithubHelp logo

rtfeldman / elm-spa-example Goto Github PK

View Code? Open in Web Editor NEW
3.3K 82.0 530.0 800 KB

A Single Page Application written in Elm

Home Page: https://dev.to/rtfeldman/tour-of-an-open-source-elm-spa

License: MIT License

HTML 1.35% Elm 98.65%

elm-spa-example's Introduction

RealWorld Example App

👉 I gave a talk to explain the principles I used to build this. I highly recommend watching it!

Elm codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld spec and API.

This codebase was created to demonstrate a fully fledged fullstack application built with Elm including CRUD operations, authentication, routing, pagination, and more.

For more information on how this works with other frontends/backends, head over to the RealWorld repo.

How it works

Check out the full writeup!

Building

I decided not to include a build script, since all you need for a development build is the elm executable, and all you need on top of that for production is Uglify.

Development Build

Install Elm (e.g. with npm install --global elm), then from the root project directory, run this:

$ elm make src/Main.elm --output elm.js

If you want to include the time-traveling debugger, add --debug like so:

$ elm make src/Main.elm --output elm.js --debug

To view the site in a browser, bring up index.html from any local HTTP server, for example http-server.

Production Build

This is a two-step process. First we compile elm.js using elm make with --optimize, and then we Uglify the result.

Step 1

$ elm make src/Main.elm --output elm.js --optimize

This generates production-optimized JS that is ready to be minified further using Uglify.

Step 2

(Make sure you have Uglify installed first, e.g. with npm install --global uglify-js)

$ uglifyjs elm.js --compress 'pure_funcs="F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9",pure_getters=true,keep_fargs=false,unsafe_comps=true,unsafe=true,passes=2' --output=elm.js && uglifyjs elm.js --mangle --output=elm.js

This one lengthy command (make sure to scroll horizontally to get all of it if you're copy/pasting!) runs uglifyjs twice - first with --compress and then again with --mangle.

It's necessary to run Uglify twice if you use the pure_funcs flag, because if you enable both --compress and --mangle at the same time, the pure_funcs argument will have no effect; Uglify will mangle the names first and then not recognize them when it encounters those functions later.

elm-spa-example's People

Contributors

andys8 avatar bryanjenningz avatar buinauskas avatar dmattia avatar ericsimons avatar jasondew avatar kaaloo avatar lgdean avatar liubko avatar mandrolic avatar mrvicadai avatar rtfeldman avatar simonewebdesign avatar stil4m avatar yatesco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elm-spa-example's Issues

Replace the rocket operator

In Elm 0.19 you will not be able to define infix operators which means that the rocket operator (=>) will not be allowed anymore so this code will stop working. I suggest all cases where it is being used be replaced with tuples.

typo in README.md

In the second paragraph, "to" is not required. i.e. The sentence "For more information on how to this works with ..." to be changed to "For more information on how this works with ...".

Get rid of the catch-all case in the update function

The catch-all case at the bottom makes adding new messages not fail compilation.
One way to fix this is to add a catch-all for each page message instead, like this:

       ( SettingsMsg subMsg, _ ) ->
            model => Cmd.none

elm/http 2.0

I'm working with this architecture example & an existing project that is using elm/http 2.0...is there a straightforward path to upgrading that API?

I've been attempting, but continually getting stuck...will happily do the gruntwork if I can get an assist to get started. 😃

Test failing on Url.Parser, Url.Builder - elm 0.18

Hello,
Is the current master branch dependent upon elm .19 ?
I'm receiving this on elm test :

Executing task: elm test <

I cannot find module 'Url.Parser'.

Module 'Article.Slug' is trying to import it.

Potential problems could be:

  • Misspelled the module name
  • Need to add a source directory or new dependency to elm-package.json
    Compilation failed while attempting to build /home/fitzpe/programming/elm/elm-spa-example/tests/RoutingTests.elm
    The terminal process terminated with exit code: 1

Thoughts anyone?

Thanks,
Peter Fitzgibbons

add tests?

To my understanding, the waited elm-test mentioned in tests/README.md has been released.
Even a small example of how to do tests would be useful.

A more complex example that integrates with travis-ci would be even more helpful.

Hot reloading

I love how you put it together, makes the boring decision for you, loving it.

One thing I'm unable to figure out is hot reloading. Currently, any time I change anything on a page, I lose the state after a full reload, adding -H hasn't helped. Do you have any advice?

reduce `toSession` proliferation

In the code as supplied here, each page needs to have a toSession method exposed that essentially just returns the session field out of its model. Why not make this more generic?

getSession: { a | session: Session } -> Session
getSession =
    session

Then you can replace the calls in the case statement:

        ...
        Home home ->
            Home.toSession home

        Settings settings ->
            Settings.toSession settings

with

        ...
        Home home ->
           getSession home

        Settings settings ->
            getSession settings

And there is no longer a need to have a toSession in the pages, unless you want it.

Can redirect to login/page after being logged in

Awesome SPA, thanks!

This may be a minor issue, if a user is logged in, even though the button for the login page goes away they can still manually hit the login page while logged in:

image

Not sure if you want to prevent this behavior? Not a common case but it could come up none the less.

URI pieces should be URI encoded

Summary:
The URIs of this SPA are build just by concatenating Strings. This causes "Error Loading Page" when such a string ends up in URL.

Steps to reproduce:

  1. Register a profile with username like @#$%^&*
  2. Navigate to this user's profile by clicking the username in the top navbar.

Actual result:
URL contains unescaped characters http://rtfeldman.github.io/elm-spa-example/#/profile/@#$%^&* and page shows "Error Loading Page"

Expected behavior:
URL has escaped characters and profile page is shown correctly.

I was recently solving similar problem in my app and was looking for a "canonical solution" to this kind of issue and found this example app (which I'd expect to be "golden standard" of how to write SPAs in Elm) is also plagued by this issue. I ended up solving it by writing custom URL segment parser like this:
jhrcek/random-failures@e87b20b

API Domain change

Hello!

Due to governance changes, we are now using the realworld.io domain for the RealWorld demo (both client and API).
Requests from conduit.productionready.io are redirected to api.realworld.io, but such a redirection might lead to inconsistent responses.

We encourage domain change for the community.
If this repository is maintained anymore, we'll consider hosting a demo of your implementation in a few weeks with the domain change.

The demo link will be added to the RealWorld documentation.

Browser URL and current visible page can get out of sync

It seems that with the current implementation the browser URL and the visible page can get out of sync if you navigate to a route that needs to fetch some data and then you navigate to some other route before the data had arrived.

Steps to reproduce:

  1. Start in /login page
  2. Click on "Home" link to navigate to the home
  3. Before the home data arrives, quickly click on "Sign in" link to navigate back to login page.

After home data arrives and the model is updated your browser will end up pointing to /login but the visible page will actually be the home. Another minor glitch in this scenario is that the spinner is removed before the data actually arrives. This happens because the messages arrive to the app in an undesired order:

SetRoute Just Home
SetRoute Just Login
HomeLoaded Ok …

Path-based routing

Current approach works well when you have hash-based routing.

However if you want to use path-based routing, you'd have to prevent default link behaviour in order to avoid loading the page every time, which in turn requires either:

  1. breaking modularity, since you'd have to supply the SetRoute message of the root module somehow or
  2. introducing redundant branches that handle route changes on sub-components (much like the HomeMsg and the like are wrapping the other sub-messages at the moment)

CORS issues with CakePHP and Laravel backends

I tested diferent frontends with cakephp and laravel backends.
Angular and react worked fine, but there is an issue with elm frontend seems related to CORS.

Can somebody check this issue?

"Error loading feed" in elm with .NET backend

Documentation is kinda sparse on this point, so I found out that I needed to change the url in Endpoint.elm by opening up Fiddler and watching traffic to see where my site was loading from. I found the following URL in that file, and changed it (to "http://localhost:5000"):

Url.Builder.crossOrigin "https://conduit.productionready.io"

That allowed me to talk to my backend instead of the default demo, or so I thought. But when the first page loads, I get: Error loading feed. Error loading tags.

On the backend side, I'm getting a vague error, which a Google search says is just telling me that the inbound URL (being sent by elm) is malformed. But at least I know I'm talking to the backend server:

"Microsoft.AspNetCore.Server.Kestrel Connection id '0HLJS3KI1AV8A', Request id '0HLJS3KI1AV8A:00000004': the application completed without reading the entire request body."

I downloaded the React/Redux frontend and got that working, so I know the backend is performing properly once I hit it, but it's not working in ELM version for some reason.

Don't use catch-all in main update - not a recommended way to code in Elm

Very easy fix to make the code more elm-like (aka the compiler more helpful).

You currently do

( _, _ ) ->
            -- Disregard messages that arrived for the wrong page.
            ( model, Cmd.none )

When someone adds a new page though, it will fall into this case, which is clearly not what we want. We want the compiler to force us to add it. To achieve this, it's better to instead do:

( GotEditorMsg subMsg, Editor slug editor ) ->
    Editor.update subMsg editor
        |> updateWith (Editor slug) GotEditorMsg model

( GotEditorMsg _, _ ) ->
    (model, Cmd.none)

( GotArticleMsg subMsg, Article article ) ->
     Article.update subMsg article
        |> updateWith Article GotArticleMsg model

( GotArticleMsg _, _) ->
    (model, Cmd.none)

etc.

It is more verbose but definitely more elm-like. Now if someone adds GotXMsg the compiler will tell them that they are missing a case, go elm 😃 I think it's also valuable to set an example given this repo is used as a reference for how to code in Elm by many people new to Elm.

elm-live

Ubuntu 16.04

elm-live --output=elm.js src/Main.elm --pushstate --open --debug

gives. As you see below, I seem to have elm 0.18 properly working.

I can also start the project from within elm-reactor and click on Main.elm, but the it looks ugly, since the css are not loaded properly.

mattias@omen:~/data-oss/elm/elm-spa-example$ elm-live --output=elm.js src/Main.elm --pushstate --open --debug
/usr/local/lib/node_modules/elm-live/source/elm-live.js:165
  let serverStarted;
  ^^^

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
    at exports.runInThisContext (vm.js:53:16)
    at Module._compile (module.js:374:25)
    at Object.Module._extensions..js (module.js:417:10)
    at Module.load (module.js:344:32)
    at Function.Module._load (module.js:301:12)
    at Module.require (module.js:354:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (/usr/local/lib/node_modules/elm-live/index.js:1:80)
    at Module._compile (module.js:410:26)
    at Object.Module._extensions..js (module.js:417:10)
mattias@omen:~/data-oss/elm/elm-spa-example$ ls
assets  elm-package.json  elm-stuff  index.html  LICENSE  README.md  src  tests
mattias@omen:~/data-oss/elm/elm-spa-example$ elm-repl
---- elm-repl 0.18.0 -----------------------------------------------------------
 :help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------

development reload

Is there a way to see the changes immediately in the browser when changing the code, without making and restarting the webserver? Like the way elm reactor works.

Task.map causing HTTP requests do be done in sequence

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

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 any way to fix that?

A way to not reroute some url modification.

Let's say if the user clicks on a button that opens a record, the code will open initialize and open a view, as an enhancement, the url is modified to allow users to share links, but then it will make the app do a reroute/setroute page then open exactly the same view.

So, is there a way to do this:

  • user clicks on a record, the program initialize and open the view, the url is then programatically modified for link sharing purposes <--- should not do a reroute.

  • user manually modify the url in the browser <--- should do a reroute to the pages.

Possible bug in article creation

While creating article if you click "publish" button multiple time you create multiple articles with the same content. There was no single project I've seen for which such behavior was intentional, so I'd assume that here it is a bug as well.

Log out on "Page Not Found" view doesn't change navbar

A very minor bug, but if you are on an unknown route and so on the "Page Not Found" view, and then you log out, it doesn't change the navbar to reflect that you have logged out, it just appears as if nothing has happened. You have to refresh the page for it to realize you have logged out (I believe this is because we don't watch for session changes on that page here).

Error appears in wrong page

If i change something in the Article Decoder, when i insert a new Article, the error of the decoder appers in the editor "view" but really the error is in single Article view. From my point of view it's bad user experience for the user because the error is not in saving but in loading.

Rewrite using Browser.element

Any chance this can be rewritten using Browser.element instead of Browser.application?

I'm starting my journey into the realm of Elm and I was hoping to use this as a reference but I didn't have to dig deep in my background check to find some issues with a core concept used in this repo.

Browser.application is, as far as I can tell, a naive implementation that doesn't work out in the wild. Due to it taking direct control of the body tag it doesn't play along well with some extensions (1) (2). And the sole purpose if this repo is to demonstrate how you would go about building a "real" elm app.

[1] Discussion on discourse
[2] elm/browser#66

No styling

I had hoped for an example of how to integrate elm-ui or other CSS sheets but building this package the styles look nothing like the hosted one on Netlify. Am I overlooking something?

The app builds too long, in case of a lot of messages to handle in the Update function

Hello.
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?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.