# docx-editor-mail-merge

> Word document editor with mail-merge fields, insert-field dialog, and merge toolbar.

**Framework:** vue  **Component:** DocxEditor  **Variant:** mail-merge

## Get this item

**If you are an agent, fetch the JSON.** Source is inlined, so one request is enough and no tooling is required:

```
GET https://ai.syncfusion.com/r/vue/docx-editor-mail-merge.json
```

**Install Package(s)**

```bash
npm install @syncfusion/ej2-vue-buttons @syncfusion/ej2-vue-documenteditor @syncfusion/ej2-vue-lists @syncfusion/ej2-vue-popups @syncfusion/ej2-vue-splitbuttons
```

**Notes**

- Syncfusion release: 2026 Volume 2 (v34.1.29)
- The Syncfusion package is licensed. The composition in this file is source you own and edit. See https://ai.syncfusion.com/licensing.md

## Source files

### src/components/docx-editor-mail-merge/DocxEditor.vue

```vue
<template>
<div class='control-pane'>
<div class="control-section">
  
   
        <div class="default-section">
          <div ref="de_titlebar" id="documenteditor_titlebar" class="e-de-ctn-title">
    <div v-on:keydown="titleBarKeydownEvent" v-on:click="titleBarClickEvent" class="single-line" id="documenteditor_title_contentEditor" title="Document Name. Click or tap to rename this document." contenteditable="false">
        <label v-on:blur="titleBarBlurEvent" id="documenteditor_title_name" :style="titileStyle" >{{documentName}}</label>
    </div>    
    <ejs-button id="de-print" :style="iconStyle" :iconCss="printIconCss" v-on:click="printBtnClick" title="Print this document (Ctrl+P).">Print</ejs-button>	
    <ejs-dropdownbutton ref="de-export" :style="iconStyle" :items="exportItems" :iconCss="exportIconCss" cssClass="e-caret-hide" content="Download" v-bind:select="onExport" :open="openExportDropDown" title="Download this document."></ejs-dropdownbutton>        
</div>
<div style="display: flex; flex-direction: row;">
<div class="col-lg-2 control-section" style="padding-right: inherit;padding-top:5px;padding-left: 5px;height: 600px;border-left: 1px solid rgb(238, 238, 238);border-bottom:  1px solid rgb(238, 238, 238);" >
                    <b><label style="display:block;margin:1px;padding-top: 18px;font-size: 14px;padding-top: 5px">Select Field to Insert</label></b>
                    <ejs-listview id='de-flat-list' :dataSource='listData' :fields='fields' :select="onselect"></ejs-listview>
                </div>
                <div class="col-lg-10 control-section" style='padding-left: 0px;padding-right: 0px;padding-top: 0px;' >
<ejs-documenteditorcontainer id="container" ref="doceditcontainer" :serviceUrl="hostUrl" :enableToolbar='true' height='600px' :toolbarClick='clickHandler'>
     <ejs-dialog ref="accessDialog" :header='header' :width='width' :height='height' :showCloseIcon='true'  :buttons='buttons' :content='content'>
        </ejs-dialog></ejs-documenteditorcontainer>  
                </div>
                </div>            
        </div>
    </div>
     <div class="overlay" id="popup-overlay"></div>
            <div id='waiting-popup' >
                <svg class="circular" height="40" width="40">
                    <circle className="circle-path" cx="25" cy="25" r="20" fill="none" stroke-width="6" stroke-miterlimit="10" />
                </svg>
            </div>
</div>
</template>
<script>
import { DocumentEditorContainerComponent, Toolbar } from "@syncfusion/ej2-vue-documenteditor";
import { DropDownButtonComponent } from "@syncfusion/ej2-vue-splitbuttons";
import { DialogComponent } from '@syncfusion/ej2-vue-popups';
import { ButtonComponent } from '@syncfusion/ej2-vue-buttons';
import { Dialog } from '@syncfusion/ej2-popups';
import { ListViewComponent } from "@syncfusion/ej2-vue-lists";
import { Draggable, Droppable } from '@syncfusion/ej2-base';

export default {
    components: {
        'ejs-documenteditorcontainer': DocumentEditorContainerComponent,
        'ejs-dropdownbutton': DropDownButtonComponent,
        'ejs-button': ButtonComponent,
        'ejs-dialog': DialogComponent,
        'ejs-listview': ListViewComponent
    },
    data: function() {
        return {
          hostUrl : 'https://document.syncfusion.com/web-services/docx-editor/api/documenteditor/',
          documentName : 'Mail Merge',
          documentTitle: 'Untitled Document',
          iconStyle: 'float:right;background: transparent;box-shadow:none;border-color: transparent;border-radius: 2px;color:inherit;font-size:12px;text-transform:capitalize;margin-top:4px;height:28px;font-weight:400;font-family:inherit;',
            titileStyle: 'text-transform:capitalize;font-weight:400;font-family:inherit;text-overflow:ellipsis;white-space:pre;overflow:hidden;user-select:none;cursor:text',
            openIconCss: 'e-de-icon-Open e-de-padding-right',
            printIconCss: 'e-de-icon-Print e-de-padding-right',
            exportIconCss: 'e-de-icon-Download e-de-padding-right',
            exportItems: [
                { text: 'Syncfusion Document Text (*.sfdt)', id: 'sfdt' },
                { text: 'Word Document (*.docx)', id: 'word' },
                { text: 'Word Template (*.dotx)', id: 'dotx' },
                { text: 'Plain Text (*.txt)', id: 'txt' },
            ],
        listData: [
        {
            text: 'ProductName',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'ShipName',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'CustomerID',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'Quantity',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'UnitPrice',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'Discount',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'ShipAddress',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'ShipCity',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'ShipCountry',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'OrderId',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        },
        {
            text: 'OrderDate',
            category: 'Drag or click the field to insert.',
            htmlAttributes: { draggable: true }
        }
    ],
    fields: {tooltip: 'category'},
     toolbarItems : [
            'New', 'Open', 'Separator', 'Undo',
            'Redo',
            'Separator',
            {
                prefixIcon: 'sf-icon-InsertMergeField',
                tooltipText: 'Insert Field',
                text: this.onWrapText('Insert Field'),
                id: 'InsertField'
            },
            {
                prefixIcon: 'sf-icon-FinishMerge',
                tooltipText: 'Merge Document',
                text: this.onWrapText('Merge Document'),
                id: 'MergeDocument'
            },
            'Separator',
            'Image',
            'Table',
            'Hyperlink',
            'Bookmark',
            'TableOfContents',
            'Separator',
            'Header',
            'Footer',
            'PageSetup',
            'PageNumber',
            'Break',
            'Separator',
            'Find',
            'Separator',
            'Comments',
            'TrackChanges',
            'Separator',
            'LocalClipboard',
            'RestrictEditing',
            'Separator',
            'FormFields',
            'UpdateFields',
        ],
        header: 'Merge Field',
        content:
            '<div class="dialogContent">'
            // tslint:disable-next-line:max-line-length
            + '<label class="e-insert-field-label">Name:</label></br><input type="text" id="field_text" class="e-input" placeholder="Type a field to insert eg. FirstName">'
            + '</div>',
        showCloseIcon: true,
        isModal: true,
        width: '200px',
        height: '200px',
        close: this.closeFieldDialog,
        buttons: [
            {
                'click':this.buttonclick,
                buttonModel: {
                    content: 'Ok',
                    cssClass: 'e-flat',
                    isPrimary: true,
                },
            },
            {
                'click': this.closeFieldDialog,
                buttonModel: {
                    content: 'Cancel',
                    cssClass: 'e-flat',
                },
            },
        ],
   
         
        };
    },  
    provide:{
        DocumentEditorContainer:[Toolbar]
    },
      methods: {
        onExport: function (args) {
            switch (args.item.id) {
                case 'word':
                    this.save('Docx');
                    break;
                case 'sfdt':
                    this.save('Sfdt');
                    break;
                case 'txt':
                    this.save('Txt');
                    break;
                case 'dotx':
                    this.save('Dotx');
                    break;
            }
        },
        openExportDropDown: function () {
            // tslint:disable-next-line:max-line-length
            document.getElementById('word').setAttribute('title', 'Download a copy of this document to your computer as a DOCX file.');
            // tslint:disable-next-line:max-line-length
            document.getElementById('sfdt').setAttribute('title', 'Download a copy of this document to your computer as an SFDT file.');
            // tslint:disable-next-line:max-line-length
            document.getElementById('txt').setAttribute('title', 'Download a copy of this document to your computer as a TXT file.');
            // tslint:disable-next-line:max-line-length
            document.getElementById('dotx').setAttribute('title', 'Download a copy of this document to your computer as a DOTX file.');
        },
        save: function (format) {
            // tslint:disable-next-line:max-line-length
             this.$refs.doceditcontainer.ej2Instances.documentEditor.save( this.$refs.doceditcontainer.ej2Instances.documentEditor.documentName === '' ? 'sample' :  this.$refs.doceditcontainer.ej2Instances.documentEditor.documentName, format);
        },
        openBtnClick: function () {
            this.$refs.uploadDocument.click();
        },
        printBtnClick: function () {
            this.$refs.doceditcontainer.ej2Instances.documentEditor.print();
        },
        titleBarKeydownEvent: function (e) {
            if (e.keyCode === 13) {
                e.preventDefault();
                document.getElementById("documenteditor_title_contentEditor").contentEditable = 'false';
                if (document.getElementById("documenteditor_title_contentEditor").textContent === '') {
                    document.getElementById("documenteditor_title_contentEditor").textContent = 'Document1';
                }
            }
        },
        titleBarBlurEvent: function (args) {
            if (document.getElementById("documenteditor_title_contentEditor").textContent === '') {
                document.getElementById("documenteditor_title_contentEditor").textContent = 'Document1';
            }
            document.getElementById("documenteditor_title_contentEditor").contentEditable = 'false';
            this.$refs.doceditcontainer.ej2Instances.documentEditor.documentName = document.getElementById("documenteditor_title_name").textContent;
        },
        titleBarClickEvent: function () {
            this.updateDocumentEditorTitle();
        },
        updateDocumentEditorTitle: function () {
            document.getElementById("documenteditor_title_contentEditor").contentEditable = 'true';
            document.getElementById("documenteditor_title_contentEditor").focus();
            window.getSelection().selectAllChildren(document.getElementById("documenteditor_title_contentEditor"));
        },
        documentChangedEvent: function () {
            var obj = this.$refs.doceditcontainer.ej2Instances.documentEditor;
            this.documentTitle = obj.documentName === '' ? 'Untitled Document' : obj.documentName;
            document.getElementById("documenteditor_title_name").textContent = obj.documentName ;
            setTimeout(() => { obj.scrollToPage(1); }, 10);
        },
        onselect: function (args) {
        var fieldName = args.text;
      //  listView.selectItem(undefined);
        this.insertField(fieldName);
    },
    insertField: function(fieldName) {
        var fileName = fieldName.replace(/\n/g, '').replace(/\r/g, '').replace(/\r\n/g, '');
        var fieldCode = 'MERGEFIELD  ' + fileName + '  \\* MERGEFORMAT ';
       // container.documentEditor.editor.insertField(fieldCode, '«' + fieldName + '»');
        //container.documentEditor.focusIn();
        this.$refs.doceditcontainer.ej2Instances.documentEditor.editor.insertField(fieldCode, '«' + fieldName + '»');
        this.$refs.doceditcontainer.ej2Instances.documentEditor.focusIn();
    },
     onWrapText: function (text) {
        var content = '';
        var index = text.lastIndexOf(' ');
        content = text.slice(0, index);
        text.slice(index);
        content += '<div class="e-de-text-wrap">' + text.slice(index) + '</div>';
        return content;
    },
    buttonclick: function()
    {
         var fieldNameTextBox = document.getElementById('field_text');
                    var fieldName = fieldNameTextBox.value;
                    if (fieldName !== '') {
                        this.$refs.doceditcontainer.ej2Instances.documentEditor.editor.insertField('MERGEFIELD ' + fieldName + ' \\* MERGEFORMAT');
                    }
                    this.$refs.accessDialog.hide();
                    this.$refs.doceditcontainer.ej2Instances.documentEditor.focusIn();
    },
   clickHandler: function (args) {
            switch (args.item.id) {
            case 'MergeDocument':
                this.mergeDocument();
                break;
            case 'InsertField':
                this.showInsertFielddialog();
                break;
        }
        },
     mergeDocument: function () {
        this.$refs.doceditcontainer.ej2Instances.documentEditor.saveAsBlob('Docx').then((blob) => {
            var exportedDocumment = blob;
            var fileReader = new FileReader();
            fileReader.onload = () => {
                var base64String = fileReader.result;
                var responseData= {
                    fileName: this.$refs.doceditcontainer.ej2Instances.documentEditor.documentName + '.docx',
                    documentData: base64String
                };
               // waitingPopUp = document.getElementById('waiting-popup');
                //inActiveDiv = document.getElementById('popup-overlay');
                this.showHideWaitingIndicator(true);
                var baseUrl = 'https://document.syncfusion.com/web-services/docx-editor/api/documenteditor/MailMerge';
                var httpRequest = new XMLHttpRequest();
                httpRequest.open('POST', baseUrl, true);
                httpRequest.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
                httpRequest.onreadystatechange = () => {
                    if (httpRequest.readyState === 4) {
                        if (httpRequest.status === 200 || httpRequest.status === 304) {
                            this.$refs.doceditcontainer.ej2Instances.documentEditor.open(httpRequest.responseText);
                        } else {
                            // Failed to merge document
                            DialogUtility.alert({
                                title: 'Information',
                                content: 'failure to merge document',
                                showCloseIcon: true,
                                closeOnEscape: true,
                            });
                        }
                        this.showHideWaitingIndicator(false);
                    }
                };
                httpRequest.send(JSON.stringify((responseData)));
            };
            fileReader.readAsDataURL(blob);
        });
    },
     showHideWaitingIndicator: function (show) {
         var waitingPopUp = document.getElementById('waiting-popup');
        var inActiveDiv = document.getElementById('popup-overlay');
        inActiveDiv.style.display = show ? 'block' : 'none';
        waitingPopUp.style.display = show ? 'block' : 'none';
    },
     showInsertFielddialog: function () {
      /*  var instance = this;
        if (document.getElementById('insert_merge_field') === null || document.getElementById('insert_merge_field') === undefined) {
            let fieldcontainer = document.createElement('div');
            fieldcontainer.id = 'insert_merge_field';
            document.body.appendChild(fieldcontainer);
            
                this.$refs.accessDialog.appendTo('#insert_merge_field');
            fieldcontainer.parentElement.style.position = 'fixed';
            fieldcontainer.style.width = 'auto';
            fieldcontainer.style.height = 'auto';
        }
        this.$refs.accessDialog.close = () => { this.$refs.doceditcontainer.ej2Instances.documentEditor.focusIn();};
        this.$refs.accessDialog.beforeOpen = () => { this.$refs.doceditcontainer.ej2Instances.documentEditor.focusIn();};
        this.$refs.accessDialog.show();*/
        this.$refs.accessDialog.show();
        let fieldNameTextBox = document.getElementById('field_text');
        fieldNameTextBox.value = '';
        
    },
     closeFieldDialog: function () {
        this.$refs.accessDialog.hide();
        this.$refs.doceditcontainer.ej2Instances.documentEditor.focusIn();
    },
   
     
    },
    mounted() {
        this.$nextTick(function () {
            
          var obj = this.$refs.doceditcontainer.ej2Instances.documentEditor;
          obj.open(JSON.stringify(<document-base64format>));
          obj.documentName='Mail Merge';
          this.$refs.doceditcontainer.ej2Instances.documentEditorSettings.showRuler = true;
          this.$refs.doceditcontainer.ej2Instances.documentChange = () => {
               this.$refs.accessDialog.hide();
            };
             this.$refs.accessDialog.hide();
             this.$refs.doceditcontainer.ej2Instances.documentEditor.focusIn();
           
                    
       });
       
        var items = this.toolbarItems;
          this.$refs.doceditcontainer.ej2Instances.toolbarItems = items;
        document.getElementById('de-flat-list').addEventListener('dragstart', (event) => {
            event.dataTransfer.setData('Text', event.target.innerText);
            event.target.classList.add('de-drag-target');
        });
        // Prevent default drag over for document editor element
        document.getElementById('container').addEventListener('dragover', (event) => {
            event.preventDefault();
        });

        // Drop Event for document editor element
        document.getElementById('container').addEventListener('drop', (e) => {
            var text = e.dataTransfer.getData('Text');
            this.$refs.doceditcontainer.ej2Instances.documentEditor.selection.select({ x: e.offsetX, y: e.offsetY, extend: false });
            this.insertField(text);
        });

        document.addEventListener('dragend', (event) => {
            if (event.target.classList.contains('de-drag-target')) {
                event.target.classList.remove('de-drag-target');
            }
        });
         
    }
     
    
};

</script>

```

### src/components/docx-editor-mail-merge/DocxEditorView.vue

```vue
<template>
  <DOCXEditor />
</template>

<script setup>
import DOCXEditor from '../../components/DOCXEditor.vue'
</script>
```
