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 Gren Architecture. You can see sandboxes in action in the following examples:
Those are nice, but I very highly recommend reading this guide straight through to really learn how Gren works. Understanding the fundamentals actually pays off in this language!
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-css
give all of the features of CSS without any CSS files. You can add all the styles you need in yourview
function, and there is no need to worry about class names matching.Compile your Gren code to JavaScript with
gren make --output=gren.js
and then make your own HTML file that loadsgren.js
and 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 theview
function 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
/home
https://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/examples
https://other.example.com/home
http://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!