Integrating and Wrapping Monaco Editor in Vue 2
Basic Integration of Monaco Editor in Vue 2
Start by installing the requirde packages:
npm install monaco-editor monaco-editor-webpack-plugin
Configure vue.config.js to include the Monaco Webpack plugin:
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new MonacoWebpackPlugin()
]
}
};
In your component template, add a container element with explicit dimensions:
<div id="editor-host" style="height: 300px;"></div>
Then initialize the editor in your Vue component:
<template>
<div>
<div id="editor-host" style="height: 300px;"></div>
</div>
</template>
<script>
import * as monaco from 'monaco-editor';
export default {
data() {
return {
editorConfig: {
value: '',
language: 'javascript',
theme: 'vs-dark',
autoIndent: true,
readOnly: false
}
};
},
mounted() {
const hostElement = document.getElementById('editor-host');
this.editorInstance = monaco.editor.create(hostElement, this.editorConfig);
}
};
</script>
Creating a Resuable Vue Wrapper Component
Create a dedicated component (MonacoEditor.vue) that encapsulates the editor logic and supports reactive props:
<template>
<div :style="{ height, width }" class="monaco-wrapper"></div>
</template>
<script>
import * as monaco from 'monaco-editor';
export default {
name: 'MonacoEditor',
props: {
modelValue: {
type: String,
default: ''
},
height: {
type: String,
default: '300px'
},
width: {
type: String,
default: '100%'
},
options: {
type: Object,
default: () => ({})
}
},
data() {
return {
baseConfig: {
value: this.modelValue,
language: 'javascript',
theme: 'vs-dark',
autoIndent: true,
readOnly: false
}
};
},
computed: {
mergedOptions() {
return { ...this.baseConfig, ...this.options };
}
},
mounted() {
this.initEditor();
},
methods: {
initEditor() {
this.editor = monaco.editor.create(this.$el, this.mergedOptions);
this.editor.onDidChangeModelContent(() => {
this.$emit('update:modelValue', this.editor.getValue());
});
},
resize() {
this.$nextTick(() => {
if (this.editor) this.editor.layout();
});
}
},
watch: {
height: 'resize',
width: 'resize',
options: {
handler() {
this.$nextTick(() => {
if (this.editor) {
this.editor.updateOptions(this.mergedOptions);
}
});
},
deep: true
},
modelValue(newVal) {
if (this.editor && this.editor.getValue() !== newVal) {
this.editor.setValue(newVal);
}
}
},
beforeUnmount() {
if (this.editor) {
this.editor.dispose();
}
}
};
</script>
Use the wrapper component in a parent view:
<template>
<div>
<el-form>
<el-form-item label="Height">
<el-input v-model="inputHeight" @change="v => editorHeight = v" />
</el-form-item>
<el-form-item label="Width">
<el-input v-model="inputWidth" @change="v => editorWidth = v" />
</el-form-item>
</el-form>
<MonacoEditor
v-model="codeContent"
:height="editorHeight"
:width="editorWidth"
:options="editorOptions"
/>
</div>
</template>
<script>
import MonacoEditor from '@/components/MonacoEditor.vue';
export default {
components: { MonacoEditor },
data() {
return {
codeContent: 'console.log("hello");',
inputHeight: '100px',
inputWidth: '500px',
editorHeight: '100px',
editorWidth: '500px',
editorOptions: {}
};
},
mounted() {
setTimeout(() => {
this.editorOptions = { fontSize: 40 };
}, 2000);
}
};
</script>