golang template.Execute and struct embedding - templates

I have a little website project written go where you can store links, and I ran into a problem :
The website has many different pages which show different information so you need to pass template.Execute a different kind of a struct. But every page also needs info like username and tags, which are displayed in sidebar. I tried to make something like this instead of just making completely new struct type for each page.
http://play.golang.org/p/VNfD6i8p_N
type Page interface {
Name() string
}
type GeneralPage struct {
PageName string
}
func (s GeneralPage) Name() string {
return s.PageName
}
type PageRoot struct {
Page
Tags []string
IsLoggedIn bool
Username string
}
type ListPage struct {
Page
Links []Link
IsTagPage bool
Tag string
}
type GalleryPage struct {
Page
Image Link
Next int
Previous int
}
But I get an error when I execute the template: "fp.tmpl" at <.Links>: can't evaluate field Links in type main.Page
The part of the template where the error occurs:
{{with .Page}}
{{range .Links}}
<tr>
<td>{{if .IsImage}}<img src="{{.Url}}" />{{end}}</td>
<td>{{.Name}}</td>
<td>{{.Url}}</td>
<td>{{.TagsString}}</td>
</tr>
{{end}}
{{end}}
And {{.Name}} doesn't work. (It's the function embedded from GeneralPage)

You're embeding the Page interface, but what you need is GeneralPage.
Maybe you can use a map[string]interface{} to store your data (and then check if not-nil in your template), it easier.
But you can share the main layout and just change the detail (like a master page).
Look at http://golang.org/pkg/text/template/#example_Template_share

Related

Template object field enforcement

Go provides great build in HTML templating functionality, however for me it is important that I can ensure certain fields will always be available to templates in my application. A good example of this is a title field, which needs to be shown on every HTML page.
Given the following pages:
Home
Register
Contact
I would likely create the following objects for the templating system:
HomePage struct
RegisterPage struct
ContactPage struct
Is there a recommended way to ensure that the structs for each page have certain fields available to them?
Ideally I would implement this through Polymorphism, but that isn't officially supported in Go. The alternative, embedding doesn't appear to have any enforcement, i.e all of the child structs can embed a parent struct but don't have to.
Let me know if I haven't expressed my question clearly enough.
Executing a template does not enforce anything to the parameters, Template.Execute() accepts a value of type interface{}.
You are the one creating the HomePage, RegisterPage and ContactPage structs. What stops you from embedding a BasePage struct with the required fields? Are you worried you will forget about it? You will notice it at the first testing, I wouldn't worry about that:
type BasePage struct {
Title string
Other string
// other required fields...
}
type HomePage struct {
BasePage
// other home page fields...
}
type RegisterPage struct {
BasePage
// other register page fields...
}
If you want from code to check if page structs embed the BasePage, I recommend another way: interfaces.
type HasBasePage interface {
GetBasePage() BasePage
}
Example HomePage that implements it:
type HomePage struct {
BasePage
// other home page fields...
}
func (h *HomePage) GetBasePage() BasePage {
return h.BasePage
}
Now obviously only pages that have a GetBasePage() method can be passed as a value of HasBasePage:
var page HasBasePage = &HomePage{} // Valid, HomePage implements HasBasePage
If you don't want to use interfaces, you can use the reflect package to check if a value is a struct value and if it embeds another interface. Embedded structs appear and can be accessed like ordinary fields e.g. with Value.FieldByName(), with the type name being the field name.
Example code using reflect to check if a value embeds BasePage:
page := &HomePage{BasePage: BasePage{Title: "Home page"}}
v := reflect.ValueOf(page)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
fmt.Println("Error: not struct!")
return
}
bptype := reflect.TypeOf(BasePage{})
bp := v.FieldByName(bptype.Name()) // "BasePage"
if !bp.IsValid() || bp.Type() != bptype {
fmt.Println("Error: struct does not embed BasePage!")
return
}
fmt.Printf("%+v", bp)
Output (try it on the Go Playground):
{Title:Home page Other:}

Revel: pass template var to url

