In special cases where the timing of the use of the component is uncertain, or the component we want to use cannot be identified in the Vue template, we need to mount the component dynamically, or create and mount the component dynamically using runtime compilation.

Today we will take you from the actual project, look at the actual solution to customer problems, how to dynamically mount components, and show you a complete solution to the dynamic mount problem of the complete process.

Unresolvable “dynamic mount”

Our spreadsheet control, SpreadJS, has a function that when a user double-click a cell, an input box is displayed to edit the contents of the cell. Users can customize the input box according to the custom cell type specification and integrate any Form input type as required.

The input box is created and destroyed by inheriting the cell type mapping method, so there is a problem — this dynamic creation method cannot be configured in a VUE template and used directly.

Not long ago, a customer asked me if the custom cells of your control support Vue components such as ElementUI’s AutoComplete.

Due to the aforementioned problem:

After a long meditation, I replied to the customer seriously: “The component life cycle is inconsistent, can not use”, but then changed to say that we can use common components to solve the problem.

What about the problem? It worked out.

But this helpless “can not use”, but also become my midnight dream to cross the barrier these days.

Then, looking at the Vue documentation one day, I realized that the App was mounted to # App at runtime. In theory, other components should be able to dynamically mount to the Dom where they are needed, so the timing problem is solved.

The dynamic mounting function is enabled

Continuing with the documentation, the global apivue.extend (options) is created by extend. Vue instances can be mounted directly to DOM elements using the $mount method — which is exactly what we need.

<div id="mount-point"></div> // create constructor var Profile = vue.extend ({template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg'}}}) // Create a Profile instance and mount it to an element. new Profile().$mount('#mount-point')Copy the code

Create an AutoCompleteCellType according to the SpreadJS custom cell example and set it into the cell:

function AutoComplateCellType() { } AutoComplateCellType.prototype = new GC.Spread.Sheets.CellTypes.Base(); AutoComplateCellType.prototype.createEditorElement = function (context, cellWrapperElement) { // cellWrapperElement.setAttribute("gcUIElement", "gcEditingInput"); cellWrapperElement.style.overflow = 'visible' let editorContext = document.createElement("div") editorContext.setAttribute("gcUIElement", "gcEditingInput"); let editor = document.createElement("div"); / / custom cell editorContext as a container, need to create a child used to mount, cannot be directly mounted onto the editorContext editorContext. The appendChild (editor). return editorContext; } AutoComplateCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) { let width = cellRect.width > 180 ? cellRect.width : 180; If (editorContext) {// create constructor var Profile = vue.extend ({template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { return { firstName: 'Walter', lastName: 'White', alias: 'Heisenberg'}}}) // Create a Profile instance and mount it to an element. new Profile().$mount(editorContext.firstChild); }};Copy the code

Run, double-click to enter edit state, only to find an error

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.

According to the error message, we have two solutions at this time:

  1. Enable runtimeCompiler and add the runtimeCompiler: true configuration to vue.config.js to allow runtime compilation, which can dynamically generate templates to meet the needs of dynamic components
  2. Pre-compiled templates are only dynamically mounted, the components of AutoComplete are determined, and we can use this approach

Create an Autocomplete. vue component for dynamic mounting, which allows you to mount compiled components.

<template> <div> <p>{{ firstName }} {{ lastName }} aka {{ alias }}</p> </div> </template> <script> export default { data: function () { return { firstName: "Walter", lastName: "White", alias: "Heisenberg", }; }}; </script> import AutoComplate from './AutoComplate.vue' AutoComplateCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) { let width = cellRect.width > 180 ? cellRect.width : 180; If (editorContext) {// create constructor var Profile = vue.extend (AutoComplate); // Create a Profile instance and mount it to an element. new Profile().$mount(editorContext.firstChild); }};Copy the code

Double-click to enter edit state and see the contents of the component

In the next step, you need to set and obtain the editing content of the component for the custom cell. In this case, you can add props to the component and obtain the content of all props on the VueComponent instance created during mounting.

Update autocomplate. vue, add props, add input for editing

<template> <div> <p>{{ firstName }} {{ lastName }} aka {{ alias }}</p> <input type="text" v-model="value"> </div> </template> <script> export default { props:["value"], data: function () { return { firstName: "Walter", lastName: "White", alias: "Heisenberg", }; }}; </script>Copy the code

The VueComponent instance is stored through this.vm, and the Value is obtained and set to the VUE component in the getEditorValue and setEditorValue methods. After editing, destroy the dynamically created component by calling the $destroy() method.

AutoComplateCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) { let width = cellRect.width > 180 ? cellRect.width : 180; If (editorContext) {// Create constructor var Profile = vue.extend (MyInput); // Create a Profile instance and mount it to an element. this.vm = new Profile().$mount(editorContext.firstChild); }}; AutoComplateCellType. Prototype. GetEditorValue = function (editorContext) {/ / component set a default value if this. (vm) {return this.vm.value; }}; AutoComplateCellType.prototype.setEditorValue = function (editorContext, If (editorContext) {this.vm.value = value; }}; AutoComplateCellType. Prototype. DeactivateEditor = function (editorContext, context) {/ / destruction of this component. Vm. $destroy (); this.vm = undefined; };Copy the code

Now that the process is complete, just replace the input with ElementUI’s el-AutoComplete in Autocomplate. vue and implement the corresponding method.

The results of

Let’s see what happens.

In fact, dynamic mount is not a complex operation, understanding the Vue example, through the VM to operate the instance, flexible use of dynamic mount or runtime compiled components is not difficult.

The solution to all of this is in the beginning of the Vue tutorial, but the use of scaffolding and various tools makes us forget the original purpose of Vue and make simple problems more complicated.

This is the end of today’s sharing, we will bring you more serious and interesting content ~

Is there anything you “lose track of” in development?