Nested template overwrite ui:default with ui:insert - templates

Is there a rule on how to overwrite template definitions <ui:define> with <ui:insert>.
Template A:
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets">
template A content<br/>
<ui:insert name="content"/>
</ui:composition>
Template B:
<ui:composition template="/resources/templates/A.xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:define name="content">
template B content<br/>
<ui:insert name="content"/>
</ui:define>
</ui:composition>
Site 1:
<ui:composition template="/resources/templates/B.xhtml">
Site 1<br/>
<ui:define name="content">
site content<br/>
</ui:define>
</ui:composition>
Output:
Site 1
site content
Content for the <ui:define> is taken from Site 1, the content of the templates is not rendered.
Site 2:
<ui:composition template="/resources/templates/B.xhtml">
Site 2<br/>
</ui:composition>
Output:
Site 2
template B content
template A content
Content for the <ui:define> is taken from template B and template A, where strangely template B content is rendered before content of template A.
Is it possible to overwrite <ui:define> with a new <ui:insert> using same name?
Creating new names for the nested <ui:insert> is one possibility but its hard to keep track of the hierarchy and where insert's are used at all.

Unfortunately Facelets doesn't allow you to 'chain' insert/define.
In your first example (Site 1), there is single insert in Template A called "content". There are two definitions being considered; that of the direct template client (Template B) and that of the template client of Template B (Site 1). For this case, Facelets does not see that Template B has another insert. It just considers two competing definitions for "content", and the rule is that the top-most one wins, which is Site 1.
You did forgot to put the namespace on the template client though. The output you should be seeing is:
template A content
site content
Namely "template A content" is in the lowest template, outside the insert tag. It will be rendered directly. "site content" is in the top-most definition of "content".
In the second example (Site 2), there is no definition at all in the top-most template client. There is only one definition, and that's in Template B, so that one will be used. The output you should be seeing is:
template A content
template B content
You'll see "template A content" for the same reasons as in the first example, and "template B content" since it's the only definition. The nested second insert after that will be ignored.
Creating new names for the nested is one possibility but its hard to keep track of the hierarchy and where insert's are used at all.
It is indeed. Precisely for this reason I created a spec issue over a year ago for this at: https://github.com/eclipse-ee4j/faces-api/issues/1008
If this particular functionality is important to you please vote and/or leave a comment.

Related

Selecting among multiple Go subtemplates based on one value in a template [duplicate]

We can define template name via {{define "home"}}, and then load it in other (parent) template via {{template "home"}}.
How I can load template via variable value {{template .TemplateName}}. Or it's impossible?
Unfortunately you can't.
The syntax of the {{template}} action:
{{template "name"}}
The template with the specified name is executed with nil data.
{{template "name" pipeline}}
The template with the specified name is executed with dot set
to the value of the pipeline.
The name of the template to be included is a constant string, it is not a pipeline which could vary during execution based on parameters.
If the allowed syntax would be:
{{template pipeline}}
then you could use something like {{template .TemplName}} but since the syntax only allows a constant string, you can't.
Reasoning from Rob why dynamic template invocation is not allowed (source):
We want the template language to be statically analyzable so the context of a template's invocation is clear, checkable, and lockdownable. If an invocation point is totally dynamic, this can't be done. Similarly, if a template can belong to multiple sets, its context can differ between sets in a way that would require all sets to be analyzed simultaneously. Since both these constraints are easy to work around if you want to, at the cost of losing those static checks in a higher-level package, it seemed wise to control the situation in the base template implementation. A higher-level package, such as a hypothetical HTML-only wrapper, can guarantee no workarounds more easily if the constraints are clear.
Alternative #1: Execute Includable Template First
What you can do is execute the template you would want to include first, and insert the result where you want to include it. You can use special types not to escape the result of the inner template when inserting, for example html.HTML in case of HTML templates.
See this example:
func main() {
t := template.Must(template.New("t").Parse(t))
template.Must(t.New("t1").Parse(t1))
params := struct {
Name string
Value interface{}
}{"t1", nil}
b := bytes.Buffer{}
t.ExecuteTemplate(&b, params.Name, nil)
params.Value = template.HTML(b.String())
t.Execute(os.Stdout, params)
}
const t = `<html><body>
Now I will include template with name: {{.Name}}
{{.Value}}
</body>/html>`
const t1 = `I'm template <b>t1</b>.`
Output:
<html><body>
Now I will include template with name: t1
I'm template <b>t1</b>.
</body>/html>
Try it on the Go Playground.
The result of template t1 was inserted unescaped. If you leave out template.HTML:
params.Value = b.String()
t1 would be inserted escaped, like this:
<html><body>
Now I will include template with name: t1
I'm template <b>t1</b>.
</body>/html>
Alternative #2: Restructure Templates
You can restructure your templates not to be in situations where you would want to include a template with varying names.
Example: you might want to create pages where you have a page template something like this:
<html><body>
Title, headers etc.
{{template .Page}}
Footers
</body></html>
You can restructure it to be something like this:
header template:
<html><body>
Title, headers, etc.
footer template:
Footers
</body></html
And your page templates would include header and footer like this:
{{template "header" .}}
Page content comes here.
{{template "footer" .}}
Alternative #3: Use {{if}} action and predefined names
If you know the template names prior and it is not an exhausting list, you can use the {{if}} template action to include the desired template. Example:
{{if eq .Name "page1"}}
{{template "page1" .}}
{{else if eq .Name "page2"}}
{{template "page2" .}}
...
{{end}}
Alternative #4: Modifying the static template text
The idea here is that you could modify the static text of the outer template manually and insert the name of the inner template you want to include.
The downside of this method is that after inserting the name of the inner template, you have to re-parse the template, so I don't recommend this.

