Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • jacob phillips 130 posts 372 karma points
    Aug 08, 2014 @ 22:08
    jacob phillips
    0

    Best Practice, Select Default Text

    I often find myself checking for a value to be set, and if it's not, use some default, like this:

    <xsl:choose>
     <xsl:when test="$mediaNode/teaser != ''">
       <xsl:value-of select="$mediaNode/teaser" />                                                  
     </xsl:when>
     <xsl:otherwise>
    Listen
     </xsl:otherwise>
    </xsl:choose>
    

    It seems like this is not really a best practice. Should I be using something else in place of "Listen" like:

    <xsl:value-of select="Listen" />
    

    ..or something like that, and why?

  • Bjarne Fyrstenborg 1280 posts 3990 karma points MVP 7x c-trib
    Aug 08, 2014 @ 23:41
    Bjarne Fyrstenborg
    1

    Hi Jacob

    It's up to you how you output the default text and what you case is..

    You can do like in your example or you can wrap the text Listen inside a <xsl:text> element like this: <xsl:text>Listen</xsl:text> or use <xsl:value-of select="'Listen'" /> ... noticed you'll need the to apostrophes like this 'Listen' inside the two quotes. Or you can use HTML elements like <p>Listen</p>.

    If you use the value mulitple times, you would probably define a variable in the beginning:

    <xsl:variable name="defaultText" select="'Listen'" />
    <xsl:choose>
       <xsl:when test="$mediaNode/teaser != ''">
          <xsl:value-of select="$mediaNode/teaser" />
       </xsl:when>
       <xsl:otherwise><xsl:value-of select="$defaultText" /></xsl:otherwise>
    </xsl:choose>

    Furthermore you could also check that the teaser text property doesn't contains just whitespace with normalize-space($mediaNode/teaser) in the test attribute.

    But if you have a multilingual website, you would more likely use dictionary items, where DefaultText is the alias of your dictionary item:

    <xsl:variable name="defaultText" select="umbraco.library:GetDictionaryItem('DefaultText')" />
    <xsl:choose>
       <xsl:when test="normalize-space($mediaNode/teaser)">
          <xsl:value-of select="$mediaNode/teaser" />
       </xsl:when>
       <xsl:otherwise><xsl:value-of select="$defaultText" /></xsl:otherwise>
    </xsl:choose>

    /Bjarne

  • jacob phillips 130 posts 372 karma points
    Aug 08, 2014 @ 23:55
    jacob phillips
    0

    Thanks, so I guess using value-of is ok, and normalize-space seems like a good check.

    I thought I've seen certain contexts where formatting in an xslt carries through to the page.

    In other words, if I'm using tabs or spaces to format my xslt file, like this:

     <xsl:otherwise>
    Listen
     </xsl:otherwise>
    

    Notice that I have a cr/lf after opening otherwise and after Listen. Or sometimes in XSLT's I might have it tabbed in a larger context, like this:

                         <xsl:otherwise>
                                Listen
                         </xsl:otherwise>
    

    I thought sometimes this can get you into trouble. But I can't remember where. I'll just use value-of.

  • Bjarne Fyrstenborg 1280 posts 3990 karma points MVP 7x c-trib
    Aug 09, 2014 @ 00:10
    Bjarne Fyrstenborg
    0

    I don't think it would have something to say besides perhaps some formatting in the HTML source code..

    one thing you should be aware of in xslt is self closing tags in the some browsers, if e.g. an varible is empty and is wrapped inside some tags, which then would be empty and the browser might then mess up the markup, because the tags are closed other places in the code, which not was the intention.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 7x admin c-trib
    Aug 09, 2014 @ 00:16
    Chriztian Steinmeier
    1

    Hi Jacob,

    I'll add a couple of scenarios here, because I think it heavily depends on where you're doing this:

    If I can get around using the choose construct, I will. It's not always possible, but when it's very much like an if/else and you're just doing something in the "else" part if the first one is "empty", I can usually do with this pattern:

    <xsl:variable name="teaser" select="normalize-space($mediaNode/teaser)" />
    
    <xsl:value-of select="$teaser" />
    <xsl:if test="not($teaser)">Listen</xsl:if>
    

    A very common scenario in Umbraco is using a property (e.g. pageTitle) but falling back to @nodeName if the property is empty. Because they're both nodes and not strings, we can use this one-liner:

    <h1><xsl:value-of select="(@nodeName[not(normalize-space(../pageTitle))] | pageTitle)[1]" /></h1>
    

    Another scenario is adding an attribute (e.g., a class) to a node, where the class (or classes) depend on something else - first, let's take the "maybe add a class" example:

    <p>
        <xsl:if test="position() = 1">
            <xsl:attribute name="class">first</xsl:attribute>
        </xsl:if>
        ...
    </p>
    

    Second, here's a version where you have a default class, but may need to change it in some cases:

    <p class="text">
        <xsl:if test="self::Info">
            <xsl:attribute name="class">information</xsl:attribute>
        </xsl:if>
        ...
    </p>
    

    /Chriztian

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 7x admin c-trib
    Aug 09, 2014 @ 00:30
    Chriztian Steinmeier
    1

    About the whitespace question: It's all about the XML parsing step before the XSLT stylesheet is read by the processor.

    An XSLT file is an XML file, and there's a simple rule about whitespace in XML, that there's something called significant whitespace and insignificant whitespace.

    Take these simple examples:

    <p>
        Some text
    </p>
    

    The whitespace in the above (between <p> and Some, and between text and </p>) is parsed as insignificant - so effectively, it's read by the XML parser as equal to the following:

    <p>Some text</p>
    

    This is because the p element only contains text.

    Now look at this one:

    <p>
        Some <em>formatted</em> text
    </p>
    

    Because the p element has both text and element content, it's said to have "mixed content" and therefore, the same whitespace chunks as in the first example above are now considered significant whitespace and thus will be part of the document tree the parser hands off.

    So when you're doing this:

            <xsl:otherwise>
                   text here
            </xsl:otherwise>
    

    It's interpreted exactly the same as:

    <xsl:otherwise>text here</xsl:otherwise>
    

    Phew, lenghty explanation - but I hope it helps a little :-)

    /Chriztian

  • jacob phillips 130 posts 372 karma points
    Aug 09, 2014 @ 02:07
    jacob phillips
    0

    Thanks Bjarne, I think I remember losing several hours on the self-closing tags issue once a while back.

    Chriztian, this is really clever, I will use this:

    <xsl:variable name="teaser" select="normalize-space($mediaNode/teaser)" />
    
    <xsl:value-of select="$teaser" />
    <xsl:if test="not($teaser)">Listen</xsl:if>
    

    ...and the pageTitle vs. nodeName scenario I have ALL OVER THE PLACE in a choose. Geez this is awesome:

    <h1><xsl:value-of select="(@nodeName[not(normalize-space(../pageTitle))] | pageTitle)[1]" /></h1>
    

    I had to look at this for a second, but I think I understand. When pageTitle does exist we have:

    select="(@nodeName[0] | pageTitle)[1]"
    

    But how come @nodeName[0] is false? I was dealing with this earlier today when I used

    <xsl:variable name="twitterParamsArray" select="umbraco.library:Split(normalize-space($twitterParams), ',')" />
    

    I found that in order to access my expected two items, they were actually located like this:

    $twitterParamsArray//value[1]
    $twitterParamsArray//value[2]
    

    I tried like this first:

    $twitterParamsArray//value[0]
    $twitterParamsArray//value[1]
    

    and found nothing in [0]; I don't get that. Is this related? Does accessing an item with [0] always mean false?

    And thanks for the whitespace explanation. I think I understand now.

  • Chriztian Steinmeier 2798 posts 8788 karma points MVP 7x admin c-trib
    Aug 10, 2014 @ 01:34
    Chriztian Steinmeier
    0

    Hi Jacob,

    About [1] vs. [0]:

    When you write this:

    $twitterParamsArray//value[1]
    

    It's actually just shorthand for this:

    $twitterParamsArray//value[position() = 1]
    

    So when you ask for individual items in a nodeset, you ask for them by position (1-based) - not by offset (0-based), which I know is pretty much how all other languages does it :-)

    The pageTitle / @nodeName select works like this:

    When pageTitle exists and isn't empty, you'll get this:

    select="(@nodeName[false()] | pageTitle)[1]"
    

    which will be a nodeset with only the pageTitle node in it - selecting the first item in that set, of course gives us the pageTitle.

    When pageTitle is empty (or doesn't even exist), we get this:

    select="(@nodeName[true()] | pageTitle)[1]"
    

    which can be either a nodeset with both the @nodeName attribute and the pageTitle element, OR, a nodeset with only the @nodeName attribute. In either case, taking the first node from that set gives us @nodeName.

    /Chriztian

  • jacob phillips 130 posts 372 karma points
    Aug 10, 2014 @ 03:01
    jacob phillips
    0

    Thanks again Chriztian for breaking this down. I love to learn like this.

Please Sign in or register to post replies

Write your reply to:

Draft