using razor templates, how would i loop through child or descendants
I have a project need to be able to print the current page to a pdf or the pages below current 'chapter' or all pages below the 'book' but for the life of me, i cannot figure out how to mod the razor to build the pdf iterating down the tree.
can someone share a code snippet? Here is what i was trying, but it is not getting the children of the current node...
@foreach (var item in @Model.Children) { <!-- Main content starts within page sequence --> <fo:page-sequence master-reference="master"> <!-- Doucment header --> <fo:flow flow-name="header"> <fo:block> <fo:inline font-family="Arial" font-size="23pt" color="#3399ff"> @item.Name </fo:inline> </fo:block> </fo:flow> <!-- Doucment footer --> <fo:static-content flow-name="footer"> <fo:block font-size="8pt" color="#aaaaaa"> <fo:block text-align-last="justify"> @item.Name by @item.WriterName - @DateTime.Now <fo:leader leader-pattern="space"/> Page <fo:page-number/> of <fo:page-number-citation ref-id="last-page"/> </fo:block> </fo:block> </fo:static-content> <!-- Document Body --> <fo:flow flow-name="xsl-region-body"> <fo:block> @ParseRichText(FoHelper.Instance.GetRichTextNodes(@item.BodyText)) </fo:block> <!-- Having this before the closing tag of the body flow allows us to have a pager in the footer --> <fo:block id="last-page" keep-together.within-page="auto"></fo:block> </fo:flow> </fo:page-sequence> }
okay, so... this actually works... but makes a page for each subnode... how would i make it flow?
@foreach (var item in @Model.DescendantsOrSelf()) { <!-- Main content starts within page sequence --> <fo:page-sequence master-reference="master"> <!-- Doucment header --> <fo:flow flow-name="header"> <fo:block> <fo:inline font-family="Arial" font-size="23pt" color="#3399ff"> @item.Name </fo:inline> </fo:block> </fo:flow> <!-- Doucment footer --> <fo:static-content flow-name="footer"> <fo:block font-size="8pt" color="#aaaaaa"> <fo:block text-align-last="justify"> @item.Name by @item.WriterName - @DateTime.Now <fo:leader leader-pattern="space"/> Page <fo:page-number/> of <fo:page-number-citation ref-id="last-page"/> </fo:block> </fo:block> </fo:static-content> <!-- Document Body --> <fo:flow flow-name="xsl-region-body"> <fo:block> @ParseRichText(FoHelper.Instance.GetRichTextNodes(@item.BodyText)) </fo:block> <!-- Having this before the closing tag of the body flow allows us to have a pager in the footer --> <fo:block id="last-page" keep-together.within-page="auto"></fo:block> </fo:flow> </fo:page-sequence> }
i actually want each chapter an its pages in line... @Ravi was dead on with his interpretation... right now it is listing chapter 1, chapter2 then pages for chapter1 then pages for chapter2
basically, i am looking for 'list entire structure from specified node'
next - here is my full template [yes, please note it is a template] umbraco 4.7.1.1
<%@ Master Language="C#" MasterPageFile="~/masterpages/PDFMaster.master" AutoEventWireup="true" %> <asp:content ContentPlaceHolderId="PdfContentPlaceHolder" runat="server"> <umbraco:Macro runat="server" language="cshtml"> @inherits umbraco.MacroEngines.DynamicNodeContext @using FergusonMoriyama.Pdf @using System.Xml @using umbraco.IO @{ Response.ContentType = "text/xml"; Response.AppendHeader("X-Pdf-Render","true"); // -- Uncomment this to force the browser to download the PDF. //Response.AppendHeader("X-Pdf-Force-Download",@Model.Name); } <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:ibex="http://www.xmlpdf.com/2003/ibex/Format"> <!-- Sets standard PDF Metadata --> <ibex:properties title="@Model.Name" author="@Model.WriterName" subject="" keywords="metat,bacon,sheep" creator="PDF Creator for Umbraco" />
<!-- Uncomment below to add protection to the PDF - optionally specify a password --> <!-- <ibex:security deny-print="true" deny-extract="true" deny-modify="true" user-password="" owner-password=""/> -->
<!-- This defines a simple page layout with a heder and a footer --> <!-- See http://www.w3schools.com/xslfo/obj_layout-master-set.asp --> <fo:layout-master-set> <fo:simple-page-master master-name="master" page-width="210mm" page-height="297mm" margin-top="1cm" margin-bottom="1cm" margin-left="1cm" margin-right="1cm"> <fo:region-body margin-top="1.5cm" margin-bottom="1.5cm" column-count="1" column-gap="0.5cm"/> <fo:region-before region-name="header" extent="3cm"/> <fo:region-after region-name="footer" extent="1.5cm"/> </fo:simple-page-master> </fo:layout-master-set> <!-- Main content starts within page sequence --> <fo:page-sequence master-reference="master"> <!-- Doucment header --> <fo:flow flow-name="header"> <fo:block> <fo:inline font-family="Arial" font-size="23pt" color="#3399ff"> @Model.AncestorOrSelf(2).Name </fo:inline> </fo:block> </fo:flow> <!-- Doucment footer --> <fo:static-content flow-name="footer"> <fo:block font-size="8pt" color="#aaaaaa"> <fo:block text-align-last="justify"> @Model.Name by @Model.WriterName - @DateTime.Now <fo:leader leader-pattern="space"/> Page <fo:page-number/> of <fo:page-number-citation ref-id="last-page"/> </fo:block> </fo:block> </fo:static-content> <!-- Document Body --> <fo:flow flow-name="xsl-region-body"> @foreach (var chapter in @Model.AncestorOrSelf(1).First().Children.Where("NodeTypeAlias == \"SPD\"")) { <fo:block> <fo:inline font-family="Arial" font-size="23pt" color="#3399ff"> @chapter.Name </fo:inline> </fo:block> <fo:block> @ParseRichText(FoHelper.Instance.GetRichTextNodes(@chapter.BodyText)) </fo:block> @foreach (var page in chapter.Children()) { <fo:block> <fo:inline font-family="Arial" font-size="23pt" color="#3399ff"> @page.Name </fo:inline> </fo:block> <fo:block> @ParseRichText(FoHelper.Instance.GetRichTextNodes(@page.BodyText)) </fo:block> } } <!-- Having this before the closing tag of the body flow allows us to have a pager in the footer --> <fo:block id="last-page" keep-together.within-page="auto"></fo:block> </fo:flow> </fo:page-sequence> </fo:root> @helper ParseElement(XmlNode node) { <!-- @node.Name --> switch(node.Name) { case "p": <fo:block margin-bottom="0.5cm"> @ParseRichText(node.ChildNodes) </fo:block> break; case "strong": <fo:inline font-weight="bold"> @ParseRichText(node.ChildNodes) </fo:inline> break; case "em": <fo:inline font-style="italic"> @ParseRichText(node.ChildNodes) </fo:inline> break; case "a": <fo:basic-link color="blue" text-decoration="underline" external-destination="url('@node.Attributes["href"].Value')"> @ParseRichText(node.ChildNodes) </fo:basic-link> break; case "u": <fo:inline text-decoration="underline"> @ParseRichText(node.ChildNodes) </fo:inline> break; case "ol": <fo:list-block margin-bottom="0.5cm"> @ParseRichText(node.ChildNodes) </fo:list-block> break; case "ul": <fo:list-block margin-bottom="0.5cm"> @ParseRichText(node.ChildNodes) </fo:list-block> break; case "li": <fo:list-item> <fo:list-item-label> <fo:block>-</fo:block> </fo:list-item-label> <fo:list-item-body> <fo:block margin-left="0.5cm"> @ParseRichText(node.ChildNodes) </fo:block> </fo:list-item-body> </fo:list-item> break; case "img": var docRoot = IOHelper.MapPath("~/"); <fo:block> <fo:external-graphic src="@docRoot/@node.Attributes["src"].Value" content-width="9cm"/> </fo:block> @ParseRichText(node.ChildNodes) break; } }
@helper ParseRichText(XmlNodeList nodes) { foreach(XmlNode node in nodes) { switch(node.NodeType) { case XmlNodeType.Text: @node.Value @ParseRichText(node.ChildNodes); break; case XmlNodeType.Element: @ParseElement(node); break; default: @ParseRichText(node.ChildNodes); break; } } } </umbraco:Macro> </asp:Content>
That sounds like an XML issue, maybe something with your ParseRichText or ParseElement calls. Maybe the BodyText of one of the Page nodes has some bad XML
Hye bob, sorry was on a train with dodgy internet.. I suspect Tom is right.
because a you can nest foreach, like you can anywhere else.
when you are processeing the content I suspect it might lso be laid out in nodes and you can use the model to get the right values or you just need to escape something.
you effectively need to see the data in the "duff node" and see what you have.
it might be the value is arranged like this <nodeblah>hfbsdhfsdhf</nodeblah><nodebla2>vhsdbfhfsb</nodebla2> so you just need to handle it..
using razor templates, how would i loop through child or descendants
I have a project need to be able to print the current page to a pdf or the pages below current 'chapter' or all pages below the 'book' but for the life of me, i cannot figure out how to mod the razor to build the pdf iterating down the tree.
can someone share a code snippet? Here is what i was trying, but it is not getting the children of the current node...
okay, so... this actually works... but makes a page for each subnode... how would i make it flow?
Hi Bob, could you not just move:
So it is inside:
e.g.
@darren, when i move it there i get the following error:
Error loading MacroEngine script (file: )
hey bob, that is a pretty generic error - i can't tell much from that. can you post the whole template? or mail it to me?
so i eventually got this to work ALMOST exactly the way i wanted... HOWEVER, there is a caveat... as there always is...
i have a structure...
- book
- - chapter1
- - - page
- - - page
- - chapter2
What i want, is for a pdf version of the book -- in sequence down the tree... i thought this was working...
BUT it is listing chapter1, chapter2, then the pages from chapter1 - i am terrible with razor, any thoughts?
so its listing that you are returning:
chapter 1
chapter 2
then chapter 1 content and
chapter 2 content
If I understand correctly, you only want:
- chapter 1
- chapter 2
You probably need to change your Descendants() to Children then.
i actually want each chapter an its pages in line... @Ravi was dead on with his interpretation... right now it is listing chapter 1, chapter2 then pages for chapter1 then pages for chapter2
basically, i am looking for 'list entire structure from specified node'
okay, so i was actually able to make this a bit shorter...
@foreach (var item in @Model.AncestorOrSelf(1).Descendants("SPD").First().Descendants())
however, Descendants() still not workign the way i would think -- why does it go all nodes at level2, then listing all level3?
instead of level2 [subnodes] level2[subnodes]???
I think instead you should just use .Children to get the Chapters, then a loop inside that loop to get the pages?
-Tom
can you nest a foreach? it is thowing generic error for me?
Yep, you definitely can!
Where is the error occuring and what is it (anything helpful?)? Maybe paste your updated code?
-Tom
@Tom -- thanks a ton!
first, here is my silly error - not much help
Error loading MacroEngine script (file: )
next - here is my full template [yes, please note it is a template] umbraco 4.7.1.1
Hmm, weird error, it disappears if you remove the second loop?
This might be your problem, try chapter.Children instead of chapter.Children()
yeah, sorry... that was in there for a second test option -- me hacking...
took it out, same result...
if i take out the second @foreach i get output of the chapter level, yes - but nothing else.
Wonder if you can get a better error with ?umbdebugshowtrace and checking the trace?
got much better error results... taking about not needing a second @ for the nested for each... took that out... then i get
There are multiple root elements. Line 69, position 2
hmmmm any thoughts?
That sounds like an XML issue, maybe something with your ParseRichText or ParseElement calls. Maybe the BodyText of one of the Page nodes has some bad XML
Hye bob, sorry was on a train with dodgy internet.. I suspect Tom is right.
because a you can nest foreach, like you can anywhere else.
when you are processeing the content I suspect it might lso be laid out in nodes and you can use the model to get the right values or you just need to escape something.
you effectively need to see the data in the "duff node" and see what you have.
it might be the value is arranged like this <nodeblah>hfbsdhfsdhf</nodeblah><nodebla2>vhsdbfhfsb</nodebla2> so you just need to handle it..
ravi
is working on a reply...