Ignition Vision sends the Text Area value through a short data path: a script or template instance creates the string, a root-container custom property carries it, and the component’s text binding displays it. When the Text Area shows \n instead of starting a new line, the path contains two printable characters—a backslash and the letter n—rather than a newline character. Follow the value from its producer to the component and correct it at the first point where the representation is wrong.
Where does the text change on its way to the Text Area?
The sender determines whether the string contains control characters or escape notation. A Python-style script literal such as 'This is line 1\nThis is line 2' is parsed before assignment, so the escape sequence becomes a newline character. By contrast, text entered into a custom-property editor can remain the literal characters \ and n. A binding transfers the resulting string; it does not necessarily parse escape notation again.
| Path stage | Responsibility | Diagnostic question |
|---|---|---|
| Script, database value, or template parameter | Creates the string and its control characters | Does the value contain a real newline or literal \n? |
Root-container custom property such as body or ActionSteps
|
Stores and transports the string | Did its length and character sequence remain unchanged? |
| Binding | Copies the property value to the Text Area text property |
Does a direct binding receive the same value as its source? |
| Text Area renderer | Displays newline characters as line boundaries | Does a newline entered interactively survive a readback? |
A working system.nav parameter test demonstrates the distinction. Passing {'body': 'This is line 1\nThis is line 2'} from a script produced two lines, while manually entering the apparent escape sequence in the root custom property displayed it literally. The visible text looked similar at configuration time, but the underlying character sequences differed.
Check: identify the component or script that first constructs the value; do not start by changing the final binding.
Is the string carrying a newline or two printable characters?
Inspect character data, not the way a console or property editor paints it. Printing the string alone can be misleading: a real newline produces two output lines, while some diagnostic representations expose it as escape notation. Test the content explicitly.
value = event.source.parent.body
print repr(value)
print len(value)
print value.find(chr(10))
print value.find('\\n')
chr(10) tests for a line-feed character. The second search tests for the two-character token \n. A nonnegative result from the first search means the value already contains a real line break. A nonnegative result from only the second means the upstream producer supplied escaped text that still requires decoding.
| Observed content | Meaning | Required action |
|---|---|---|
find(chr(10)) locates a character |
A line feed is present | Trace whether the binding receives the same value |
find('\\n') locates a token, but no line feed exists |
The data contains backslash plus n
|
Convert the token once at the input boundary |
| Both forms occur | The input mixes encoded and decoded lines | Define one input contract before converting |
| Neither occurs | No line separator is present | Correct the source data or insertion logic |
When a user enters multiple lines directly in the Text Area, reading the component value back into a script preserves those line breaks. That is a useful control test because it proves the component can hold multiline text.
Check: record which search finds a match and the index it returns before altering the value.
How should the root custom property and binding be connected?
Use one authoritative property and one direct binding. For the demonstrated naming, the root container exposes body, and the Text Area named Body binds its text property to that custom property. The producer writes to body; the binding moves it to the component.
A test script that reads event.source.parent.body and then writes event.source.parent.getComponent('Body').text bypasses the binding for the final assignment. That can confirm the component accepts the value, but it does not test whether the binding path works. During commissioning, test the bound path separately from a direct component assignment.
- Bind the Text Area
textproperty directly to the root custom property. - Remove any temporary script that also writes the component’s
textproperty. Two writers make the last update win and obscure the actual fault. - Set the root property from a script with a known real newline, using
chr(10)if visual escaping is unclear. - Read back both the root property and the component property with
repr(). - Compare their character content and displayed line count.
Check: the root property and Text Area must report the same newline position with no direct write to the component.
How should a template repeater deliver multiline text?
A repeated template adds another hop: the repeater instance data populates a template custom property, and the internal binding copies that property to the Text Area. The same character rule applies at every hop. A template property named ActionSteps will not reinterpret literal escape notation merely because an expression binding reads it.
Define the template input contract as decoded multiline text. The repeater’s data-building script should insert real newline characters before assigning the instance property. Keep decoding outside the repeated component where possible; this gives every instance the same representation and prevents repeated expressions from applying different conversions.
- Build one test instance whose
ActionStepsvalue contains'First step' + chr(10) + 'Second step'. - Bind the internal Text Area
textproperty directly toActionSteps. - Confirm that instance renders two lines.
- Feed a production value through the same instance-data path.
- If the production value displays
\n, inspect and normalize it where the repeater dataset or instance structure is created.
Navigation parameters are not required for templates. The successful system.nav case matters because its script constructed a real newline; the transport mechanism itself is not the remedy.
Check: compare a known-good chr(10) instance with one production instance using the same template and binding.
Where should literal escape sequences be converted?
Convert escaped text only when the producer’s documented format uses literal \n as a line delimiter. Apply the conversion once, immediately after receiving that format. Replacing tokens at several layers risks transforming text the user intended to keep literally.
incoming = event.source.parent.ActionSteps
normalized = incoming.replace('\\n', chr(10))
event.source.parent.ActionSteps = normalized
This conversion handles literal backslash-n. It does not need HTML and does not depend on the Text Area parsing markup. If an upstream system supplies carriage-return and line-feed tokens separately, determine their actual character sequence with repr(), then normalize according to that interface contract. Do not blindly apply replacements for \n, \n\n, and \r\n in succession; overlapping rules can produce extra line breaks.
User-entered text needs a different rule. A user may legitimately type the characters \n while describing a command, file format, or escape sequence. Decode them only if the UI explicitly defines \n as an input convention. Text entered with the Enter key already contains line separators and needs no token replacement.
Check: run normalization twice on a test value and verify that the second pass makes no change to already-decoded multiline text.
Why do HTML, URL encoding, and line wrap fail here?
| Attempt | What it represents | Why it does not solve literal \n
|
|---|---|---|
<HTML>This is line 1<br>This is line 2 |
HTML markup with a break element | A Text Area is an editable text component, not an HTML presentation component |
%0D%0A |
Percent-encoded carriage return and line feed | It remains text unless a URL-decoding stage processes it |
Two literal \n tokens |
Two encoded delimiter tokens | Repeating an undecoded token does not create a control character |
Literal \r\n
|
Backslash tokens for carriage return and line feed | The renderer still receives printable characters until they are decoded |
| Line wrap | Visual wrapping at the component boundary | It wraps long lines but does not insert a deliberate line boundary |
A Label can be appropriate for read-only HTML presentation, and the documented Vision HTML behavior is described in HTML in Vision. It does not replace an editable Text Area when users must submit and revise multiline input. Keep stored editable content as plain text with real newline characters.
Check: resize the Text Area: wrapped lines may move with the width, but a real newline must remain at the same character position.
How do you verify the complete data path?
- Create a sentinel value containing ordinary text, one real newline made with
chr(10), and more ordinary text. - Assign it at the actual production entry point: navigation script, repeater instance builder, database read handler, or other producer.
- Inspect the producer output with
repr()and locatechr(10). - Inspect the root or template custom property using the same tests.
- Inspect the bound Text Area
textproperty without writing to it directly. - Edit the second line in the Text Area, read the value back, and verify that the newline remains in the returned string.
- Repeat with production data and confirm no layer converts the control character back to literal escape notation.
The passing result is exact: the producer, custom property, and Text Area contain a line feed at the same position; the display shows two editable lines; and the edited value returns through the application path with the line boundary intact.
FAQ
Why does an Ignition Vision Text Area display \n literally?
The value contains a printable backslash and n, not a newline character. Test for chr(10) and convert literal \n once at the input boundary.
Why does \n work in a script but not in a custom property editor?
The script parser converts \n inside a string literal into a newline before assignment. Text entered into a property editor may be stored exactly as typed, so the binding receives two printable characters.
Why does system.nav display two lines when a direct binding does not?
The navigation script can construct the parameter with a real newline. Compare the parameter and bound-property values with repr(); the transport path is not a substitute for correct character data.
Why does line wrap not create the requested line break?
Line wrap changes display layout according to component width. A deliberate break requires a newline character in the string and remains at the same position when the component is resized.
Why does the newline disappear after the user edits the text?
Trace the return path for a conversion or storage layer that serializes the control character as literal \n. Final verification passes only when the edited value returns with chr(10) at the same line boundary.