<html>
<!–
<script src=“https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js” integrity=“sha512-GZ1RIgZaSc8rnco/8CXfRdCpDxRCphenIiZ2ztLy3XQfCbQUSCuk8IudvNHxkRA3oUg6q0qejgN/qqyG1duv5Q==” crossorigin=“anonymous”></script>
–>
</html>

Vue.js 3.x Ace9 Form コンポーネント

  • Ace9 Editor のエラーメッセージ表示付きコンポーネント
  • v-model 対応
Ace9Form.js
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>

<html><style>
h2.page-header {

font-weight: 600;
font-size: 2.4rem;
position: relative;
padding: 10px 0 10px 16px;
margin-top: 3rem !important;
margin-bottom: 2rem !important;

}
h2:before {

  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 6px;
  height: 100%;
  background: #ccc;

}
div#disqus__thread {

width: 100% !important;

}
</style></html>

  • vuejs/vue3/ace9_form.txt
  • 最終更新: 2024/12/18 02:32
  • by 127.0.0.1