Default content in facelets template with empty ui:define

Is it valid to expect that facelets template will use a default content in page which use the template but uses an empty ui:define?
template:
<ui:insert name="header">
default content here <!-- is it valid default content? -->
</ui:insert>
page which implements the template:
<ui:define name="header">
<!--nothing in page defined here but expecting that default content from template will be substituted in place-->
</ui:define>
The default content is not shown though in this case (the comment is not there in the real page)
No it will not show the default content in this case, it works as you defined it (pun intended)...
There is a <ui:define name="header">...</ui:define>, so there is something defined. That it does not contain anything does not mean it will display the default content. Remove the define and the default content is shown. As is according to the specs afaik.

How to use a field of struct or variable value as template name?

We can define template name via {{define "home"}}, and then load it in other (parent) template via {{template "home"}}.
How I can load template via variable value {{template .TemplateName}}. Or it's impossible?
Unfortunately you can't.
The syntax of the {{template}} action:
{{template "name"}}
The template with the specified name is executed with nil data.
{{template "name" pipeline}}
The template with the specified name is executed with dot set
to the value of the pipeline.
The name of the template to be included is a constant string, it is not a pipeline which could vary during execution based on parameters.
If the allowed syntax would be:
{{template pipeline}}
then you could use something like {{template .TemplName}} but since the syntax only allows a constant string, you can't.
Reasoning from Rob why dynamic template invocation is not allowed (source):
We want the template language to be statically analyzable so the context of a template's invocation is clear, checkable, and lockdownable. If an invocation point is totally dynamic, this can't be done. Similarly, if a template can belong to multiple sets, its context can differ between sets in a way that would require all sets to be analyzed simultaneously. Since both these constraints are easy to work around if you want to, at the cost of losing those static checks in a higher-level package, it seemed wise to control the situation in the base template implementation. A higher-level package, such as a hypothetical HTML-only wrapper, can guarantee no workarounds more easily if the constraints are clear.
Alternative #1: Execute Includable Template First
What you can do is execute the template you would want to include first, and insert the result where you want to include it. You can use special types not to escape the result of the inner template when inserting, for example html.HTML in case of HTML templates.
See this example:
func main() {
t := template.Must(template.New("t").Parse(t))
template.Must(t.New("t1").Parse(t1))
params := struct {
Name string
Value interface{}
}{"t1", nil}
b := bytes.Buffer{}
t.ExecuteTemplate(&b, params.Name, nil)
params.Value = template.HTML(b.String())
t.Execute(os.Stdout, params)
}
const t = `<html><body>
Now I will include template with name: {{.Name}}
{{.Value}}
</body>/html>`
const t1 = `I'm template <b>t1</b>.`
Output:
<html><body>
Now I will include template with name: t1
I'm template <b>t1</b>.
</body>/html>
Try it on the Go Playground.
The result of template t1 was inserted unescaped. If you leave out template.HTML:
params.Value = b.String()
t1 would be inserted escaped, like this:
<html><body>
Now I will include template with name: t1
I'm template <b>t1</b>.
</body>/html>
Alternative #2: Restructure Templates
You can restructure your templates not to be in situations where you would want to include a template with varying names.
Example: you might want to create pages where you have a page template something like this:
<html><body>
Title, headers etc.
{{template .Page}}
Footers
</body></html>
You can restructure it to be something like this:
header template:
<html><body>
Title, headers, etc.
footer template:
Footers
</body></html
And your page templates would include header and footer like this:
{{template "header" .}}
Page content comes here.
{{template "footer" .}}
Alternative #3: Use {{if}} action and predefined names
If you know the template names prior and it is not an exhausting list, you can use the {{if}} template action to include the desired template. Example:
{{if eq .Name "page1"}}
{{template "page1" .}}
{{else if eq .Name "page2"}}
{{template "page2" .}}
...
{{end}}
Alternative #4: Modifying the static template text
The idea here is that you could modify the static text of the outer template manually and insert the name of the inner template you want to include.
The downside of this method is that after inserting the name of the inner template, you have to re-parse the template, so I don't recommend this.

