newtfire / digitProjectDesign-Hub

shared repo for DIGIT 400: Digital Project Design class at Penn State Erie, The Behrend College
Creative Commons Zero v1.0 Universal
12 stars 2 forks source link

XSLT 3 Help #28

Closed elmusfi closed 3 years ago

elmusfi commented 3 years ago

Hi all,

Here's what I have so far for the assignment:

 <xsl:template match="/">
    <html>
        <head>
            <title><xsl:value-of select="recipe/@name"/></title>
        </head>
        <body>
            <h1>
                <xsl:value-of select="recipe/title"/>
            </h1>
            <h2>
                Ingredients
            </h2>
            <table>
                <tr>
                    <th>Ingredient ID</th>
                    <th>Type</th>
                    <th>Quantity</th>
                    <th>Unit</th>
                    <th>Step(s) Used in Recipe</th>
                </tr>
                <xsl:apply-templates select="recipe/list[1]"/>
            </table>
            <h2>
                Equipment
            </h2>
            <ul>
                <xsl:apply-templates select="recipe/list[2]"/>
            </ul>
            <h2>
                Making and Cooking It
            </h2>
            <ol>
                <xsl:apply-templates select="recipe/list[3]"/>
            </ol>
        </body>
    </html>
    </xsl:template>

    <xsl:template match="recipe/list[1]/item">
        <tr>
            <td><xsl:value-of select="//recipe/list[1]/item/@type"/></td>
            <td><xsl:value-of select="//recipe/list[1]/item/@quant"/></td>
            <td><xsl:value-of select="//recipe/list[1]/item/@unit"/></td>
            <td><xsl:value-of select="//recipe/list[1]/item/@xml:id"/></td>
            <td></td>
        </tr>
    </xsl:template>

The problem is that when it comes time to make the table, every single attribute value of type, quant, etc. are put into each tr, like this:

<td>wet veggies spice dry veggies dairy veggies spice animalByproduct fruit spice spice
               spice dough</td>
            <td>4 1 .5 1 1 1 6 1 2 1 1 .5 .5 1</td>
            <td>Tbsp whole tsp can package package whole Tbsp whole whole tsp tsp tsp package</td>
            <td>canolaO bellpepp saffthreads chxpea chopspin fetachz scallion chopdill lgeggs lemon
               gratnutmeg blpepp salt phyllo</td>
            <td></td>

I'm not sure how to get it to stop repeating itself. I've tried splitting each td into the following:

<xsl:template match="recipe/list[1]/item">
        <tr>
            <td><xsl:apply-templates select="//recipe/list[1]/item/@type"/></td>
            <td>(insert other tds here I just don't want to type them out</td>
        </tr>
    </xsl:template>

and

<xsl:template match="//recipe/list[1]/item/@type">
        <xsl:value-of select="."/>
    </xsl:template>

but all this did was take away the spaces in the result. What am I doing wrong? Any help appreciated!

PiperBaron commented 3 years ago

@elmusfi

Hey, Elizabeth! I came to the issues page, because I was having a problem of my own, and I thought I'd try to help you out. I think I found your issue!

I think you're referencing the attributes on the Ingredients items multiple times, which is why you're getting the repeating output. In your code:

 <xsl:template match="recipe/list[1]/item">
        <tr>
            <td><xsl:value-of select="//recipe/list[1]/item/@type"/></td>
            <td><xsl:value-of select="//recipe/list[1]/item/@quant"/></td>
            <td><xsl:value-of select="//recipe/list[1]/item/@unit"/></td>
            <td><xsl:value-of select="//recipe/list[1]/item/@xml:id"/></td>
            <td></td>
        </tr>
    </xsl:template>

You're already matching on recipe/list/item, and then you're doing in again in each <td> element. If you take out those references, and instead just hone in on each attribute, you should get the right output. Like this:

<xsl:template match="recipe/list[1]/item">
        <tr>
            <td><xsl:value-of select="@xml:id"/></td>
            <td><xsl:value-of select="@type"/></td>
            <td><xsl:value-of select="@quant"/></td>
            <td><xsl:value-of select="@unit"/></td>
            <td></td>
        </tr>
    </xsl:template>

As for my own issue... I've been trying to get the Steps part of the table right, but I'm not quite there, and I don't really know why it's not working...

Right now, my template for the Ingredients list looks like this:

 <xsl:template match="list[@type='ingredients']/item">
        <tr>
            <td><xsl:apply-templates select="@xml:id"/></td>
            <td><xsl:apply-templates select="@type"/></td>
            <td><xsl:apply-templates select="@quant"/></td>
            <td><xsl:apply-templates select="@unit"/></td>
            <td><xsl:apply-templates select="//list[@type='Instructions']/item/ingred[substring-after(@ptr, '#')=//item/@xml:id]/parent::item/@type"/></td>
        </tr>
    </xsl:template>

The <td><xsl:apply-templates select="//list[@type='Instructions']/item/ingred[substring-after(@ptr, '#')=//item/@xml:id]/parent::item/@type"/></td> part spits out "234" in each cell under the "Step(s) used in recipe" header. I guess it's looking for every <ingred> element that has any Ingredient @xml:id in it. I've been trying to get it to reference the current node, but I keep getting errors and hitting brick walls. If anyone has any advice, it'd be greatly appreciated!

ebeshero commented 3 years ago

@PiperBaron Thanks for responding here to @elmusfi ! Your advice is exactly right. And the issue you're having for matching the steps is actually kind of related!

Here is the code in question:

<xsl:apply-templates select="//list[@type='Instructions']/item/ingred[substring-after(@ptr, '#')=//item/@xml:id]/parent::item/@type"/>

You've actually got a little too much going on here, in much the same way that @elmusfi was too. You're on the right track, though! You are starting by climbing down from the top of the tree to correctly locate the <ingred> element, and your predicate is stripping the substring-after the # so you get something that'll be equivalent to @xml:id on the <item> element your template rule is currently matching. So you can simplify this. This won't be quite correct, and I just realized I need to add something more to our assignment to help you here--but it's where I would start:

<xsl:apply-templates select="//list[@type='Instructions']/item/ingred[substring-after(@ptr, '#')= ./@xml:id"/>

That's simpler code, but it's also not going to work! It's saying, match the @xml:id on the template match node. Only the trouble is, that template match node holds all the ingredients items. So in XSLT we have to use a special function called current() to indicate we only want to match the current <item> being processed by the template rule. This should work:

<xsl:apply-templates select="//list[@type='Instructions']/item/ingred[substring-after(@ptr, '#')= current()/@xml:id"/>

We have to use the XSLT current() function to refer to the <item> element currently being processed, rather than the dot, because the dot gets the current XPath context, and within the XPath we’re evaluating, that’s one or another element in the <list type="ingredients"> element. The current() function, on the other hand, returns the current XSLT context, which is the specific <item> element that our template has matched.

I'm going to add a little more explanation to the assignment about that! I forgot I needed to explain current(), but it's the syntax you definitely need here.