Accessing struct variable in slice of many structs in html template golang - templates

I'm attempting to send a slice containing many structs to an html template.
I have a 'post' struct
type Post struct {
threadID int
subject string
name string
text string
date_posted string
}
I create a slice of type Post ( posts := []Post{} )
this slice is then populated using rows from my database and then executed on my template.
defer latest_threads.Close()
for latest_threads.Next(){
var threadID int
var subject string
var name string
var text string
var date_posted string
latest_threads.Scan(&threadID, &subject, &name, &text, &date_posted)
post := Post{
threadID,
subject,
name,
text,
date_posted,
}
posts = append(posts, post)
}
t, error := template.ParseFiles("thread.html")
if error != nil{
log.Fatal(error)
}
t.Execute(w, posts)
}
The program compiles / runs okay but when viewing the html output from the template
{{.}}
{{range .}}
<div>{{.threadID}}</div>
<h3>{{.subject}}</h3>
<h3>{{.name}}</h3>
<div>{{.date_posted}}</div>
<div><p>{{.text}}</p></div>
<br /><br />
{{end}}
{{.}} outputs just fine however upon reaching the first {{.threadID}} in {{range .}} the html stops.
<!DOCTYPE html>
<html>
<head>
<title> Test </title>
</head>
<body>
//here is where {{.}} appears just fine, removed for formatting/space saving
<div>

It's not really intuitive, but templates (and encoding packages like JSON, for that matter) can't access unexported data members, so you have to export them somehow:
Option 1
// directly export fields
type Post struct {
ThreadID int
Subject, Name, Text, DatePosted string
}
Option 2
// expose fields via accessors:
type Post struct {
threadID int
subject, name, text, date_posted string
}
func (p *Post) ThreadID() int { return p.threadID }
func (p *Post) Subject() string { return p.subject }
func (p *Post) Name() string { return p.name }
func (p *Post) Text() string { return p.text }
func (p *Post) DatePosted() string { return p.date_posted }
Update template
(this part is mandatory regardless of which option you chose from above)
{{.}}
{{range .}}
<div>{{.ThreadID}}</div>
<h3>{{.Subject}}</h3>
<h3>{{.Name}}</h3>
<div>{{.DatePosted}}</div>
<div><p>{{.Text}}</p></div>
<br /><br />
{{end}}
And this should work.

Related

Regex for replace a string [duplicate]

