Browser
This module helps you set up an Gren Program with functions like
sandbox and document.
Sandboxes
Create a “sandboxed” program that cannot communicate with the outside world.
This is great for learning the basics of The Elm Architecture, which Gren uses for structuring applications. You can see sandboxes in action in the following examples:
Elements
Create an HTML element managed by Gren. The resulting elements are easy to embed in larger JavaScript projects, and lots of companies that use Gren started with this approach! Try it out on something small. If it works, great, do more! If not, revert, no big deal.
Unlike a sandbox, an element can talk to the outside world in
a couple ways:
Cmd— you can “command” the Gren runtime to do stuff, like HTTP.Sub— you can “subscribe” to event sources, like clock ticks.flags— JavaScript can pass in data when starting the Gren programports— set up a client-server relationship with JavaScript
As you read the guide you will run into a bunch of examples of element
in this section. You can learn more about flags and ports in the interop
section.
Documents
Create an HTML document managed by Gren. This expands upon what element
can do in that view now gives you control over the <title> and <body>.
This data specifies the <title> and all of the nodes that should go in
the <body>. This means you can update the title as your application changes.
Maybe your "single-page app" navigates to a "different page", maybe a calendar
app shows an accurate date in the title, etc.
Note about CSS: This looks similar to an
<html>document, but this is not the place to manage CSS assets. If you want to work with CSS, there are a couple ways:
Packages like
rtfeldman/elm-cssgive all of the features of CSS without any CSS files. You can add all the styles you need in yourviewfunction, and there is no need to worry about class names matching.Compile your Gren code to JavaScript with
gren make --output=gren.jsand then make your own HTML file that loadsgren.jsand the CSS file you want. With this approach, it does not matter where the CSS comes from. Write it by hand. Generate it. Whatever you want to do.If you need to change
<link>tags dynamically, you can send messages out a port to do it in JavaScript.The bigger point here is that loading assets involves touching the
<head>as an implementation detail of browsers, but that does not mean it should be the responsibility of theviewfunction in Gren. So we do it differently!
Applications
Create an application that manages Url changes.
When the application starts, init gets the initial Url. You can show
different things depending on the Url!
When someone clicks a link, like <a href="/home">Home</a>, it always goes
through onUrlRequest. The resulting message goes to your update function,
giving you a chance to save scroll position or persist data before changing
the URL yourself with pushUrl or load. More info on this in
the UrlRequest docs!
When the URL changes, the new Url goes through onUrlChange. The
resulting message goes to update where you can decide what to show next.
Applications always use the Browser.Navigation module for precise
control over Url changes.
More Info: Here are some example usages of application programs:
These are quite advanced Gren programs, so be sure to go through the guide first to get a solid conceptual foundation before diving in! If you start reading a calculus book from page 314, it might seem confusing. Same here!
All links in an application create a UrlRequest. So
when you click <a href="/home">Home</a>, it does not just navigate! It
notifies onUrlRequest that the user wants to change the Url.
Internal vs External
Imagine we are browsing https://example.com. An Internal link would be
like:
settings#privacy/homehttps://example.com/home//example.com/home
All of these links exist under the https://example.com domain. An External
link would be like:
https://gren-lang.org/exampleshttps://other.example.com/homehttp://example.com/home
Anything that changes the domain. Notice that changing the protocol from
https to http is considered a different domain! (And vice versa!)
Purpose
Having a UrlRequest requires a case in your update like this:
import Browser exposing (..)
import Browser.Navigation as Nav
import Url
type Msg
= ClickedLink UrlRequest
update : Msg -> Model -> { model : Model, command : Cmd msg }
update msg model =
case msg of
ClickedLink urlRequest ->
case urlRequest of
Internal url ->
{ model = model
, command = Nav.pushUrl model.key (Url.toString url)
}
External url ->
{ model = model
, command = Nav.load url
}
This is useful because it gives you a chance to customize the behavior in each
case. Maybe on some Internal links you save the scroll position with
Browser.Dom.getViewport so you can restore it
later. Maybe on External links you persist parts of the Model on your
servers before leaving. Whatever you need to do!
Note: Knowing the scroll position is not enough to restore it! What if the
browser dimensions change? The scroll position will not correlate with
“what was on screen” anymore. So it may be better to remember
“what was on screen” and recreate the position based on that. For
example, in a Wikipedia article, remember the header that they were looking at
most recently. Browser.Dom.getElement is designed
for figuring that out!