I want to understand how url helper works.
For example, in my template I have:
my super url
and in controller:
func (c Pages) IndexPages() revel.Result {
...
}
I need url like
http://localhost:9000/pages?page=1
I don't want to write:
func (c Pages) IndexPages(page int) revel.Result {
because I want to check if the controller contains the param page.
How to add my template var to c.Params.Query with the url helper?
Revel url helper code in template.go
// Return a url capable of invoking a given controller method:
// "Application.ShowApp 123" => "/app/123"
func ReverseUrl(args ...interface{}) (template.URL, error) {
We need to update the manual with information about this template function, but you can see in the link and code above how it's intended to be used. You pass it a controller and action with parameters and it creates a template.URL object with the matching route.
It seems you're not interested in how the url helper works (though that's what you asked). You want to know how to pass the page variable to the template? In your controller you need to pass page via c.RenderArgs["page"] = page. Then you can reference it in your template: my super url Revel Template Doc.
As you noted, you can manually get the value for page with page := c.Params.Query.Get("page") if you don't want to use the parameter binding feature.

How do you pass multiple objects to go template?

Most examples I can find describe very simple/basic things, such as showing attributes of a person object like this:
The name is {{.Name}}. The age is {{.Age}}.
What happens if you have a more complicated web page, for example, multiple different objects and lists of objects, i.e. How do you do something like this:
{{p.Name}} is aged {{p.Age}}.
Outstanding invoices {{invoices.Count}}
<table>
<tr><td>{{invoices[0].number}}</td></tr>
.... etc...
You can declare and pass in an anonymous struct like this:
templ.Execute(file, struct {
Age int
Name string
}{42, "Dolphin"})
and access the variables like:
{{.Age}}, {{.Name}}
While this still requires you to make a struct, it is among the most concise ways to do it. You'll have to decide if it is too ugly for you ;)
You can put your more complex data into struct, and pass it just like you did Name and Age. For example,
type vars struct {
P User
Invoices []Invoice
}
type User struct {
Name string
Age int
}
type Invoice {
Number int
Description string
}
If you pass an instance of vars into the template execution, you can reference sub-structures by using dots and array indexes, just like in regular go code.
{{.P.Name}}, {{.P.Age}}, {{.Invoices[0].Number}}
It depends on what your data is.
I want to categorise this.
Primary data that the template is meant for. In your example that would be Invoice/Invoicelist. If you have to pass more than one of these you have to reconsider your template design.
Secondary data such as logged in user information or any common information that you find yourself passing into several templates.
Since these information are common. I usually make them into functions. Since these functions cannot have input params. You might want to create them as closures (within another function). Assign these function to funMap and add it to the template after parsing.
func MakeFuncMap(u *user) map[string]interface{} {
return map[string]interface{}{
"User": func() *user {return u}, //Can be accessed by "User." within your template
}
}
t, err := template.New("tmpl").Funcs(MakeFuncMap(nil)).Parse("template") //You will need to append a dummy funcMap as you will not have access to User at the time of template parsing
//You will have to clone the template to make it thread safe to append funcMap.
tClone, _ := t.Clone()
tClone.Funcs(MakeFuncMap(u)).Execute(w, invoicelist)
Now you can execute the template with only the invoicelist as data.
Within your template you should be able to access user information using "User." and invoice list by "."
You should be able to define the funcMap once for all the common data. So that you will be able reuse it.
To loop through a invoicelist you can look into range
{{range .}} //if you are passing invoicelist then the . means invoicelist
//in here . means each of the invoice
<label>{{User.Name}}, {{User.Age}}</label>
<label>{{.Id}}</label>
{{end}}
EDIT: Included fix for issue pointed out by Ripounet

How can I dynamically render HTML using Meteor Spacebars templates?

So let's say I'm storing <div>{{name}}</div> and <div>{{age}}</div> in my database.
Then I want to take the first HTML string and render it in a template - {{> template1}} which just renders the first string with the {{name}} handlebar in it. Then I want to give that newly generated template/html data, so that it can fill in the handlebar with the actual name from the database, so that we would get <div>John</div>. I've tried doing
<template name="firstTemplate">
{{#with dataGetter}}
{{> template1}}
{{/with}}
</template>
Where template1 is defined as
<template name="template1">
{{{templateInfo}}}
</template>
And templateInfo is the helper that returns the aforementioned html string with the handlebar in it from the database.
dataGetter is just this (just an example, I'm working with differently named collections)
Template.firstTemplate.dataGetter = function() {
return Users.findOne({_id: Session.get("userID")});
}
I can't get the {{name}} to populate. I've tried it a couple of different ways, but it seems like Meteor doesn't understand that the handlebars in the string need to be evaluated with the data. I'm on 0.7.0 so no Blaze, I can't upgrade at the moment due to the other packages I'm using, they just don't have 0.8+ version support as of yet. Any ideas on how I can get this to work are much appreciated.
In 1.0 none of the methods described above work. I got this to work with the function below defined in the client code. The key was to pass the options { isTemplate: true} to the compile function.
var compileTemplate = function(name, html_text) {
try {
var compiled = SpacebarsCompiler.compile(html_text, { isTemplate:true });
var renderer = eval(compiled);
console.log('redered:',renderer);
//Template[name] = new Template(name,renderer);
UI.Template.__define__(name, renderer);
} catch (err){
console.log('Error compiling template:' + html_text);
console.log(err.message);
}
};
The you can call with something like this on the client:
compileTemplate('faceplate', '<span>Hello!!!!!!{{_id}}</span>');
This will render with a UI dynamic in your html
{{> Template.dynamic template='faceplate'}}
You can actually compile strings to templates yourself using the spacebars compiler.. You just have to use meteor add spacebars-compiler to add it to your project.
In projects using 0.8.x
var compiled = Spacebars.compile("<div>{{name}}</div> and <div>{{age}}</div>");
var rendered = eval(compiled);
Template["dynamicTemplate"] = UI.Component.extend({
kind: "dynamicTemplate",
render: rendered
});
In projects using 0.9.x
var compiled = SpacebarsCompiler.compile("<div>{{name}}</div> and <div>{{age}}</div>");
var renderer = eval(compiled);
Template["dynamicTemplate"] = Template.__create__("Template.dynamicTemplate", rendered);
Following #user3354036's answer :
var compileTemplate = function(name, html_text) {
try {
var compiled = SpacebarsCompiler.compile(html_text, { isTemplate:true }),
renderer = eval(compiled);
console.log('redered:',renderer);
//Template[name] = new Template(name,renderer);
UI.Template.__define__(name, renderer);
} catch (err) {
console.log('Error compiling template:' + html_text);
console.log(err.message);
}
};
1) Add this in your HTML
{{> Template.dynamic template=template}}
2) Call the compileTemplate method.
compileTemplate('faceplate', '<span>Hello!!!!!!{{_id}}</span>');
Session.set('templateName','faceplate');
Save the template name in a Session variable. The importance of this is explained in the next point.
3) Write a helper function to return the template name. I have used Session variable to do so. This is important if you are adding the dynamic content on a click event or if the parent template has already been rendered. Otherwise you will never see the dynamic template getting rendered.
'template' : function() {
return Session.get('templateName');
}
4) Write this is the rendered method of the parent template. This is to reset the Session variable.
Session.set('templateName','');
This worked for me. Hope it helps someone.
If you need to dynamically compile complex templates, I would suggest Kelly's answer.
Otherwise, you have two options:
Create every template variation, then dynamically choose the right template:
eg, create your templates
<template name="displayName">{{name}}</template>
<template name="displayAge">{{age}}</template>
And then include them dynamically with
{{> Template.dynamic template=templateName}}
Where templateName is a helper that returns "age" or "name"
If your templates are simple, just perform the substitution yourself. You can use Spacebars.SafeString to return HTML.
function simpleTemplate(template, values){
return template.replace(/{{\w+}}/g, function(sub) {
var p = sub.substr(2,sub.length-4);
if(values[p] != null) { return _.escape(values[p]); }
else { return ""; }
})
}
Template.template1.helpers({
templateInfo: function(){
// In this context this/self refers to the "user" data
var templateText = getTemplateString();
return Spacebars.SafeString(
simpleTemplate(templateText, this)
);
}
Luckily, the solution to this entire problem and any other problems like it has been provided to the Meteor API in the form of the Blaze package, which is the core Meteor package that makes reactive templates possible. If you take a look at the linked documentation, the Blaze package provides a long list of functions that allow for a wide range of solutions for programmatically creating, rendering, and removing both reactive and non-reactive content.
In order to solve the above described problem, you would need to do the following things:
First, anticipate the different HTML chunks that would need to be dynamically rendered for the application. In this case, these chunks would be <div>{{name}}</div> and <div>{{age}}</div>, but they could really be anything that is valid HTML (although it is not yet part of the public API, in the future developers will have more options for defining this content in a more dynamic way, as mentioned here in the documentation). You would put these into small template definitions like so:
<template name="nameDiv">
<div>{{name}}</div>
</template>
and
<template name="ageDiv">
<div>{{age}}</div>
</template>
Second, the definition for the firstTemplate template would need to be altered to contain an HTML node that can be referenced programmatically, like so:
<template name="firstTemplate">
<div></div>
</template>
You would then need to have logic defined for your firstTemplate template that takes advantage of some of the functions provided by the Blaze package, namely Blaze.With, Blaze.render, and Blaze.remove (although you could alter the following logic and take advantage of the Blaze.renderWithData function instead; it is all based on your personal preference for how you want to define your logic - I only provide one possible solution below for the sake of explanation).
Template.firstTemplate.onRendered(function() {
var dataContext = Template.currentData();
var unrenderedView = Blaze.With(dataContext, function() {
// Define some logic to determine if name/age template should be rendered
// Return either Template.nameDiv or Template.ageDiv
});
var currentTemplate = Template.instance();
var renderedView = Blaze.render(unrenderedView, currentTemplate.firstNode);
currentTemplate.renderedView = renderedView;
});
Template.firstTemplate.onDestroyed(function() {
var renderedView = Template.instance().renderedView;
Blaze.remove(renderedView);
});
So what we are doing here in the onRendered function for your firstTemplate template is dynamically determining which of the pieces of data that we want to render onto the page (either name or age in your case) and using the Blaze.With() function to create an unrendered view of that template using the data context of the firstTemplate template. Then, we select the firstTemplate template element node that we want the dynamically generated content to be contained in and pass both objects into the Meteor.render() function, which renders the unrendered view onto the page with the specified element node as the parent node of the rendered content.
If you read the details for the Blaze.render() function, you will see that this rendered content will remain reactive until the rendered view is removed using the Blaze.remove() function, or the specified parent node is removed from the DOM. In my example above, I am taking the reference to the rendered view that I received from the call to Blaze.render() and saving it directly on the template object. I do this so that when the template itself is destroyed, I can manually remove the rendered view in the onDestroyed() callback function and be assured that it is truly destroyed.
A very simple way is to include in the onRendered event a call to the global Blaze object.
Blaze.renderWithData(Template[template_name], data ,document.getElementById(template_id))

Change Template of Uploaded File in Media Library in Sitecore

I've done some research into this but not sure I understand all the pieces that need to go into the following problem.
My client needs a special template to be used instead of the auto detected media templates in the Media Library if they upload to a certain Folder. The template has special fields. The template can also house different types of files (PDFs, vendor specific formats, executables).
For development purposes we are currently uploading the file and then doing a template switch afterwards but what really needs to happen is that file be uploaded to that template type in the first place. I was wondering if there was a way to hook into the upload process to make sure the special template is used when underneath a certain path in the Media Library? If so, where should I start?
We recently had to do something similar. Along the same line as techphoria414, I'd tap into the upload save pipeline. Then, to make it a bit more generic and reusable, use the power of Sitecore's configuration parsing to hook everything up to your handler. Here's what we ended up with.
Main class with required "Process" method:
public class ChangeTemplate
{
public string Name { get; set; }
public string Path { get; set; }
public List<ChangedMediaTemplate> Templates { get; set; }
public ChangeTemplate()
{
Templates = new List<ChangedMediaTemplate>();
}
public void Process(UploadArgs args)
{
var db = Sitecore.Context.ContentDatabase;
var uploadPath = db.GetItem(args.Folder).Paths.ContentPath;
if (!uploadPath.StartsWith(Path))
{
// Not uploading to designated folder
return;
}
foreach (var item in args.UploadedItems)
{
// Need to change template for this item?
var changedTemplate = Templates.Where(t => t.Old.Equals(item.Template.FullName)).FirstOrDefault();
if (changedTemplate != null)
{
var newTemplate = db.Templates[changedTemplate.New];
try
{
item.ChangeTemplate(newTemplate);
}
catch (Exception e)
{
Log.Error("Unable to change {0} template on upload of {1} to {2}.".FormatWith(Name, item.Name, uploadPath), e, this);
}
}
}
}
}
Minor supporting class:
public class ChangedMediaTemplate
{
public string Old { get; set; }
public string New { get; set; }
}
And then the config:
<processors>
<uiUpload>
<processor patch:after="*[#type='Sitecore.Pipelines.Upload.Save, Sitecore.Kernel']" mode="on" type="Foo.Project.SitecoreX.Pipelines.Upload.ChangeTemplate, Foo.Project.Classes">
<Name>Product Images</Name>
<Path>/sitecore/media library/Images/Foo/products</Path>
<Templates hint="list">
<Template type="Foo.Project.SitecoreX.Pipelines.Upload.ChangedMediaTemplate, Foo.Project.Classes">
<Old>System/Media/Unversioned/Image</Old>
<New>User Defined/Foo/Product/Image/Unversioned/Product Image</New>
</Template>
<Template type="Foo.Project.SitecoreX.Pipelines.Upload.ChangedMediaTemplate, Foo.Project.Classes">
<Old>System/Media/Unversioned/Jpeg</Old>
<New>User Defined/Foo/Product/Image/Unversioned/Product Jpeg</New>
</Template>
<Template type="Foo.Project.SitecoreX.Pipelines.Upload.ChangedMediaTemplate, Foo.Project.Classes">
<Old>System/Media/Versioned/Image</Old>
<New>User Defined/Foo/Product/Image/Versioned/Product Image</New>
</Template>
<Template type="Foo.Project.SitecoreX.Pipelines.Upload.ChangedMediaTemplate, Foo.Project.Classes">
<Old>System/Media/Versioned/Jpeg</Old>
<New>User Defined/Foo/Product/Image/Versioned/Product Jpeg</New>
</Template>
</Templates>
</processor>
</uiUpload>
</processors>
Modifying or adding new template rules becomes as simple as editing the config as needed.
Hope this helps!
Unfortunately, to my knowledge, the Sitecore.Resources.Media.MediaCreator(handels mediaitem creation) can not be overridden. So the only (easy) way is to change the templates for the entire media library.
Otherwise I think you need to make your own changes to the sheerUI - but i wouldn't recommend it. Anyhow.. the mediaitems Sitecore creates, are defined in web.config under
<mediaLibrary>
<mediaTypes>
<mediaType name="Any" extensions="*">...</mediaType>
<mediaType name="Windows Bitmap image" extensions="bmp">...</mediaType>
....
</mediaTypes>
</mediaLibrary>
There is a version/unversion template for each mediaitem you can change.
If you want to look into SheerUI I recommend you start here:
http://learnsitecore.cmsuniverse.net/en/Developers/Articles/2009/10/My-First-Sitecore-XAML-Application.aspx
I would use an item:saving handler. If the item is a Media Item, and within a configured folder, then you can change its template. As always with item:saving, insert some checks very early in the method and exit quickly if you determine the item is not of concern.
I want to add something to ambrauer's answer above. It is a good solution but the code should be tweaked before use in production.
The following line:
var uploadPath = db.GetItem(args.Folder).Paths.ContentPath;
should be changed to:
if (args.Folder == null) return;
var uploadFolderItem = db.GetItem(args.Folder);
if (uploadFolderItem == null) return;
var uploadPath = uploadFolderItem.Paths.ContentPath;
The reason is that without the null check on args.Folder, the package upload tool in the Sitecore installation wizard breaks.
This is not critical to developers like us but some administrators rely on this tool as part of their workflow if they do not have full access to the site.