XSLT tips that will change your life!
This is an open discussion with 65 replies, filed under XSLT.
Search
Thanks!
This thread sure is useful.
Not directly live-changing and more symphony centric:
I needed a convenient way to handle a nested page navigation that lists entries instead of subpages.
So, for each section whose entries should appear as sub navigation item, create a DS that follows a strict naming convention. Let's say you have a page called "projects" then you'd might name it TOC Projects
. Choose the field that's representing your url parameter handle (e.g. a field called name
) for Parameter Output
and uncheck paginate results
since you want all entries to appear in your ds-toc-projects
parameter.
Now the xslt (apply it to your navigation DS):
<xsl:template match="page"> <xsl:variable name="subname"> <xsl:value-of select="concat('/data/params/ds-toc-', @handle)"/> </xsl:variable> <li> <a href="{$root}/{$lang}/{@handle}/" class="xhr"><xsl:value-of select="item"/></a> <xsl:if test="dyn:evaluate($subname)"> <xsl:apply-templates select="dyn:evaluate($subname)" mode="sub"> <xsl:with-param name="parent" select="@handle"/> </xsl:apply-templates> </xsl:if> </li> </xsl:template> <xsl:template match="*" mode="sub"> <xsl:param name="parent"/> <ul class="sub clear"> <xsl:for-each select="item"> <li> <a href="{$root}/{$lang}/{$parent}/{@handle}/" class="xhr"><xsl:value-of select="."/> </a> </li> </xsl:for-each> </ul> </xsl:template>
Since xpath-expressions can't be created at runtime you'll need a EXSLT extension called dyn:evaluate
. So your template header would look something like this:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dyn="http://exslt.org/dynamic" extension-element-prefixes="dyn" version="1.0" >
@iwyg, where does page <xsl:template match="page">
come from?
where does page
<xsl:template match="page">
come from?
From a navigation datasource probably. If I'm not mistaken, it's the PLH variation :)
@vladG, yep. PLH?
Yeah,PLH? What's that then?
Page LHandles extension. You have it installed. Recognized from your code.
oh, yes. indeed.
Nice, will it be possible to post another code where Page LHandles is not installed?
sure, obmit the $lang
variable and you're good. Instead of {$root}/{$lang}/{@handle}
it'd be {$root}/{@handle}
, though I'm not 100% sure if @handle
represents the page-handle on a regular Navigation DS
[edit] To complete confusion: {$lang}
is not a regular parameter. The real parameter is $url-language
. In this case, I created this variable to ensure theres a default language string in case $url-language
is not present. So, never use $lang
without declaring it before :)
Okie, to clarify it further. The code will be insert in 'project.xsl' and datasource for 'Project' page will be 'Navigation' and 'TOC Project'?
Cool. Got it work!
I normally tend to include page-navigation stylesheets in my master stylesheet.
Let's take a node set with similar nodes and parameter $param
which identifies a node (eg: $param = '4-3'
must select field-4-3
). How do we select the node?
<data> <field-1-1>...</field-1-1> <field-3-2>...</field-3-2> <field-4-3>...</field-4-3> ... </data>
Do we select it this way?
<xsl:variable name="result" select="data/*[starts-with(name(), concat('field-', $param))]"/>
Wrong! Dumping $result
(<xsl:copy-of select="$result"/>
) will return all field-*
nodes:
<field-1-1>...</field-1-1> <field-3-2>...</field-3-2> <field-4-3>...</field-4-3>
The correct way to select only the desired node is by first creating an additional variable to hold the name of the node (don't ask me why, I simply noticed this):
<xsl:variable name="node-name" select="concat('field-', $param)"/> <xsl:variable name="result" select="data/*[starts-with(name(), $node-name)]"/>
Dumping $result
(<xsl:copy-of select="$result"/>
) will return correct node:
<field-4-3>...</field-4-3>
Ok ... let's learn from my mistake.
I have a file called /utilities/imports.xsl
in which I'm importing all the stylesheets in utilities
folder:
<xsl:import href="master.xsl"/> <xsl:import href="XXX.xsl"/> <xsl:import href="YYY.xsl"/> <xsl:import href="ZZZ.xsl"/> ...
And in every /pages/XXX.xsl
or /widgets/XXX.xsl
stylesheet where I need to include something, I simply import ../utilities/imports.xsl
because I have auto-complete from my IDE and other sweets.
Benchmarks for page creation time as shown in Google Chrome Network tab (waiting for server to respond):
Import everywhere Import only in master.xsl 810ms ~ 1250ms 480ms ~ 530ms
Conclusion:
During dev it's OK to import everything everywhere (if it helps the Dev process), but before deploying to Production, make sure you're optimizing the imports.
You can escape an accolade in AVT using another similar accolade. This is useful if you're using handlebars or similar:
XSLT:
<div id="{{{{id}}}}">{{body}}</div>
Result:
<div id="{{id}}">{{body}}</div>
vladG is on fire :) nice performance tip there!
The performance tip is pretty neat, I didn't know about that one, will be interesting to apply to some existing projects and see the result.
Given Frontend Localisation
, FLang Detection GTLDs
and Page LHandles
, a developer has the necessary info to build localised URLs. Here's an example on how to retrieve a link to a Symphony page by it's ID:
Templates:
<!-- Store the languages in a variable --> <xsl:variable name="langs" select="/data/fl-languages"/> <!-- Compute current language code --> <xsl:variable name="cur-lang"> <xsl:value-of select="$langs/current-language/@language"/> <xsl:if test="$langs/current-language/@region != ''"> <xsl:text>-</xsl:text> <xsl:value-of select="$langs/current-language/@region"/> </xsl:if> </xsl:variable> <!-- This matches the parent page from Navigation DS --> <xsl:template match="page" mode="plink"> <xsl:param name="root" select="/data/params/root"/> <xsl:param name="lang" select="$cur-lang"/> <xsl:variable name="lang_code"> <xsl:choose> <!-- When main language, omit it from URL --> <xsl:when test="$langs/supported-languages/item[ @handle = $lang ]/@main = 'yes'"/> <!-- Other languages must be included in URL --> <xsl:otherwise> <xsl:value-of select="concat($lang, '/')"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="concat($root, '/', $lang_code, item[ @lang = $lang ]/@handle, '/')"/> </xsl:template> <!-- This matches any children pages from Navigation DS --> <xsl:template match="page/page" mode="plink"> <xsl:param name="root" select="/data/params/root"/> <xsl:param name="lang" select="$cur-lang"/> <xsl:apply-templates select="parent::page" mode="plink"> <xsl:with-param name="root" select="$root"/> <xsl:with-param name="lang" select="$lang"/> </xsl:apply-templates> <xsl:value-of select="concat(item[ @lang = $lang ]/@handle, '/')"/> </xsl:template>
Get the link to Symphony page with ID = 1.
<xsl:apply-templates select="/data/navigation//page[ @id = 1 ]" mode="plink"/>
@vladG
This is useful if you're using handlebars or similar
This is actually a great tip. Thanks. Unfortunately Handlebar has some constructs which won't compie with xml e.g. if else statements.
But nice tip :)
Create an account or sign in to comment.
Just to be safe, I prefer to use root links starting with
/
and apply the template if the condition is met: