Renders elm views from a directory of elm modules. This can and is meant to be plugged into Express as a template engine.
The 2 simple goals of this project are:
-
Allowing to reuse elm views when rendering the page the first time, avoiding to have a duplicate html view in the case of a multi-page app doing server-side rendering
-
Building views uniformly with the same technology (Elm!) and avoid having another template language in the stack.
Elm views format changed in v2.0! see corresponding section
Elm needs to be installed either globally or locally in the project.
> npm i -g elm
or
> npm i elm
> npm i elm-view-engine
You can use the view engine by itself to being able to render a directory of elm views, or you can plug it in to express as a view engine.
Typescript:
import { configure, Options } from "elm-view-engine";
configure(new Options("path/to/my/views", "path/to/elm/root")).then((engine) => {
const someContext = {
arrayOfUsers: [ "Paul", "Jack", "Barbara" ];
};
const viewContent = engine.getView("UsersView", someContext);
// Do something with the html
}));
Javascript:
var eve = require('elm-view-engine');
var options = {
viewsDirPath: 'path/to/my/views',
projectRoot: 'path/to/elm/root',
};
eve.configure(options).then((engine) => {
const someContext = {
arrayOfUsers: [ 'Paul', 'Jack', 'Barbara' ],
};
const viewContent = engine.getView('UsersView', someContext);
// Do something with the html
}));
Just call the configure function passing your express app in the options:
Typescript:
import { configure, Options } from "elm-view-engine";
import * as express from "express";
const app = express();
configure(new Options("path/to/my/views", "path/to/elm/root", app)).then(() => {
// app template engine is ready
app.use(...);
}));
Javascript:
var eve = require('elm-view-engine');
var express = require('express');
var app = express();
var options = {
viewsDirPath: 'path/to/my/views',
projectRoot: 'path/to/elm/root',
expressApp: app,
};
eve.configure(options).then(() => {
// app template engine is ready
app.use(...);
}));
Your elm views must expose 2 functions with the following signatures:
view : MyContext -> Html MyMsg
context : Json.Decode.Decoder MyContext
If they don't, views compilation will fail.
- MyContext is a record defining the structure of the context passed to the view. If you don't need any context for your view, you can use the following template:
context : Json.Decode.Decoder ()
context =
decode ()
view : () -> Html Never
view _ =
-- Code for your view
context
returns a decoder for the context that will deserialize the json context internally to the engine- Passing an invalid context when requesting a view will result in an error from the engine
A view with a simple context:
module Greeter exposing (view, context)
import Html exposing (Html, h1, div, text)
import Json.Encode
import Json.Decode exposing (decodeValue, string)
import Json.Decode.Pipeline exposing (decode, required)
-- Model
type alias SimpleContext =
{ simpleName : String }
context : Json.Decode.Decoder SimpleContext
context =
decode SimpleContext
|> required "simpleName" string
-- View
view : SimpleContext -> Html Never
view ctx =
div
[]
[ h1 []
[ text ("Hello " ++ ctx.simpleName ++ "!")
]
]
The views get compiled once when calling configure(). They are then cached and reused. This causes 2 inconveniences:
- The initial compilation can take some time and clog your server if you compile at startup.
- When a view is modified, it doesn't change right away: A new compilation is needed
To overcome this, it is possible to compile your views with the CLI by using the elm-view-engine
command. It will compile the views right away, and can be done before or after the server has started. The views can now be compiled beforehand or during server execution for development (See example for integration in npm scripts).
Running elm-view-engine -h
gives more info about the command.
Just git clone https://github.com/OzTK/elm-view-engine
and run the npm test
command from the root of the project. You can check the coverage by running npm run coverage:local
.
Because of the dynamic nature of the project, I had to generate elm code (the template for the module is src/MainTemplate.elm). I also needed to compile this generated elm code on the fly (when you call configure()) and render elm views into html strings. I relied on the following libraries for this purpose:
- elm-server-side-renderer: rendering elm views to strings
- node-elm-compiler: compiling elm code inside the module
- handlebars: templating/generating the elm code
Please feel free to open issues for any concerns. PRs welcome!