How to inherit a template with no ID in Odoo?

I am trying to show the date a change was made in a task. To do this, I need to inherit the template of the widget "mail_thread". That template hasn't an id in its definition. This is it:
<?xml version="1.0" encoding="UTF-8"?>
<template>
<!--
mail.Widget template used to namespace the css -->
<t t-name="mail.Root">
<div class="oe_mail">
</div>
</t>
...
<span t-att-title="widget.date">
<t t-if="widget.timerelative" t-esc="widget.timerelative"/>
<t t-if="!widget.timerelative" t-raw="widget.display_date"/>
</span>
...
</template>
In my module, I need to replace the <span> tag in order to show the date.
So, how to inherit that template and replace the tag?
There are different inheritance mechanism for client side templates (web templates, defined inside a <templates> tag, "compiled" with javascript in the client when loading it) and server-side templates (usually views, must be included in the data list in the __openerp__.py file, 'compiled' when launching/upgrading the odoo server).
You extend web/widget templates templates using <t t-extend="template_name"> followed by one or more
<t t-jquery="jquery_selector" t-operation="operation"> which acts kinda like xpath, but client side and more 'powerful'.
You don't need ids, inheritance is based on the template name. (t-name directive)
Server-Side view inheritance
Client-Side template inheritance:
Template inheritance is used to alter existing templates in-place,
e.g. to add information to templates created by an other modules.
Template inheritance is performed via the t-extend directive which
takes the name of the template to alter as parameter.
The alteration is then performed with any number of t-jquery
sub-directives:
<t t-extend="base.template">
<t t-jquery="ul" t-operation="append">
<li>new element</li>
</t> </t>
The t-jquery directives takes a CSS selector. This selector is used on
the extended template to select context nodes to which the specified
t-operation is applied:
append
the node’s body is appended at the end of the context node (after the context node’s last child)
prepend
the node’s body is prepended to the context node (inserted before the context node’s first child)
before
the node’s body is inserted right before the context node
after
the node’s body is inserted right after the context node
inner
the node’s body replaces the context node’s children
replace
the node’s body is used to replace the context node itsel
No operation
if no t-operation is specified, the template body is interpreted as javascript code and executed with the context node as this
I also wanted to change the date show format in this xml file. So I copied the whole template for this default layout to my new module and only changed date in the span tag.
<?xml version="1.0" encoding="UTF-8"?>
<template>
<!-- default layout -->
<t t-name="mail.thread.message">
....
<span t-att-title="widget.date">
<!--<t t-if="widget.timerelative" t-esc="widget.timerelative"/>-->
<!--<t t-if="!widget.timerelative" t-raw="widget.display_date"/>-->
<t t-raw="widget.display_date"/>
</span>
....
</t>
</template>
need to declare this xml file in your __openerp__.py
It worked for me.

How can I add the filename of each used templates and includes in freemarker templates as HTML comments?

Is there a way to configure freemarker so that the Freemarker template engine automatically inserts the name of the current template as an HTML comment?
Example HTML output I would like to see:
<!-- template file: main.ftl -->
normal template code of the file main.ftl
<!-- template file: myinclude.ftl -->
This is the code from myinclude.ftl
I would like to use such functionality for debugging purposes only so that it is easier to find out which HTML fragments where rendered in which template.
Any hints?
You could write a TemplateLoader implementation that just delegates to another TemplateLoader (to the one that you are using when not in debug-mode), but captures the stream that it returns, and inserts the HTML comment after the <#ftl ...> directive. (But don't add line-break to the comment, or else it will offset the line number in error messages; use ${'\n'}<!-- ... -->${'\n'} instead.)