// Template is a template-based text formatter. // // This uses the core `text/template` to process a given string template. // // Params // - template (string): A template string. // - template.Context (bool): If true, the context will be placed into the // template renderer as 'Cxt', and can be used as `{{.Cxt.Foo}}`. False // by default. // - ... (interface{}): Values passed into the template. // // Conventionally, template variables should start with an initial capital. // // Returns a formatted string. func Template(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) { format := cookoo.GetString("template", "", p) withCxt := cookoo.GetBool("template.Context", false, p) name := fmt.Sprintf("%x", md5.Sum([]byte(format))) //c.Logf("debug", "Template %s is '%s'\n", name, format) tpl, err := template.New(name).Parse(format) if err != nil { return "", err } data := p.AsMap() if withCxt { //c.Logf("debug", "Adding context.") data["Cxt"] = c.AsMap() } var out bytes.Buffer if err := tpl.Execute(&out, data); err != nil { return "", err } return out.String(), nil }
// RenderHTML renders an HTML template. // // This uses the `html/template` system built into Go to render data into a writer. // // Params: // - template (required): An html/templates.Template object. // - templateName (required): The name of the template to render. // - values: An interface{} with the values to be passed to the template. If // this is not specified, the contents of the Context are passed as a map[string]interface{}. // Note that datasources, in this model, are not accessible to the template. // - writer: The writer that data should be sent to. By default, this will create a new // Buffer and put it into the context. (If no Writer was passed in, the returned writer // is actually a bytes.Buffer.) To flush the contents directly to the client, you can // use `.Using('writer').From('http.ResponseWriter')`. // // Returns // - An io.Writer. The template's contents have already been written into the writer. // // Example: // // reg.Route("GET /html", "Test HTML"). // Does(cookoo.AddToContext, "_"). // Using("Title").WithDefault("Hello World"). // Using("Body").WithDefault("This is the body."). // Does(web.RenderHTML, "render"). // Using("template").From('cxt:templateCache'). // Using("templateName").WithDefault("index.html"). // Does(web.Flush, "_"). // Using("contentType").WithDefault("text/html"). // Using("content").From("cxt:render") // // In the example above, we do three things: // - Add Title and Body to the context. For the template rendered, it will see these as // {{.Title}} and {{.Body}}. // - Render the template located in a local file called "index.html". It is recommended that // a template.Template object be created at startup. This way, all of the templates can // be cached immediately and shared throughout processing. // - Flush the result out to the client. This gives you a chance to add any additional headers. func RenderHTML(cxt cookoo.Context, params *cookoo.Params) (interface{}, cookoo.Interrupt) { ok, missing := params.Requires("template", "templateName") if !ok { return nil, &cookoo.FatalError{"Missing params: " + strings.Join(missing, ", ")} } var buf bytes.Buffer out := params.Get("writer", &buf).(io.Writer) tplName := params.Get("templateName", nil).(string) tpl := params.Get("template", nil).(*template.Template) vals := params.Get("values", cxt.AsMap()) err := tpl.ExecuteTemplate(out, tplName, vals) if err != nil { log.Printf("Recoverable error parsing template: %s", err) // XXX: This outputs partially completed templates. Is this what we want? io.WriteString(out, "Template error. The error has been logged.") return out, &cookoo.RecoverableError{"Template failed to completely render."} } return out, nil }