Can I define a snippet of <<LINQ markup>> to re-use?

Still wasn’t able to inline the images for this post, but I moved them to google photos, so at least now when you click you can actually see them in your browser, rather than having to download them as image.png :roll_eyes:

I’m generating documents using user-maintainable Word .dotx templates that get passed into Aspose LINQ ReportingEngine.BuildReport.
One such document is a table of either two or three columns, in which most of the cells would be this same template statement:

<<[ItemIndex]>>. Item#<<[ItemCode]>> <<if [IsAbcd]>> ABCD<</if>><<if [IsEfgh]>> EFGH<</if>><<if [IsWxyz]>> (WXYZ)<</if>>

The original code that we’re now replacing also used Aspose.Words to do this, but used the classic Mail Merge approach, and did this whole concatenation in code beforehand. That means if they wanted to change how any of that is formatted, they would need developer intervention and a new version of the application.
With the LINQ reporting engine it’s much easier to do IFs right in the template, so I’m revisiting this.
In order to support two or three columns, the template had two sections, and the one with the wrong number of columns gets removed by the code. I think the new version will have to do the same.
The dataset schema looked something like this:

public class ExampleDataRow {
	public string Concatenation1 { get; set; }
	public string Concatenation2 { get; set; }
	public string Concatenation3 { get; set; }
}

and the corresponding template in Word:
https://photos.app.goo.gl/wqkp7szomkkn6zxs5 old school mail merge template

Wanting to do this in the Word template instead, we can start by exposing more of the schema:

public class ExampleItem {
	public int ItemIndex { get; set; }
	public string ItemCode { get; set; }
	public bool IsAbcd { get; set; }
	public bool IsEfgh { get; set; }
	public bool IsWxyz { get; set; }
}
public class ExampleDataRow {
	public ExampleItem Item1 { get; set; }
	public ExampleItem Item2 { get; set; }
	public ExampleItem Item3 { get; set; }
}

The corresponding template in Word looks like this:
https://photos.app.goo.gl/7swejvk3wh7cqrkna LINQ reporting template version 1

Even having adjusted the format on the non-printing markup, it’s hard to make sense of and even to tell that it’s the same thing maintained in 5 different places.

We can go a bit farther by nesting collections, which surprisingly lets us just use indexers:

public class ExampleDataTable
{
	public List<List<ExampleItem>> ExampleDataRows { get; set; }
}
<<[[0].ItemIndex]>>. Item#<<[[0].ItemCode]>> <<if [[0].IsAbcd]>> ABCD<</if>><<if [[0].IsEfgh]>> EFGH<</if>><<if [[0].IsWxyz]>> (WXYZ)<</if>>

And further from that, we can use var:

<<var [i = [0]]>><<[i.ItemIndex]>>. Item#<<[i.ItemCode]>> <<if [i.IsAbcd]>> ABCD<</if>><<if [i.IsEfgh]>> EFGH<</if>><<if [i.IsWxyz]>> (WXYZ)<</if>>

Which results in a snippet that’s exactly the same for all of the cells we need it in…
but there’s nothing to do with that but still put it in 5 places, and so that still makes the template unreadable and unmaintainable. We haven’t really made any progress, other than making it a little less error-prone by not repeating Item1 Item2 Item3 several times:
https://photos.app.goo.gl/lwfucm6pkqnkmknp6 LINQ reporting template using nested lists and var

What I’d like to be able to do is define that snippet once and just reference in all of the places it applies.
I don’t know if there’s any existing way to do this, but I can’t seem to see one. (I even tried using Select to project a string, but not only was that really messy, and would require the template maintainer to understand C# code which is unreasonable, but moreover you can really only get the text of the <<if>> statements that way, not any formatting they might wish to apply.)

Some of the ways I’d think that could look:

  • Some kind of function syntax, which would be the easiest to understand and most flexible: <a href=https://photos.app.goo.gl/wt2zmffyk12w4r1e7>a function-like template

  • A verbatim template include kind of syntax (which I’m guessing might be easier to implement, just a couple early passes on the document to find template tags and substitute them): https://photos.app.goo.gl/c2ze7drcopz8mscn6 a verbatim early-include type of approach

Thoughts? Any existing way to do anything like this? Any roadmap for anything like this to exist in the future?

Thanks

@grant.bowering You can achieve what you need by using nested templates. From the nested document you can access

  • Data sources
  • Variables
  • A contextual object (see “Using Contextual Object Member Access” for more information)
  • Known external types (see “Setting up Known External Types” for more information)

For example in the main document the following can be defined:

<<var [s = “Hello!”]>><<doc [“C:\Temp\sub.docx”] -build>>

In the sub-template you can access variable defined in the main template, like the following:

Nested template says “<<[s]>>”

Using such approach you can define LINQ syntax in the sub-template and then reuse it by building the sub-template and inserting it dynamically.
Is this approach fulfill your requirements?

Oh interesting! That’s close to what I need, but not quite there…

These dotx files are in SharePoint, so maybe I could have a SharePoint URL in the doc tag, though there might likely be some problems with authentication in that context…

I suppose A byte array containing document data could be a member of the data structure, so the code could load it in the same way as it has already loaded the bytes of the main template, and the tags in the template would be like <<doc [subBytesProperty] -build>>

That would result in a Word template like this:
[using sub-document byte array property]https://photos.app.goo.gl/ujbapvti163c28tja

That’s a lot better…

The problem is it’s really not very user-friendly to have it in a separate file, especially if it’s only being used in one template. You can’t really see what you’re doing, and doing it as a property in the datasource, it’s not even clear what file you should open in order to adjust that.*

Is there any way to leverage this to do something in-line? Like, just treat the contents of the [""]s as a literal snippet of document? Something like

<<var [cellTemplate = "<<[i.ItemIndex]>>. Item#<<[i.ItemCode]>> etc."]>>

...

<<doc [cellTemplate] -build>>

Of course, if I try that, the result is

InvalidOperationException: Cannot load a document using the provided string value: '<<[i.ItemIndex]>>. Item#<<[i.ItemCode]>> etc.'. A string expression providing document data should return a file path, an URI, or Base64-encoded document bytes.

An additional concern is that even if it did work, I’d still just get the string result, not any formatting, because I set the variable inside [square brackets] so I’m just setting a string value and formatting is ignored… it would be nice to be able to support rich formatting a snippet like that inline instead, like

<<var [cellTemplate = “<<[i.ItemIndex]>>. Item#<<[i.ItemCode]>> etc.”]>>

and have that boldness be part of the result.
That would clearly require totally different syntax though, versus just supporting “if the string isn’t a file path, a URI, or Base64-encoded document bytes, then just DocumentBuilder.Write it into a new document” which sounds like it would be pretty easy to do if <<doc -build>> already exists. I’d like to be able to do that at the least, and being able to keep formatting could be a second separate feature that takes a little longer to implement.

Any other approaches you can suggest?

Thanks.

(*btw, is there anything like a comment tag? Just so one can give instructions or explanations to the template maintainer that just get stripped out when generation is run. If it was possible to have comments in the template, then at least one could hyperlink to the sub-template, like

<< /* to edit the cellTemplate, go to ms-word:ofe|u|<http://link/to/file/ > */ >>

@grant.bowering Thank you for additional information and useful thought. I have created a feature request WORDSNET-23357 to provide syntax to reuse part of the template in several places. We will keep you informed and let you know once this feature is available.
Regarding comments in your template. You can use a variable definition in this case:
<<var [readme = “to edit the cellTemplate, go to ms-word:ofe|u|http://link/to/file/”]>>

2 Likes