Letter with a recipient from an address data source
A user creates a letter and selects the recipient in the Forms dialog from a central address data source, here a CSV file on a file share. The address appears as a fully formatted address block in the letter. If the recipient is not found, the user enters the details manually.
Result
- Forms dialog: search by name and city in the address data source, take over a hit or enter the recipient manually; plus subject and date.
- Address block in the letter: company or first and last name, street, postal code and city; the country only for recipients outside Switzerland.
- The address data source is interchangeable (SQL, HTTP, Excel) without changing the field or the template.
Building blocks
| Building block | Task in this example | Reference |
|---|---|---|
| Forms (content template) | Object Recipient with schema, result list and DataProvider; plus subject and date | Forms |
| CsvDataProvider | searches the CSV file and maps its columns to the schema | CsvDataProvider |
| Fields (content template) | Text field RecipientAddressBlock formats the address | Fields |
| Word content template | insert the field and the Forms fields via «Bind field» | Accessing primedocs fields |
Data flow
Step 1: Forms with the recipient object and the address data source
Activate the Forms document function on the content template:
<FormsConfiguration>
<Elements>
<Object Id="Recipient" Label="Empfänger" Required="true">
<Schema>
<Text Id="CompanyName" Label="Firma" />
<Text Id="FirstName" Label="Vorname" />
<Text Id="LastName" Label="Name" />
<Text Id="Street" Label="Strasse" />
<Text Id="PostalCode" Label="PLZ" />
<Text Id="City" Label="Ort" />
<Text Id="Country" Label="Land" />
</Schema>
<Summary>
<Field Id="CompanyName" />
<Field Id="FirstName" />
<Field Id="LastName" />
<Field Id="City" />
</Summary>
<DataProviders>
<CsvDataProvider DisplayName="Adressen">
<Options>
<FilePath>\\fileserver\primedocs\addresses.csv</FilePath>
<HasHeaders>true</HasHeaders>
<Delimiter>;</Delimiter>
</Options>
<SearchParameters>
<!-- Id = Spaltenname in der CSV-Datei -->
<Text Id="Name" Label="Name" />
<Text Id="Ort" Label="Ort" />
</SearchParameters>
<Mapping>
<!-- Source = Spaltenname in der CSV-Datei, Target = Id im Schema -->
<Map Source="Firma" Target="CompanyName" />
<Map Source="Vorname" Target="FirstName" />
<Map Source="Name" Target="LastName" />
<Map Source="Strasse" Target="Street" />
<Map Source="PLZ" Target="PostalCode" />
<Map Source="Ort" Target="City" />
<Map Source="Land" Target="Country" />
</Mapping>
</CsvDataProvider>
</DataProviders>
</Object>
<Text Id="Subject" Label="Betreff" Required="true" />
<Date Id="Date" Label="Datum" Format="d. MMMM yyyy" RelativeDate="Today" />
</Elements>
</FormsConfiguration>
Schemadefines the fields of each recipient, independent of the source.Summarydetermines the columns of the result list in the dialog.SearchParametersare the search fields; theirIdis the column name in the CSV file.Mappingconnects columns (Source) with schema ids (Target).Required="true"prevents a letter without a recipient.
The labels are set directly in Label so that the configuration works without preparation. For multilingual templates, use translate-Label and translate-Format with keys from the global translations instead.
Why an Object: It groups the address fields into one recipient to which the DataProvider is attached. If the source is replaced, only the DataProviders block changes; schema, field and template stay the same.
Step 2: Field for the address block
Activate the Fields document function on the content template:
<FieldsConfiguration>
<Fields>
<Text Name="RecipientAddressBlock">
<Code><![CDATA[
function main() {
const recipient = $("Forms.Recipient");
if (!recipient) {
return "";
}
const name = recipient.CompanyName
? recipient.CompanyName
: $.joinNonEmpty(" ", recipient.FirstName, recipient.LastName);
const country = recipient.Country && recipient.Country !== "CH" ? recipient.Country : "";
return $.joinNonEmpty("\n",
name,
recipient.Street,
$.joinNonEmpty(" ", recipient.PostalCode, recipient.City),
country);
}
]]></Code>
</Text>
</Fields>
</FieldsConfiguration>
Why a field: An Object cannot be inserted into the document directly. The field delivers the finished text; $.joinNonEmpty drops empty lines, for example for persons without a company or addresses without a country.
Step 3: Bind the fields in the Word template
- Open the content template in the editor.
- Place the cursor where the address block goes, click «Bind field» and select
RecipientAddressBlock. - Insert subject and date the same way:
Forms.SubjectandForms.Date.
The address block is inserted as a plain-text content control; the line breaks from \n produce the lines of the address block.
Testing
- Without the address data source: Test the template in the template editor. In the Forms dialog, the test-data button fills all fields, including the recipient object, with sample values; for text fields this is the field id. The address block therefore shows four lines, including the country, because the test value
Countryis notCH. - With the address data source: Provide a CSV file with the header
Firma;Vorname;Name;Strasse;PLZ;Ort;Land. The path is resolved on the server; the file must be reachable for the server. Search for a name in the dialog and take over the hit. - Country: Select a recipient with country
DE. The address block shows four lines; withCHit stays at three.
Variants
Other address data source: Replace the DataProviders block with a SqlDataProvider, HttpDataProvider or ExcelDataProvider. The Target ids in the mapping stay; field and template do not change.
Recipient from the business application: A third-party system passes the recipient via Connect in the Forms element as an Object with Key="Recipient". The Forms dialog shows it prefilled; the user can still change it. See Connect: Structure.
Allow hits from the source only: <DataProviders DisableManualCreate="true"> prevents manual entry.
Several recipients and mail merge: Use an ObjectCollection with SelectedObjectId instead of an Object. See Forms, section on SelectedObjectId.
Pitfalls
| Symptom | Cause | Solution |
|---|---|---|
| The recipient cannot be inserted via «Bind field» | An Object cannot be inserted directly | Text field as in step 2 |
| The search always returns all rows | The Id of a search parameter is not a column name, so the filter does not apply | Compare the header of the CSV file with the search parameter ids |
| The search returns no data or reports an error | The file is not reachable for the server | Check the UNC path and the access of the server process |
| The fields of a hit stay empty | Source in the mapping is not a column name, or Target is not a schema id | Compare the mapping with header and schema |
| Error in the field when no recipient is entered | The object is null | Check if (!recipient) as in step 2, or Required="true" |
| The country appears for Swiss addresses too | The source delivers «Schweiz» instead of CH | Adapt the comparison value in the field to the source |