@data-content : The Dynamic Content Attribute
The data-content attribute provides a mechanism for dynamically generating and inserting content into elements during the data binding phase.
This is supported by the data-content-type and data-content-action attributes.
On this page
- Usage
- Supported Elements
- Binding Values
- Notes
- Examples
- 1. Simple Text Replacement in Label
- 2. Dynamic Formatted Text
- 3. Conditional Content Display
- 4. Dynamic HTML Content in Div
- 5. Inline Template Content
- 6. Appending Dynamic Content
- 7. Prepending Header Content
- 8. Complex Multi-Line HTML Content
- 9. Dynamic Table Content Generation
- 10. Conditional Section Inclusion
- 11. List Generation with Dynamic Styling
- 12. Placeholder with Fallback Content
- 13. Markdown Content Injection
- 14. Frame with Dynamic Document Content
- 15. Localized Content Injection
- 16. Rich Text with Embedded Bindings
- 17. Template with Alternate Content Definition
- See Also
Usage
The data-content attribute accepts a string value containing HTML/XHTML markup or plain text. The content is parsed according to the document’s default parser or the MIME type specified in data-content-type. Unlike static content declared in the template, data-content evaluates at runtime, allowing you to:
- Generate content from bound data values
- Insert dynamically constructed HTML/XHTML markup
- Replace element content conditionally
- Prepend or append dynamic content to existing elements
- Create inline templates without nested element structures
This attribute is particularly useful when:
- Content structure varies based on data
- You need to generate markup programmatically
- Template content comes from external sources
- You want to avoid deeply nested template structures
Basic Syntax
<!-- Simple text content -->
<label data-content="{{model.userName}}">Default Name</label>
<!-- HTML markup content -->
<div data-content="<p>Hello <strong>{{model.name}}</strong></p>"></div>
<!-- Dynamic content from bound expression -->
<span data-content="{{model.dynamicContent}}"></span>
<!-- Dynamic content in a template to be used when looping over each item (completely dynamic) -->
<template data-bind="{{context.items}}" data-content="{{model.layout}}" data-content-type="text/html" ></template>
Content Actions
The data-content-action attribute controls how the content is inserted:
| Action | Behavior | Default |
|---|---|---|
append |
Add after existing content | No (except for VisualComponent) |
prepend |
Add before existing content | No |
replace |
Replace all existing content | Yes (for most components) |
<!-- Replace existing content (default for labels) -->
<label data-content="{{model.newText}}">Old text</label>
<!-- Append to existing content -->
<div data-content="<p>{{model.additionalInfo}}</p>" data-content-action="append">
<p>Existing content stays here</p>
</div>
Supported Elements
The data-content attribute is supported on the following elements:
Primary Support
<label>- Text labels with dynamic content (HTMLLabel)<template>- Template elements with inline content definition (HTMLTemplate)<frame>- Frame elements for document assembly (HTMLFrame)
General Support
- All
VisualComponentdescendants - Any visual element that inherits from VisualComponent
Implementation Details
HTMLLabel: Content is set directly and rendered as text literal during pre-layout phase.
HTMLTemplate: Content is parsed as a template and used instead of child elements.
HTMLFrame: Content is parsed as HTML document and used for frame content.
VisualComponent: Content is parsed according to MIME type and inserted per the specified action.
Binding Values
Expression Evaluation
The data-content attribute value is evaluated during data binding, supporting:
<!-- Direct property binding -->
<label data-content="{{model.userName}}"></label>
<!-- Legacy syntax -->
<label data-content="{@:model.userName}"></label>
<!-- String concatenation -->
<label data-content="{{concat('Hello ', model.name)}}"></label>
<!-- Conditional content -->
<label data-content="{{model.isActive ? 'Active' : 'Inactive'}}"></label>
Content Type Specification
Use data-content-type to specify the MIME type of the content:
<!-- HTML content (default) -->
<div data-content="<p>HTML content</p>" data-content-type="text/html"></div>
<!-- XHTML content -->
<div data-content="<div xmlns='http://www.w3.org/1999/xhtml'><p>XHTML</p></div>"
data-content-type="application/xhtml+xml"></div>
<!-- Plain text -->
<div data-content="{{model.plainText}}" data-content-type="text/plain"></div>
<!-- Markdown content -->
<div data-content="{{model.markdownContent}}" data-content-type="text/markdown"></div>
Markup Requirements
Content must be well-formed according to the specified type:
For HTML/XHTML:
- Must be valid markup
- Elements must be properly closed
- Attributes must be quoted
- Namespace declarations required for XHTML
For Plain Text:
- No parsing applied
- Rendered as literal text
- Special characters handled automatically
Notes
Content Parsing
-
Parse Timing: Content is parsed and inserted during the data binding phase, after all binding expressions are evaluated.
-
Parser Selection: The document’s parser for the specified MIME type is used. If no type is specified, the default content MIME type is used.
-
Validation: In strict conformance mode, parse errors will throw exceptions. In lax mode, warnings are logged.
-
Component Requirements: Parsed content must result in compatible components for the target element.
Label-Specific Behavior
For <label> elements:
- Content is set as simple text during pre-layout
- Existing content is cleared
- No HTML parsing occurs
- Most efficient for simple text replacement
- Always replaces existing content
Template-Specific Behavior
For <template> elements:
- Content defines the template to repeat
- Takes precedence over child elements
- Must contain valid HTML/XHTML
- Creates parsable template generator
- Useful for dynamic template definition
Frame-Specific Behavior
For <frame> elements:
- Content must be a complete HTML document
- Parsed as HTMLDocument component
- Used for document assembly scenarios
- Initialized and data-bound after parsing
- Only accepts full XHTML documents
Content Action Behavior
The default data-content-action varies by component:
- Label: Always replaces (action ignored)
- Template: Always replaces (action ignored)
- Frame: Always replaces (action ignored)
- VisualComponent: Default is
append
Performance Considerations
- Parsing content has overhead; cache when possible
- Use
data-style-identifierwith repeated patterns - Avoid complex markup in large iterations
- Consider building content in code for complex scenarios
- Pre-process and simplify content when feasible
Security Considerations
- Content is parsed and executed in the document context
- Validate external content sources
- Sanitize user-provided content
- Be cautious with dynamic code generation
- Use strict conformance mode for validation
Examples
1. Simple Text Replacement in Label
Replace label text with bound data:
<!-- Model: { userName: "John Smith" } -->
<label data-content="{{model.userName}}">Loading...</label>
<!-- Result: John Smith -->
2. Dynamic Formatted Text
Generate formatted content from data:
<!-- Model: { firstName: "Jane", lastName: "Doe", title: "Dr." } -->
<label data-content="{{concat(model.title, ' ', model.firstName, ' ', model.lastName)}}">
Name Placeholder
</label>
<!-- Result: Dr. Jane Doe -->
3. Conditional Content Display
Show different content based on conditions:
<!-- Model: { isActive: true, status: "Premium" } -->
<label data-content="{{model.isActive ? concat('Active - ', model.status) : 'Inactive'}}">
Status Unknown
</label>
<!-- Result: Active - Premium -->
4. Dynamic HTML Content in Div
Insert formatted HTML markup:
<!-- Model: { productName: "Widget Pro", price: 29.99, inStock: true } -->
<div data-content="<div><strong>{{model.productName}}</strong><br/>Price: ${{model.price}}<br/><em>{{model.inStock ? 'In Stock' : 'Out of Stock'}}</em></div>"></div>
<!-- Renders formatted product info -->
5. Inline Template Content
Define template content inline without nested elements:
<!-- Model: { items: [{name: "A"}, {name: "B"}] } -->
<template data-bind="{{model.items}}"
data-content="<div style='padding:5pt; border:1pt solid #ccc;'>{{.name}}</div>">
</template>
<!-- Generates: -->
<!-- <div>A</div> -->
<!-- <div>B</div> -->
6. Appending Dynamic Content
Add content after existing elements:
<div data-content="<p><em>{{model.disclaimer}}</em></p>" data-content-action="append">
<p>Main content appears here first.</p>
</div>
<!-- Results in main content followed by disclaimer -->
7. Prepending Header Content
Add content before existing elements:
<div data-content="<h2>{{model.title}}</h2>" data-content-action="prepend">
<p>This paragraph will appear after the title.</p>
</div>
<!-- Results in title followed by paragraph -->
8. Complex Multi-Line HTML Content
Generate complex markup structure:
<!-- Model: { user: {name: "Alice", role: "Admin", department: "IT"} } -->
<div data-content="<div style='border: 2pt solid #336699; padding: 10pt;'>
<h3 style='margin: 0; color: #336699;'>{{model.user.name}}</h3>
<div style='margin-top: 5pt;'>
<strong>Role:</strong> {{model.user.role}}<br/>
<strong>Department:</strong> {{model.user.department}}
</div>
</div>">
</div>
9. Dynamic Table Content Generation
Create table structure dynamically:
<!-- Model: { columns: ["ID", "Name", "Status"], hasData: true } -->
<div data-content="<table style='width:100%;'>
<thead>
<tr>
<template data-bind='{{model.columns}}'>
<th>{{.}}</th>
</template>
</tr>
</thead>
<tbody>
<tr><td colspan='3' style='text-align:center;'>
{{model.hasData ? 'Data will appear here' : 'No data available'}}
</td></tr>
</tbody>
</table>">
</div>
10. Conditional Section Inclusion
Include entire sections conditionally:
<!-- Model: { showWarning: true, warningMessage: "Important notice" } -->
<div data-content="{{model.showWarning ? concat('<div style=\'background-color: #fff3cd; padding: 10pt; border: 1pt solid #ffc107;\'><strong>Warning:</strong> ', model.warningMessage, '</div>') : ''}}">
</div>
<!-- Only renders if showWarning is true -->
11. List Generation with Dynamic Styling
Create styled list items:
<!-- Model: { priority: "high", tasks: ["Task A", "Task B"] } -->
<template data-bind="{{model.tasks}}"
data-content="<div style='padding: 8pt; margin: 5pt; border-left: 3pt solid {{model.priority === 'high' ? '#dc3545' : '#28a745'}};'>{{.}}</div>">
</template>
<!-- Each task gets colored border based on priority -->
12. Placeholder with Fallback Content
Show dynamic content or fallback:
<!-- Model: { description: null } -->
<div data-content="{{model.description ? model.description : '<em style=\'color: #999;\'>No description available</em>'}}">
Loading...
</div>
<!-- Shows fallback when description is null -->
13. Markdown Content Injection
Inject dynamically bound markdown — useful when content comes from a CMS or external source stored as markdown:
<!-- Model: { notes: "## Summary\n\nThis is the **summary** section.\n\n- Point one\n- Point two" } -->
<div data-content="{{model.notes}}" data-content-type="text/markdown"></div>
<!-- Notes are parsed as markdown and rendered as styled HTML in the PDF -->
Both text/markdown and text/x-markdown MIME types are accepted. Content is converted to HTML before layout, so all standard Scryber HTML elements and CSS styles apply to the rendered output.
Markdown bindings also work inside {{#each}} loops:
<!-- Model: { items: [{ title: "Alpha", body: "## Alpha\n\nContent for alpha." }, ...] } -->
<ul>
{{#each model.items}}
<li data-content="{{this.body}}" data-content-type="text/markdown"></li>
{{/each}}
</ul>
14. Frame with Dynamic Document Content
Assemble documents dynamically:
<!-- Model: { reportContent: "<html>...</html>" } -->
<frameset>
<frame src="header.html"></frame>
<frame data-content="{{model.reportContent}}"></frame>
<frame src="footer.html"></frame>
</frameset>
<!-- Inserts dynamic report between header and footer -->
15. Localized Content Injection
Insert locale-specific content:
<!-- Model: { locale: "es", welcomeMessages: {en: "Welcome", es: "Bienvenido"} } -->
<label data-content="{{model.welcomeMessages[model.locale]}}">
Welcome
</label>
<!-- Shows: Bienvenido -->
16. Rich Text with Embedded Bindings
Create complex formatted content with multiple bindings:
<!-- Model: { order: {id: 1234, date: "2024-10-13", customer: "Acme Corp", total: 1500.00} } -->
<div data-content="<div style='padding: 15pt; border: 2pt solid #336699;'>
<h2 style='margin: 0 0 10pt 0; color: #336699;'>Order Confirmation</h2>
<div style='margin-bottom: 5pt;'><strong>Order Number:</strong> {{model.order.id}}</div>
<div style='margin-bottom: 5pt;'><strong>Date:</strong> {{model.order.date}}</div>
<div style='margin-bottom: 5pt;'><strong>Customer:</strong> {{model.order.customer}}</div>
<div style='margin-top: 10pt; padding-top: 10pt; border-top: 1pt solid #ccc;'>
<strong>Total:</strong> <span style='font-size: 14pt; color: #336699;'>${{model.order.total}}</span>
</div>
</div>">
</div>
17. Template with Alternate Content Definition
Define template behavior inline:
<!-- Model: { items: [{type: "A", name: "Item 1"}, {type: "B", name: "Item 2"}] } -->
<template data-bind="{{model.items}}"
data-content="<div style='padding: 10pt; background-color: {{.type === 'A' ? '#e3f2fd' : '#f3e5f5'}}; margin-bottom: 5pt;'>
<strong>{{.type}}:</strong> {{.name}}
</div>">
</template>
<!-- Type A items get blue background, Type B get purple -->
See Also
- data-content-type attribute - Specify content MIME type
- data-content-action attribute - Control content insertion
- label element - Label element reference
- template element - Template element reference
- frame element - Frame element reference
- Data Binding - Data binding overview
- Expressions - Expression syntax guide