Vue.js カスタムディレクティブ

カスタムディレクティブ

Vue.jsのカスタムディレクティブを作成して、フォーカス管理やバリデーション機能を実装します。

タスク

v-focusディレクティブ

要素に自動フォーカスを設定するディレクティブを作成する

v-highlightディレクティブ

条件に応じて要素をハイライトするディレクティブを作成する

v-validateディレクティブ

入力値のバリデーションを行うディレクティブを作成する

ヒント

v-focus、v-highlight、v-validateなどのカスタムディレクティブを作成して、DOM要素の動作をカスタマイズしましょう。

参考コード

// カスタムディレクティブの定義
const app = Vue.createApp({})

// v-focusディレクティブ - 要素に自動フォーカス
app.directive('focus', {
  mounted(el) {
    el.focus()
  },
  updated(el) {
    if (el.dataset.shouldFocus === 'true') {
      el.focus()
    }
  }
})

// v-highlightディレクティブ - 条件に応じてハイライト
app.directive('highlight', {
  mounted(el, binding) {
    if (binding.value) {
      el.classList.add('highlight')
    }
  },
  updated(el, binding) {
    if (binding.value) {
      el.classList.add('highlight')
    } else {
      el.classList.remove('highlight')
    }
  }
})

// v-validateディレクティブ - 入力値のバリデーション
app.directive('validate', {
  mounted(el, binding) {
    el.addEventListener('input', () => {
      validateField(el, binding.value)
    })
    el.addEventListener('blur', () => {
      validateField(el, binding.value)
    })
  },
  updated(el, binding) {
    validateField(el, binding.value)
  }
})

// バリデーション関数
function validateField(el, rules) {
  const value = el.value
  let isValid = true
  let errorMessage = ''
  
  // 必須チェック
  if (rules.required && !value.trim()) {
    isValid = false
    errorMessage = 'この項目は必須です'
  }
  
  // 最小文字数チェック
  if (rules.minLength && value.length < rules.minLength) {
    isValid = false
    errorMessage = `最低${rules.minLength}文字入力してください`
  }
  
  // 最大文字数チェック
  if (rules.maxLength && value.length > rules.maxLength) {
    isValid = false
    errorMessage = `最大${rules.maxLength}文字まで入力できます`
  }
  
  // 正規表現チェック
  if (rules.pattern && !rules.pattern.test(value)) {
    isValid = false
    errorMessage = rules.message || '入力形式が正しくありません'
  }
  
  // 結果を適用
  if (isValid) {
    el.classList.remove('error')
    el.classList.add('success')
    el.title = ''
  } else {
    el.classList.remove('success')
    el.classList.add('error')
    el.title = errorMessage
  }
  
  return isValid
}

// フォームコンテナコンポーネント
const FormContainer = {
  template: `
    <div class="form-container">
      <div class="form-group">
        <label>名前 (必須)</label>
        <input 
          v-model="form.name"
          v-focus
          v-validate="{ required: true, minLength: 2, maxLength: 20 }"
          placeholder="名前を入力してください"
        >
      </div>
      
      <div class="form-group">
        <label>メールアドレス (必須)</label>
        <input 
          v-model="form.email"
          v-validate="{ 
            required: true, 
            pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/, 
            message: '有効なメールアドレスを入力してください' 
          }"
          placeholder="example@email.com"
        >
      </div>
      
      <div class="form-group">
        <label>電話番号</label>
        <input 
          v-model="form.phone"
          v-validate="{ pattern: /^[0-9-]{10,}$/, message: '有効な電話番号を入力してください' }"
          placeholder="090-1234-5678"
        >
      </div>
      
      <div class="form-group">
        <label>メッセージ</label>
        <textarea 
          v-model="form.message"
          v-highlight="form.message.length > 100"
          v-validate="{ maxLength: 500 }"
          rows="4"
          placeholder="メッセージを入力してください(最大500文字)"
        ></textarea>
      </div>
      
      <div class="button-group">
        <button @click="submitForm">送信</button>
        <button @click="resetForm" class="secondary">リセット</button>
        <button @click="focusFirstField" class="secondary">最初のフィールドにフォーカス</button>
      </div>
      
      <div class="directive-info">
        <h4>使用しているカスタムディレクティブ</h4>
        <ul>
          <li><strong>v-focus</strong>: 要素に自動フォーカスを設定</li>
          <li><strong>v-highlight</strong>: 条件に応じて要素をハイライト(メッセージが100文字を超えると黄色背景)</li>
          <li><strong>v-validate</strong>: 入力値のバリデーション(必須、文字数、正規表現チェック)</li>
        </ul>
      </div>
    </div>
  `,
  
  data() {
    return {
      form: {
        name: '',
        email: '',
        phone: '',
        message: ''
      }
    }
  },
  
  methods: {
    submitForm() {
      // フォーム送信処理
      console.log('フォーム送信:', this.form)
      alert('フォームが送信されました!')
    },
    
    resetForm() {
      this.form = {
        name: '',
        email: '',
        phone: '',
        message: ''
      }
    },
    
    focusFirstField() {
      // 最初のフィールドにフォーカス
      const firstInput = document.querySelector('input')
      if (firstInput) {
        firstInput.focus()
      }
    }
  }
}

// コンポーネントを登録
app.component('form-container', FormContainer)

// マウント
app.mount('#app')

プレビュー

見本