<html>
<!–
<script src=“https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js” integrity=“sha512-GZ1RIgZaSc8rnco/8CXfRdCpDxRCphenIiZ2ztLy3XQfCbQUSCuk8IudvNHxkRA3oUg6q0qejgN/qqyG1duv5Q==” crossorigin=“anonymous”></script>
–>
</html>
import { computed, nextTick, onMounted, reactive, watch } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.esm-browser.js'; import * as Util from 'https://wws.jp/_export/code/vuejs/vue3/form_utils?codeblock=0'; const Ace9Form = { template: ` <div :class="computedControlWrapperClasses" :style="controlWrapperStyles"> <pre :id="state.editorId" :style="editorStyle"></pre> <div :class="computedMessageWrapperClasses" :style="messageWrapperStyles"> <template v-for="msg in errors"> <span>{{ msg }}</span><br> </template> </div> </div> `, props: { value: { type: String }, type: { type: String, default: 'text' }, height: { type: String, default: '300' }, errors: { type: Array, default: [] }, controlWrapperClasses: { type: Array, default: [] }, controlWrapperStyles: { type: Object, default: {} }, controlClasses: { type: Array, default: [] }, controlStyles: { type: Object, default: {} }, messageWrapperClasses: { type: Array, default: [] }, messageWrapperStyles: { type: Object, default: {} }, controlErrorClass: { type: String, default: 'has-error' }, messageErrorClass: { type: String, default: 'text-danger' }, }, setup(props, context) { const state = reactive({ editorId: 'ace' + Util.getUniqStr(), editor: null }) const computedControlWrapperClasses = computed(() => { return Util.getWrapClass( props.errors, props.controlWrapperClasses, props.controlErrorClass ) }) const computedMessageWrapperClasses = computed(() => { return Util.getWrapClass( props.errors, props.messageWrapperClasses, props.messageErrorClass ) }) const editorStyle = computed(() => { return { position: 'relative', overflow: 'hidden', font: '12px/normal Monaco, Menlo, Ubuntu Mono, Consolas, source-code-pro, monospace', direction: 'ltr', textAlign: 'left', '-webkit-tap-highlight-color': 'rgba(0, 0, 0, 0)', height: props.height + 'px' } }) const ace9src = "https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"; const crossorigin = "anonymous"; onMounted(() => Util.appendScript(ace9src, null, crossorigin, initAce9)) function initAce9() { state.editor = ace.edit(state.editorId); state.editor.setTheme("ace/theme/crimson_editor"); state.editor.setValue(props.value); state.editor.session.setUseSoftTabs(true); state.editor.session.setTabSize(2); /* https://ace.c9.io/#nav=api&api=editor */ state.editor.on('blur', () => { context.emit('update:value', state.editor.getValue()) }); /* 入力中確定前文字列のフォントサイズを調整 */ const styleSheetElement = document.createElement('style'); const cssRuleString = ` #${state.editorId} textarea:focus { font-size: 12px; border: none; }`; document.head.appendChild(styleSheetElement); const sheet = styleSheetElement.sheet; sheet.insertRule(cssRuleString, sheet.cssRules.length); } watch(props, (newProp, oldProp) => { state.editor.setValue(newProp.value); setLang(newProp.type) }) function setLang(type) { if (type === 'json') { state.editor.session.setMode("ace/mode/json"); state.editor.session.setUseSoftTabs(true); state.editor.session.setTabSize(2); state.editor.session.setUseSoftTabs(true); } else if (type === 'html') { state.editor.session.setMode("ace/mode/html"); state.editor.session.setUseSoftTabs(true); state.editor.session.setTabSize(2); state.editor.session.setUseSoftTabs(true); } else if (type === 'css') { state.editor.session.setMode("ace/mode/css"); state.editor.session.setUseSoftTabs(true); state.editor.session.setTabSize(2); state.editor.session.setUseSoftTabs(true); } else if (type === 'javascript') { state.editor.session.setMode("ace/mode/javascript"); state.editor.session.setUseSoftTabs(true); state.editor.session.setTabSize(2); state.editor.session.setUseSoftTabs(true); } else if (type === 'php') { state.editor.session.setMode("ace/mode/php"); state.editor.session.setUseSoftTabs(true); state.editor.session.setTabSize(4); state.editor.session.setUseSoftTabs(true); } else { state.editor.session.setMode("ace/mode/text"); state.editor.session.setUseSoftTabs(true); state.editor.session.setTabSize(4); state.editor.session.setUseSoftTabs(true); } } return { state, computedControlWrapperClasses, computedMessageWrapperClasses, editorStyle } } }; export default Ace9Form;
<html><style>th, td { padding: 12px !important }</style></html>
| Property | Type | Default | Description |
|---|---|---|---|
| value | String | v-model プロパティとして value を持つ | |
| controlWrapperClasses | Array | [] | textarea を囲う div タグに反映するクラス |
| controlWrapperStyles | Object | {} | textarea を囲う div タグに反映するスタイル |
| controlClasses | Array | [] | 入力コントロールに反映するクラス |
| controlStyles | Object | {} | 入力コントロールに反映するスタイル |
| messageWrapperClasses | Array | [] | エラーメッセージを囲う div タグに反映するクラス |
| errors | Array | [] | エラーメッセージの配列 |
| controlErrorClass | String | 'has-error' | コントロールに反映するエラークラス |
| messageErrorClass | String | 'text-danger' | エラーメッセージに反映するエラークラス |
<html><style>th, td { padding: 12px !important }</style></html>
| Event | Value |
|---|---|
| update:value | 選択された値 |
<html>
<ace9-form :height="height" :type="type" :control-wrapper-classes="['col-sm-12', 'mb-5']" :errors="errors" v-model:value="boundText" > </ace9-form>
<ace9-form :height="height" :type="type" :control-wrapper-classes="['col-sm-12', 'mb-5']" :errors="errors" v-model:value="boundText" > </ace9-form>
<textarea-form placeholder="placeholder text" :control-wrapper-classes="['col-sm-12']" v-model:value="boundText" > </textarea-form>
<div>
<div class="col-sm-3">
<input type="number" v-model="height">
</div>
<div class="col-sm-3">
<select v-model="type">
<option>text</option>
<option>html</option>
<option>css</option>
<option>javascript</option>
<option>json</option>
<option>php</option>
</select>
</div>
<div class="col-sm-6">
<button @click="appendError">AppendError</button>
<button @click="clearError">ClearError</button>
</div>
</div>
<script type=“module”>
import { createApp } from 'https://cdnjs.cloudflare.com/ajax/libs/vue/3.0.2/vue.esm-browser.js';
import TextareaForm from '/_export/code/vuejs/vue3/textarea_form?codeblock=0';
import Ace9Form from '/_export/code/vuejs/vue3/ace9_form?codeblock=0';
const App = {
components: {
TextareaForm: TextareaForm,
Ace9Form: Ace9Form
},
data() {
return {
boundText: 'あいうえお',
height: '200',
type: 'text',
errors: []
}
},
methods: {
clearError() {
this.errors.splice(0, 1);
},
appendError() {
this.errors.splice(0, 0, 'error message B')
}
}
}
createApp(App).mount('#app')
</script>
</html>