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, plus the country 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, so 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, while 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, and the 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, but 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 |