I have a string with html markup in it (differMarkup) and would like to run that string through a tokenizer that would identify specific tags (like ins, dels, movs) and replace them with the span tag and add data attributes to it as well.
So the input looks like this:
`<h1>No Changes Here</h1>
<p>This has no changes</p>
<p id="1"><del>Delete </del>the first word</p>
<p id="2"><ins>insertion </ins>Insert a word at the start</p>`
And intended output would be this:
`<h1>No Changes Here</h1>
<p>This has no changes</p>
<p id="1"><span class="del" data-cid=1>Delete</span>the first word</p>
<p id="2"><span class="ins" data-cid=2>insertion</span>Insert a word at the start</p>
`
This is what I currently have. For some reason I'm not able to append the html tags to the finalMarkup var when setting it to span.
const (
htmlTagStart = 60 // Unicode `<`
htmlTagEnd = 62 // Unicode `>`
differMarkup = `<h1>No Changes Here</h1>
<p>This has no changes</p>
<p id="1"><del>Delete </del>the first word</p>
<p id="2"><ins>insertion </ins>Insert a word at the start</p>` // Differ Markup Output
)
func readDifferOutput(differMarkup string) string {
finalMarkup := ""
tokenizer := html.NewTokenizer(strings.NewReader(differMarkup))
token := tokenizer.Token()
loopDomTest:
for {
tt := tokenizer.Next()
switch {
case tt == html.ErrorToken:
break loopDomTest // End of the document, done
case tt == html.StartTagToken, tt == html.SelfClosingTagToken:
token = tokenizer.Token()
tag := token.Data
if tag == "del" {
tokenType := tokenizer.Next()
if tokenType == html.TextToken {
tag = "span"
finalMarkup += tag
}
//And add data attributes
}
case tt == html.TextToken:
if token.Data == "span" {
continue
}
TxtContent := strings.TrimSpace(html.UnescapeString(string(tokenizer.Text())))
finalMarkup += TxtContent
if len(TxtContent) > 0 {
fmt.Printf("%s\n", TxtContent)
}
}
}
fmt.Println("tokenizer text: ", finalMarkup)
return finalMarkup
}
```golang
Basically you want to replace some nodes in your HTML text. For such tasks it's much easier to work with DOMs (Document Object Model) than to handle the tokens yourself.
The package you're using golang.org/x/net/html also supports modeling HTML documents using the html.Node type. To acquire the DOM of an HTML document, use the html.Parse() function.
So what you should do is traverse the DOM, and replace (modify) the nodes you want to. Once you're done with the modifications, you can get back the HTML text by rendering the DOM, for that use html.Render().
This is how it can be done:
const src = `<h1>No Changes Here</h1>
<p>This has no changes</p>
<p id="1"><del>Delete </del>the first word</p>
<p id="2"><ins>insertion </ins>Insert a word at the start</p>`
func main() {
root, err := html.Parse(strings.NewReader(src))
if err != nil {
panic(err)
}
replace(root)
if err = html.Render(os.Stdout, root); err != nil {
panic(err)
}
}
func replace(n *html.Node) {
if n.Type == html.ElementNode {
if n.Data == "del" || n.Data == "ins" {
n.Attr = []html.Attribute{{Key: "class", Val: n.Data}}
n.Data = "span"
}
}
for child := n.FirstChild; child != nil; child = child.NextSibling {
replace(child)
}
}
This will output:
<html><head></head><body><h1>No Changes Here</h1>
<p>This has no changes</p>
<p id="1"><span class="del">Delete </span>the first word</p>
<p id="2"><span class="ins">insertion </span>Insert a word at the start</p></body></html>
This is almost what you want, the "extra" thing is that the html package added wrapper <html> and <body> elements, along with an empty <head>.
If you want to get rid of those, you may just render the content of the <body> element and not the entire DOM:
// To navigate to the <body> node:
body := root.FirstChild. // This is <html>
FirstChild. // this is <head>
NextSibling // this is <body>
// Render everyting in <body>
for child := body.FirstChild; child != nil; child = child.NextSibling {
if err = html.Render(os.Stdout, child); err != nil {
panic(err)
}
}
This will output:
<h1>No Changes Here</h1>
<p>This has no changes</p>
<p id="1"><span class="del">Delete </span>the first word</p>
<p id="2"><span class="ins">insertion </span>Insert a word at the start</p>
And we're done. Try the examples on the Go Playground.
If you want the result as a string (instead of printed to the standard output), you may use bytes.Buffer as the output for rendering, and call its Buffer.String() method in the end:
// Render everyting in <body>
buf := &bytes.Buffer{}
for child := body.FirstChild; child != nil; child = child.NextSibling {
if err = html.Render(buf, child); err != nil {
panic(err)
}
}
fmt.Println(buf.String())
This outputs the same. Try it on the Go Playground.

Go template can't call method on field

I have a wrapper around net/mail.Address that provides some marshalling logic. I'm trying to use it in a template, but I keep getting can't evaluate field String in type EmailAddress. The template docs say:
The name of a niladic method of the data, preceded by a period,
such as
.Method
The result is the value of invoking the method with dot as the
receiver, dot.Method().
and
Method invocations may be chained and combined with fields and keys
to any depth:
.Field1.Key1.Method1.Field2.Key2.Method2
So with that in mind I've written this:
package main
import (
"bytes"
"fmt"
"html/template"
"net/mail"
"os"
)
type EmailAddress struct{ mail.Address }
type emailFormatter struct {
From EmailAddress
To EmailAddress
}
var tmpl = template.Must(template.New("Sample Text").Parse("From: {{.From.String}}\r" + `
To: {{.To.String}}` + "\r" + `
Content-Type: text/html` + "\r" + `
Subject: Sample Text` + "\r\n\r" + `
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sample Text</title>
<meta charset="utf-8"/>
</head>
<body>
Sample Text
</body>
</html>
`));
func main() {
to := EmailAddress{
mail.Address{
Address: "em#i.l",
Name: "",
},
}
from := EmailAddress{
mail.Address{
Address: "no-reply#test.quest",
Name: "",
},
}
fmt.Println(to.String()) //outputs (as expected) "<em#i.l>"
fmt.Println(from.String()) //outputs (as expected) "<no-reply#test.quest>"
f := emailFormatter{
To: to,
From: from,
}
var buff bytes.Buffer
if err := tmpl.Execute(&buff, f); err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Println(buff.String())
}
I've separately verified that calling EmailAddress.String is totally legal, so I can't figure out why the only output of this is:
Error: template: Sample Text:1:13: executing "Sample Text" at <.From.String>: can't evaluate field String in type main.EmailAddress
EDIT
At a commenter's suggestion I changed the calls from .From.String and .To.String to .From.Address.String and .To.Address.String, because
"String isn't defined on EmailAddress, it's defined on EmailAddress.Address"
but the result is the same:
Error: template: Sample Text:1:13: executing "Sample Text" at <.From.Address.String>: can't evaluate field String in type mail.Address
Since String is defined with a pointer receiver you need to pass an "addressable" instance of mail.Address to the template to be able to execute that method.
You can do this by passing in a pointer to f.
if err := tmpl.Execute(&buff, &f); err != nil {
panic(err)
}
Or you can do it by passing in pointer to EmailAddress.
type emailFormatter struct {
From *EmailAddress
To *EmailAddress
}
// ...
f := emailFormatter{
To: &to,
From: &from,
}
// ...
if err := tmpl.Execute(&buff, f); err != nil {
panic(err)
}
Or by passing in a pointer to mail.Address.
type EmailAddress struct{ *mail.Address }
// ...
to := EmailAddress{
&mail.Address{
Address: "em#i.l",
Name: "",
},
}
from := EmailAddress{
&mail.Address{
Address: "no-reply#test.quest",
Name: "",
},
}
f := emailFormatter{
To: to,
From: from,
}
// ...
if err := tmpl.Execute(&buff, f); err != nil {
panic(err)
}
Note that the reason you don't need to do that in the Go code is because there the compiler does it for you.
For example:
fmt.Println(to.String())
becomes:
fmt.Println((&to).String())
A method call x.m() is valid if the method set of (the type of) x
contains m and the argument list can be assigned to the parameter list
of m. If x is addressable and &x's method set contains m, x.m() is
shorthand for (&x).m()

Run a function on data in a go html/template

I want to add hyphens (-) to a string in a go template when someone tries to save it. I'm using some modified code from the go wiki tutorial here: https://golang.org/doc/articles/wiki/
Code:
<h1>Editing {{.Title}}</h1>
<form action="/save/{{.Title}}" method="POST">
<div><input name="title" type="text" placeholder="title"></div>
<div><textarea name="body" rows="20" cols="80">{{printf "%s" .Body}}</textarea></div>
<div><input type="submit" value="Save"></div>
</form>
The line with
<form action="/save/{{.Title}}" method="POST">
is the relevant line. I need to transform .Title which might be something like "the quick brown fox" to "the-quick-brown-fox".
As you can see in the code above, you can add a function like println, but I'm not sure how I would do this for my case.
You can pass a template.FuncMap to the template and then you can do something like:
{{ .Title | title }}
https://play.golang.org/p/KWy_KRttD_
func Sluggify(s string) string {
return strings.ToLower(s) //for example
}
func main() {
funcMap := template.FuncMap {
"title": Sluggify,
}
tpl := template.Must(template.New("main").Funcs(funcMap).Parse(`{{define "T"}}Hello {{.Title | title }} Content: {{.Content}}{{end}}`))
tplVars := map[string]string {
"Title": "Hello world",
"Content": "Hi there",
}
tpl.ExecuteTemplate(os.Stdout, "T", tplVars)
}
All of your *Page structs are created by the loadPage function. So it would seem to be easiest to just create your hyphenated title then and store it in your page struct:
type Page struct {
Title string
HyphenTitle string
Body []byte
}
func loadPage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body, HyphenTitle: hyphenate(title)}, nil
}
func hyphenate (s string) string {
return strings.Replace(s," ","-",-1)
}
Then just use {{.HyphenTitle}} where you want it.

CheckBoxList multiple selections: how to model bind back and get all selections?

This code:
Html.CheckBoxList(ViewData.TemplateInfo.HtmlFieldPrefix, myList)
Produces this mark-up:
<ul><li><input name="Header.h_dist_cd" type="checkbox" value="BD" />
<span>BD - Dist BD Name</span></li>
<li><input name="Header.h_dist_cd" type="checkbox" value="SS" />
<span>SS - Dist SS Name</span></li>
<li><input name="Header.h_dist_cd" type="checkbox" value="DS" />
<span>DS - Dist DS Name</span></li>
<li><input name="Header.h_dist_cd" type="checkbox" value="SW" />
<span>SW - Dist SW Name </span></li>
</ul>
You can check multiple selections. The return string parameter Header.h_dist_cd only contains the first value selected. What do I need to do to get the other checked values?
The post method parameter looks like this:
public ActionResult Edit(Header header)
I'm assuming that Html.CheckBoxList is your extension and that's markup that you generated.
Based on what you're showing, two things to check:
The model binder is going to look for an object named Header with string property h_dist_cd to bind to. Your action method looks like Header is the root view model and not a child object of your model.
I don't know how you are handling the case where the checkboxes are cleared. The normal trick is to render a hidden field with the same name.
Also a nit, but you want to use 'label for="..."' so they can click the text to check/uncheck and for accessibility.
I've found that using extensions for this problem is error prone. You might want to consider a child view model instead. It fits in better with the EditorFor template system of MVC2.
Here's an example from our system...
In the view model, embed a reusable child model...
[AtLeastOneRequired(ErrorMessage = "(required)")]
public MultiSelectModel Cofamilies { get; set; }
You can initialize it with a standard list of SelectListItem...
MyViewModel(...)
{
List<SelectListItem> initialSelections = ...from controller or domain layer...;
Cofamilies = new MultiSelectModel(initialSelections);
...
The MultiSelectModel child model. Note the setter override on Value...
public class MultiSelectModel : ICountable
{
public MultiSelectModel(IEnumerable<SelectListItem> items)
{
Items = new List<SelectListItem>(items);
_value = new List<string>(Items.Count);
}
public int Count { get { return Items.Count(x => x.Selected); } }
public List<SelectListItem> Items { get; private set; }
private void _Select()
{
for (int i = 0; i < Items.Count; i++)
Items[i].Selected = Value[i] != "false";
}
public List<SelectListItem> SelectedItems
{
get { return Items.Where(x => x.Selected).ToList(); }
}
private void _SetSelectedValues(IEnumerable<string> values)
{
foreach (var item in Items)
{
var tmp = item;
item.Selected = values.Any(x => x == tmp.Value);
}
}
public List<string> SelectedValues
{
get { return SelectedItems.Select(x => x.Value).ToList(); }
set { _SetSelectedValues(value); }
}
public List<string> Value
{
get { return _value; }
set { _value = value; _Select(); }
}
private List<string> _value;
}
Now you can place your editor template in Views/Shared/MultiSelectModel.ascx...
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<WebUI.Cofamilies.Models.Shared.MultiSelectModel>" %>
<div class="set">
<%=Html.LabelFor(model => model)%>
<ul>
<% for (int i = 0; i < Model.Items.Count; i++)
{
var item = Model.Items[i];
string name = ViewData.ModelMetadata.PropertyName + ".Value[" + i + "]";
string id = ViewData.ModelMetadata.PropertyName + "_Value[" + i + "]";
string selected = item.Selected ? "checked=\"checked\"" : "";
%>
<li>
<input type="checkbox" name="<%= name %>" id="<%= id %>" <%= selected %> value="true" />
<label for="<%= id %>"><%= item.Text %></label>
<input type="hidden" name="<%= name %>" value="false" />
</li>
<% } %>
</ul>
<%= Html.ValidationMessageFor(model => model) %>
Two advantages to this approach:
You don't have to treat the list of items separate from the selection value. You can put attributes on the single property (e.g., AtLeastOneRequired is a custom attribute in our system)
you separate model and view (editor template). We have a horizontal and a vertical layout of checkboxes for example. You could also render "multiple selection" as two listboxes with back and forth buttons, multi-select list box, etc.
I think what you need is how gather selected values from CheckBoxList that user selected and here is my solution for that:
1- Download Jquery.json.js and add it to your view as reference:
2- I've added a ".cssMyClass" to all checkboxlist items so I grab the values by their css class:
<script type="text/javascript" >
$(document).ready(function () {
$("#btnSubmit").click(sendValues);
});
function populateValues()
{
var data = new Array();
$('.myCssClas').each(function () {
if ($(this).attr('checked')) {
var x = $(this).attr("value");
data.push(x);
}
});
return data;
}
function sendValues() {
var data = populateValues();
$.ajax({
type: 'POST',
url: '#Url.Content("~/Home/Save")',
data: $.json.encode(data),
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function () { alert("1"); }
});
}
</script>
3- As you can see I've added all selected values to an Array and I've passed it to "Save" action of "Home" controller by ajax 4- in Controller you can receive the values by adding an array as argument:
[HttpPost]
public ActionResult Save(int[] val)
{
I've searched too much but apparently this is the only solution. Please let me know if you find a better solution for it.
when you have multiple items with the same name you will get their values separated with coma

Grails g:select setting selectedIndex

How do you set the selectedIndex on a <g:select> tag with a value from the list? I have a page that allows you to add a record. the page then goes to a view containing a g:select and i want the g:select to default to that item i just inserted into the database.
I tried passing the new object in the flash but i can't figure out how to get it's index in the list being used to generate the g:select data.
Supposing you store a Book object in flash.book at the controller level, your second page could look like this :
<html>
<head>
<g:javascript library="prototype" />
<g:javascript>
function showLast(selectedId) {
if (selectedId) {
$$('#books option[value=' + selectedId + "]")[0].selected = true;
} else {
$('books').selectedIndex = 0;
}
};
Event.observe(window, 'load', init, false);
function init() {
showLast(${flash?.book?.id});
}
</g:javascript>
</head>
<body>
<g:select id="books" name="id"
from="${Book.list()}"
value="title"
optionValue="title"
optionKey="id"
/>
</body>
</html>