Simulators
Content controls
A content control is a structured region of a document: plain text, rich text, drop-down, check box, date picker, group or a repeating section of table rows. The simulator serves no ContentControl object, but a document you seed can hold them, and every edit through a paragraph or a range follows the rules Word for the web applies in and around them. In a template with a repeating section, deleting part of a row’s text is refused and deleting all of it goes through:
await office.seedDocx('fixtures/engagement-letter.docx');
await Word.run(async context => {
context.document.body.search('Avery').getFirst().delete();
await context.sync(); // throws GeneralException, the document unchanged
});
await Word.run(async context => {
context.document.body.search('Avery Quinn').getFirst().delete();
await context.sync(); // goes through
});
A date picker can’t be deleted
An edit that would delete any of a date picker content control’s text throws NotAllowed and leaves the document as it was. That holds for an inline or block date picker, tracked or not, and for these edits:
body.clear();- a
Replaceover it, byinsertTextorinsertOoxml, on the body, a paragraph or a range; deleteof a range or paragraph that holds any of it.
Text inserted at the start of a range inside one throws NotAllowed too. Deleting a range that only touches an inline one’s edge throws GeneralException. reset() still starts a fresh document.
getOoxml writes a date picker in two once text goes in at its start
Text your add-in inserts at an inline date picker’s start, from outside it, stays outside the picker. But the body’s and the paragraph’s getOoxml then write two controls: an empty one without the date, followed by one with a new id holding that text and the date. Once text before the picker in its paragraph is deleted, they write, in order: that text, the empty control, the date outside any control and the second control, empty. Text inserted at its end leaves it whole.
A cell control inside a row control takes no text
Text inserted inside a content control around a table cell throws NotAllowed when a content control around its row holds that cell, as in a repeating section’s items. The document stays as it was, tracked or not, through the paragraph or a range in it. A control around the cell alone, or around the row alone, takes the edit, and so does another cell of the same row.
A group content control is locked
A group content control imports locked. So an edit to text in its paragraphs throws NotAllowed and leaves the document as it was, tracked or not. That covers:
- text inserted, deleted or replaced through a paragraph or a range;
- a paragraph inserted beside one, or deleted;
- a delete across the group’s edge.
A rich-text control inside the group still takes edits, and so do the paragraphs around it. body.clear(), a Replace of the whole body and a delete of a range around the group take it with them.
A paragraph break inside an inline content control makes it a block control
insertText with a newline, or insertOoxml of several paragraphs, through a range inside an inline rich-text content control turns the control into a block control around the paragraphs it then spans. The paragraph splits at the control’s edges. With X\nY inserted after beta:
- before:
Alpha+ controlbeta gamma+.; - after:
Alpha,betaX,Y gammaand., the control holding the middle two.
Text inserted at the range’s Start goes inside the control. A Replace with a newline, from inside the control past its end, converts it too. It leaves the text past the deleted span in a paragraph of its own.
After insertText splits the paragraph, the range you inserted through keeps its text, as do ranges made in the same context.sync(). Ranges from an earlier sync that only read keep their offsets in the paragraph’s first part. With tracking on, each paragraph mark the split makes is an insertion.
insertOoxml leaves the paragraph and other ranges at their offsets in the last part, as its own splits do. Its Replace past the control’s end leaves the control inline, with the package inside it. If spaces ended the control’s text before the range, those spaces leave the control, and the package lands after them, outside. A locked content control refuses the split at its edge with NotAllowed.
A row control takes only a paragraph’s whole text away
With deletions untracked, part of a paragraph’s text inside a content control around table rows, such as a repeating section’s, can’t be deleted or replaced through delete or insertText. delete of a range that holds such a part, and insertText(…, 'Replace') over it, throw GeneralException and leave the document as it was. That holds wherever the part lies, even when the new text is the same. A delete from part of one paragraph into the next there takes only the text of the paragraphs it holds whole, without an error.
These go through: deleting or replacing a paragraph’s whole text, inserting text anywhere, Paragraph.delete, insertOoxml and tracked edits.
Deleting a content control’s only paragraph removes the control
delete() on the only paragraph of a block content control takes the control with it, and any control that held only that one. The body’s getOoxml and the saved .docx then no longer hold it. When that paragraph ends a table cell or the document, the control goes and the paragraph stays in its place, empty.
Tracked, the paragraph is marked deleted as any is, and so are the control’s edges. getOoxml writes a w:customXmlDelRangeStart and w:customXmlDelRangeEnd pair around each edge. No tracked change lists the edges:
- Rejecting the deletion brings the paragraph back, the control’s edges still marked.
- Accepting the deletion joins the paragraph to the next and leaves the control there, empty and inline at its start, its edges still marked deleted. Where the paragraph ends a table cell, or a table follows the control, it stays instead, empty.
Accepting or rejecting any change that takes the mark of a control’s only paragraph does the same, such as a seed’s deletion or insertion. So does accepting the deleted mark of the paragraph just before such a control: that paragraph joins it, with the control inline around its text. A tracked delete that takes a block control whole, such as body.clear(), marks its edges the same way. A control’s last paragraph of two goes as any paragraph does, and the control stays.
A control left empty by an accept or reject
A content control that accepting or rejecting a change left empty stays empty, inline or a block holding an empty paragraph, its edges not deleted.
Differs from Word for the web: the first time you read the body’s getOoxml, Word gives such a control its placeholder: w:showingPlcHdr and five spaces styled PlaceholderText. The paragraph’s text then reads as five no-break spaces.
A block control’s paragraph joined by acceptAll
acceptAll can join a block content control’s only paragraph to the one before it, then stop at the next change. The simulator’s getOoxml then writes the joined paragraph as it holds it.
Differs from Word for the web: Word’s getOoxml of the joined paragraph writes the first control, empty, and nothing after it, or leaves out the deleted text’s last character.