Coder Social home page Coder Social logo

treetop's Introduction

Go Report Card Coverage Status Build Status

Treetop

GoDoc

A lightweight bridge from net/http to html/template

Build HTML endpoints using a hierarchy of nested templates, as supported by the Go standard library [html/template].

  • Lightweight by design
  • No 3rd-party dependencies
  • Optional protocol extension for fragment hot-swapping [1]

Template Hierarchy

Each template is paired with an individual data loading function. Pages are constructed by combining templates in different configurations and binding endpoints to your application router.

                         BaseFunc(…)
            ┌──────────── base.html ─────────────┐
            │ <html>                             │
            │ …                                  │
            │ {{ template "content" .Content }}  │
            │ …               ▲                  │
            │ </html>         │                  │
            │~~~~~~~~~~~~~~~~ │ ~~~~~~~~~~~~~~~~~│
                              │
                         ┌────┴────┐
        ContentAFunc(…)  │         │   ContentBFunc(…)
  ┌──── content_a.html ──┴──┐   ┌──┴── content_b.html ─────┐
  │                         │   │                          │
  │ <div id="content">A</…  │   │  <div id="content">B</…  │
  │~~~~~~~~~~~~~~~~~~~~~~~~~│   │~~~~~~~~~~~~~~~~~~~~~~~~~~│

Basic example of a page hierarchy showing content A and B sharing the same 'base' template

Note. Nested hierarchy is supported, see Golang docs for details [doc]

Library Overview

The code example shows how http.Handler endpoints are created from a hierarchy of templates and handler pairs.

The example binds the "/content_a" and "/content_b" routes shown in the diagram with additional "nav" and "sidebar" templates.

base := treetop.NewView("base.html", BaseHandler)
nav := base.NewSubView("nav", "nav.html", NavHandler)
_ = base.NewDefaultSubView("sidebar", "sidebar.html", SidebarHandler)
contentA := base.NewSubView("content", "content_a.html", ContentAHandler)
contentB := base.NewSubView("content", "content_b.html", ContentBHandler)

exec := treetop.FileExecutor{}
myMux.Handle("/content_a", exec.NewViewHandler(contentA, nav))
myMux.Handle("/content_b", exec.NewViewHandler(contentB, nav))

Note that exec := treetop.FileExecutor{} is responsible for compiling a template tree from your view definitions, plumbing togeather handler functions and exposing a http.Handler interface to your application router.

Example of embedded template blocks in "base.html",

...
<div class="layout">
	{{ block "nav" .Nav }}  <div id="nav">default nav</div> {{ end }}

	{{ template "sidebar" .SideBar }}

	{{ template "content" .Content }}
</div>
...

See text/template Nested Template Definitions for more info.

Note the loose coupling between content handlers in the outline below.

func BaseHandler(rsp treetop.Response, req *http.Request) interface{} {
    // data for `base.html` template
    return struct {
        ...
    }{
        ...
        Nav: rsp.HandleSubView("nav", req),
        SideBar: rsp.HandleSubView("sidebar", req),
        Content: rsp.HandleSubView("content", req),
    }
}

func ContentAHandler(rsp treetop.Response, req *http.Request) interface{} {
    // data for the `content_a.html` template
    return struct {
        ...
    }{
        ...
    }
}

No Third-Party Dependencies

The Treetop package wraps features of the Go standard library, mostly within "net/http" and "html/template".

Request Protocol Extension

Hot-swap sections of a page with minimal boilerplate.

Since Treetop views are self-contained, they can be rendered in isolation. Treetop handlers support rendering template fragments that can be applied to a loaded document.

The following is an illustration of the protocol.

> GET /content_a HTTP/1.1
  Accept: application/x.treetop-html-template+xml

< HTTP/1.1 200 OK
  Content-Type: application/x.treetop-html-template+xml
  Vary: Accept

  <template>
      <div id="content">...</div>
      <div id="nav">...</div>
  </template>

A Client Library handles the the client side of the protocol, passively updating the DOM based on the response.

Examples

Template Executor

An 'Executor' is responsible for loading and configuring templates. It constructs a HTTP Handler instance to manage the plumbing between loading data and executing templates for a request. You can implement your own template loader [docs needed], but the following are provided:

  • FileExecutor - load template files using os.Open
  • FileSytemExecutor - loads templates from a supplied http.FileSystem instance
  • StringExecutor - treat the view template property as an inline template string
  • KeyedStringExecutor - treat the view template property is key into a template map
  • DeveloperExecutor - force per request template parsing

View Handler Function

A view handler function loads data for a corresponding Go template. Just as nested templates are embedded in a parent, nested handler data is embedded in the data of it's parent.

Example of a handler loading data for a child template,

func ParentHandler(rsp treetop.Response, req *http.Request) interface{} {
    return struct {
        ...
        Child interface{}
    }{
        ...
        Child: rsp.HandleSubView("child", req),
    }
}

Data is subsequently passed down within the template like so,

<div id="parent">
    ...
    {{ template "child" .Child }}
</div>

Hijacking the Response

The treetop.Response type implements http.ResponseWriter. Any handler can halt the executor and take full responsibility for the response by using one of the 'Write' methods of that interface2. This is a common and useful practice making things like error messages and redirects possible.

Response Writer

The Treetop Go library provides utilities for writing ad-hoc template responses when needed. PartialWriter and FragmentWriter wrap a supplied http.ResponseWriter.

For example,

func myHandler(w http.ResponseWriter, req *http.Request) {
    // check for treetop request and construct a writer
    if tw, ok := treetop.NewFragmentWriter(w, req); ok {
        fmt.Fprintf(tw, `<h3 id="greeting">Hello, %s</h3>`, "Treetop")
    }
}

This is useful when you want to use the template protocol with a conventional handler function.

The difference between a "Partial" and "Fragment" writer has to with navigation history in the web browser [docs needed]

Client Library

The client library is used to send template requests from the browser using XHR. Response fragments are handled mechanically on the client. This avoids the need for callbacks or other IO boilerplate.

treetop.request("GET", "/some/path")

See Client Library for more information.

Footnotes

1. Go supports template inheritance through nested template definitions. 2. A http.ResponseWriter will flush headers when either WriteHeaders(..) or Write(..) methods are invoked.

treetop's People

Contributors

dtcaciuc avatar rur avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

treetop's Issues

Require that HTML fragments in the response body be wrapped in a single root <template> element

Currently if any one of the template fragments does not parse as valid HTML then it is not possible to distinguish from any other fragments that might be included in the response. The plan to address this is to adopt an approach similar to multipart requests where a separator (boundary) is declared in the content header and is used to the split the body allowing each part to be decoded independently.

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.