Vue로 양식 구성 요소에 맞서기

소개



어떤 웹 서비스에도 "양식"이 필요하다고 생각합니다.
「문의 폼」이나 「신규 회원 등록 폼」이나 「탈퇴 폼」등…
그 밖에도 마케팅의 시책으로, LP로부터의 비행처로서 회원 획득을 위한 폼을 여러가지 만들 수도 있을지도 모릅니다.

저도 웹 업계에 들어가 많은 양식을 만들어 왔습니다.
그리고 어느 때 문득 이렇게 생각했습니다.
「이것, 폼을 컴퍼넌트화해 버리면 편해질까요… 」

그렇게 생각해 버리면, 이제 엔지니어의 성으로서 실행하지 않고 있을 수 없습니다.
내가 할거야.

우선은, 아래와 같은 간단한 문의 폼의 컴퍼넌트화를 생각해 갑니다.
프레임워크는 경량 Vue+TypeScript를 선택했습니다.



이 양식의 사양을 간략하게 설명하면,
  • 전자 메일 주소, 전화 번호에 유효성 검사 설정
  • 밸리데이션에 걸리면 에러 메시지를 표시한다
  • 유효성 검사를 통과하면 '필수'라는 레이블을 'OK'로 변경합니다.

    같은 곳입니다.

    구성 요소의 입도 어떻게?



    컴퍼넌트를 만들려고 했을 때에 고민해 버리는 것이, 컴퍼넌트의 입도.
    어떻게 정리하는 것이 좋다고 생각해도 어쩔 수 없기 때문에, 여기는 기존의 시스템에 탑니다.
    아직도 역시, 이용하는 것은 「Atomic Design」입니까.
    Atomic Design에 대해서 간단히 설명하면, 입도마다 「Atoms」 「Molecules」 「Organisms」라고 하는 것처럼 분류하는 녀석입니다만, 자세한 것은 하기 링크등을 참고로 해 주세요.
    Atomic Design을 알게 될거야.

    이번 컴포넌트화하는 폼을 분류하면 아래와 같이 될까 생각합니다.



    이것을 소스에 떨어뜨려 갑니다.

    프로그램 작성



    우선은 빨리 어플리케이션의 기동에 필요한 코드를 써 갑니다.
    덧붙여 앞으로의 소스는, 형편상 여러가지 간략화하면서 써 있으므로, 그대로 복사해도 움직이지 않습니다.
    분위기를 느껴보세요.

    application.ts
    import Vue from 'vue';
    import App from './App.vue';
    import router from './_router';
    import store from './_store';
    
    Vue.config.productionTip = false;
    
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount('#app');
    

    store는 나중에 사용하기 때문에 어쩌면 import 해 둡니다.

    App.vue
    <template>
      <div id="app">
        <Form />
      </div>
    </template>
    
    <script lang="ts">
      import { Component, Prop, Vue } from 'vue-property-decorator';
      import Form from './components/organisms/Form.vue';
    
      @Component({
        components: {
          Form
        }
      })
    
      export default class App extends Vue {}
    </script>
    

    여기서 마침내 Orgamisms가 등장합니다.
    또한 vue-property-decorator라는 플러그인을 사용하고 있으므로 그쪽에 관해서는 아래를 참조하십시오.
    처음의 vue-property-decorator (nuxt에도 대응)

    form.vue
    <template>
      <div>
        <div v-for="order in this.$store.state.config.order" :key="order.type">
          <FormBox :config="order" />
        </div>
      </div>
    </template>
    
    <script lang="ts">
      import { Component, Vue } from 'vue-property-decorator';
      import FormBox from '../../components/molecules/FormBox.vue';
    
      @Component({
        components: {
          FormBox
        }
      })
    
      export default class Form extends Vue {};
    </script>
    

    여기서 갑자기 store라든지 나오고 있습니다만, placeholder라든지 input의 타입이라든지 한 곳에 정리해 두는 편이 즐거울 것 같기 때문에 그렇게 하고 있습니다.

    _store.ts
    import Vue from 'vue';
    import Vuex, { MutationTree } from 'vuex';
    
    Vue.use(Vuex);
    
    type Order = {
      title: string, //FormTitleに入れる文字列
      type: string,  //inputのタイプ
      note: string,  //FormNoteに入れる文字列
      placeholder: string,
      name: string,
      id: string,
      maxlength: number,
      required: boolean
    }
    
    type State = {
      order: order[];
    }
    

    이 정보를 Molecules의 폼의 행을 생성하고 있는 「FormBox」에 건네주고 있으므로, 나머지는 그것에 따라서 Atoms를 묘화해 갈 뿐입니다.
    Atoms의 예↓

    FormTitle.vue
    <template>
      <label>
        <slot></slot>
      </label>
    </template>
    
    <script lang="ts">
      import { Component, Vue } from 'vue-property-decorator';
    
      @Component
      export default class FormLabel extends Vue {}
    </script>
    

    TextInput.vue
    <template>
      <div>
        <input :placeholder="placeholder" :name="name" :id="id" :maxlength="maxlength" @input=onInput>
        <p>{{ error }}</p>
      </div>
    </template>
    
    <script lang="ts">
      import { Component, Prop, Vue } from 'vue-property-decorator';
    
      export default class TextInput extends Vue {
        @Prop() type!: string;
        @Prop() placeholder!: string;
        @Prop() name!: string;
        @Prop() id!: string;
        @Prop() maxlength!: number;
        @Prop() isRequired!: boolean;
    
        private onInput(event: InputEvent) {
          const type: string = this.type;
          const inputText: string = event.target.value;
    
          this.inputValid(type, inputText);
        }
    
        private inputValid(type: string, text: string) {
          switch(type) {
            case 'name':
              this.nameValidate(text);
              break;
            case 'phoneticName':
              this.phoneticNameValidate(text);
              break;
            case 'email':
              this.emailValidate(text);
              break;
            case 'tel':
              this.telValidate(text);
              break;
            default:
              break;
          }
        }
        省略
      }
    </script>
    

    요약



    전체상은 쫓아가도록 썼습니다만, 어떻습니까.
    컴포넌트화를 진행하는데 있어서는 아래가 중요할까 생각합니다.
  • 어떤 디자인 시스템을 타고 싶을지 결정하십시오
  • 구성 요소의 입자 크기는 설계 시스템에서 흔들리지 않도록합니다

  • 이상입니다.
    계속해서 Vue의 지견으로 공유할 수 있는 것이 있으면, 투고해 나갈 예정입니다.

    좋은 웹페이지 즐겨찾기