티스토리 뷰

** 2021-04-16

1.0.6 버전 부터 ag grid 를 지원하도록 기능을 추가하였습니다.

---------------------------------------------------------------------------------------------------------------------------------

 

안녕하세요. 오늘은 npm package 하나를 소개해 드리겠습니다.

 

우선 다음의 예제를 vuetify 로 구현해 보겠습니다.

 

 

* Vuetify 코드

<template>
  <v-form ref="form" v-model="valid" lazy-validation>
    <v-text-field v-model="name" :counter="10" :rules="nameRules" label="Name" required/>
    <v-text-field v-model="email" :rules="emailRules" label="E-mail" required/>
    <v-select v-model="select" :items="items" :rules="[v => !!v || 'Item is required']" 
      label="Item" required/>
    <v-checkbox v-model="checkbox" :rules="[v => !!v || 'You must agree to continue!']"
      label="Do you agree?" required/>
    <v-btn :disabled="!valid" color="success" class="mr-4" @click="validate">
      Validate
    </v-btn>
    <v-btn color="error" class="mr-4" @click="reset">
      Reset Form
    </v-btn>
    <v-btn color="warning" @click="resetValidation">
      Reset Validation
    </v-btn>
  </v-form>
</template>
<script>
  export default {
    data: () => ({
      valid: true,
      name: '',
      nameRules: [
        v => !!v || 'Name is required',
        v => (v && v.length <= 10) || 'Name must be less than 10 characters',
      ],
      email: '',
      emailRules: [
        v => !!v || 'E-mail is required',
        v => /.+@.+\..+/.test(v) || 'E-mail must be valid',
      ],
      select: null,
      items: [
        'Item 1',
        'Item 2',
        'Item 3',
        'Item 4',
      ],
      checkbox: false,
    }),

    methods: {
      validate () {
        this.$refs.form.validate()
      },
      reset () {
        this.$refs.form.reset()
      },
      resetValidation () {
        this.$refs.form.resetValidation()
      },
    },
  }
</script>

다음은 제가 만든 v-iterator 를 사용한 코드입니다.

<template>
  <v-iterator :dynamicArg="screen" :data="$data" @validate="validate" @reset="reset" @resetValidation="resetValidation"/>
</template>

export default {
  computed: {
    screen () {
      return {
        items: [
          { component: 'form', ref: 'form', model: 'valid', lazyValidation: true, items: [
            { component: 'txtfld', model: 'name', couter: '10', rules: ['requiredRule', 'nameRule'], label: 'Name', required: true },
            { component: 'txtfld', model: 'email', rules: ['requiredRule', 'emailRule'], label: 'E-mail', required: true },
            { component: 'select', model: 'select', items: 'items', rules: ['requiredRule'], label: 'Item', required: true },
            { component: 'chk', model: 'checkbox', rules: ['requiredRule'], label: 'Do you agree?', required: true },
            { component: 'btn', disabled: 'isNotValid', color: 'success', class: 'mr-4', itemtext: 'Validate',
              evnts: [{event: 'click', method: 'validate'}]
            },
            { component: 'btn',  color: 'error', class: 'mr-4', itemtext: 'Reset Form',
              evnts: [{event: 'click', method: 'reset'}]
            },
            { component: 'btn', color: 'warning', class: 'mr-4', itemtext: 'Reset Validation',
              evnts: [{event: 'click', method: 'resetValidation'}]
            }
          ]}
        ]
      }
    }
  },
  data: () => ({
    valid: true,
    isNotValid: false,
    name: '',
    requiredRule: v => !!v || 'Required',
    nameRule: v => (v && v.length <= 10) || 'Name must be less than 10 characters',
    email: '',
    emailRule: v => /.+@.+\..+/.test(v) || 'E-mail must be valid',
    select: null,
    items: [
      'Item 1',
      'Item 2',
      'Item 3',
      'Item 4',
    ],
    checkbox: false,
  }),
  watch: {
    valid (value) {
      this.isNotValid = !value
    }
  },
  methods: {
    validate() {
      this.$refs.code.getRef('form').validate()
    },
    reset() {
      this.$refs.code.getRef('form').reset()
    },
    resetValidation() {
      this.$refs.code.getRef('form').resetValidation()
    },
  },
}

보시다시피 가장 큰 차이는 <template> 코드는 줄고, screen 구성을 <script> 영역에서 구현할 수 있다는 점입니다.

 

<template> 에서 화면 구성에 변화를 줄 경우 가능한 방법은 v-if, v-show 정도이지만, v-iterator 를 사용할 경우 <template>은 변화없이 <script> 내의 data, computed, method 또는 별도의 function , js 등 다양한 방법으로 구현 할 수 있습니다.

 

그럼 위 코드를 다시 변형시켜보겠습니다.

* screen.js

export default {
  items: [
    { component: 'form', ref: 'form', model: 'valid', lazyValidation: true, items: [
      { component: 'txtfld', model: 'name', couter: '10', rules: ['requiredRule', 'nameRule'], label: 'Name', required: true },
      { component: 'txtfld', model: 'email', rules: ['requiredRule', 'emailRule'], label: 'E-mail', required: true },
      { component: 'select', model: 'select', items: 'items', rules: ['requiredRule'], label: 'Item', required: true },
      { component: 'chk', model: 'checkbox', rules: ['requiredRule'], label: 'Do you agree?', required: true },
      { component: 'btn', disabled: 'isNotValid', color: 'success', class: 'mr-4', itemtext: 'Validate',
        evnts: [{event: 'click', method: 'validate'}]
      },
      { component: 'btn',  color: 'error', class: 'mr-4', itemtext: 'Reset Form',
        evnts: [{event: 'click', method: 'reset'}]
      },
      { component: 'btn', color: 'warning', class: 'mr-4', itemtext: 'Reset Validation',
        evnts: [{event: 'click', method: 'resetValidation'}]
      }
    ]}
  ]
}

* form.vue

<template>
  <v-iterator :dynamicArg="screen" :data="$data" @validate="validate" @reset="reset" @resetValidation="resetValidation"/>
</template>

const screen = require(./screen.js)
export default {
  data: () => ({
    valid: true,
    isNotValid: false,
    name: '',
    requiredRule: v => !!v || 'Required',
    nameRule: v => (v && v.length <= 10) || 'Name must be less than 10 characters',
    email: '',
    emailRule: v => /.+@.+\..+/.test(v) || 'E-mail must be valid',
    select: null,
    items: [
      'Item 1',
      'Item 2',
      'Item 3',
      'Item 4',
    ],
    checkbox: false,
  }),
  watch: {
    valid (value) {
      this.isNotValid = !value
    }
  },
  methods: {
    validate() {
      this.$refs.code.getRef('form').validate()
    },
    reset() {
      this.$refs.code.getRef('form').reset()
    },
    resetValidation() {
      this.$refs.code.getRef('form').resetValidation()
    },
  },
}

위와 같이 screen 구성을 별도의 js 로 만들어서 사용도 가능하며, json 으로 저장 후 변환하여 사용하는 것도 가능합니다.

 

◆ 이렇게 할 경우 몇가지 장점이 있습니다.

1. 화면 구성을 DB나 별도 파일로 저장이 가능합니다.

  - <template> 에 화면 구성을 코딩한 경우 변경되면 다시 빌드해야 하지만, 별도의 DB나 파일로 저장하게 되면 빌드 없이 화면을 변경 할 수 있습니다.

2. 가독성 증가

  - data object 구조로 구성되므로 template 문법에 익숙하지 않아도 좀 더 쉽게 접근 가능합니다.

3. 유지보수성 증가

  - vuetify를 그대로 사용하는 경우 vuetify가 업데이트 되는 경우 전체 template 코드에서 변경 부분을 확인해야 하지만, v-iterator를 사용하는 경우 vuetify 변경사항을 대신 적용하므로 v-iterator update로 변경 영향도를 최소화하면서 변경이 가능합니다.

 

◆ v-iterator에서는 몇가지 편의기능도 지원합니다.

1. v-currency

 - 금액 입력만을 위한 v-currency를 내장하고 있습니다. (v-text-field 기반)

2. component 단축어 지원

 - v-autocomplete > aut, v-bottom-navigation > bnavi 등 단축어를 지원합니다.

3. i18n

 - i18n을 사용하여 다국어 지원을 하는 경우 일반적으로 다음과 같이 사용합니다.

$t('common.confirm')

v-iterator에서는 $t 없이 'common.confirm' 으로 입력하여 사용합니다.

 

◆ 설치방법

v-iterator에서는 vue, vuetify, lodash 가 필수로 필요하며 우선 아래의 링크를 참고하여 설치환경을 구성합니다.

vuetifyjs.com/en/getting-started/installation/

Get started with Vuetify

Get started with Vue and Vuetify in no time. Support for Vue CLI, Webpack, Nuxt and more.

vuetifyjs.com

lodash.com/

Lodash

_.defaults({ 'a': 1 }, { 'a': 3, 'b': 2 });_.partition([1, 2, 3, 4], n => n % 2);DownloadLodash is released under the MIT license & supports modern environments. Review the build differences & pick one that’s right for you.InstallationIn

lodash.com

lodash까지 설치가 되었으면, v-iterator를 설치합니다.

npm i v-iterator --save

 

v-iterator를 전역으로 사용하는 경우 main.js 에서 import 구문을 추가합니다.

import Vue from 'vue'
import App from './App.vue'
import vuetify from './plugins/vuetify'

// import and set v-iterator
import {VIterator} from 'v-iterator'
Vue.component(VIterator.name, VIterator)

new Vue({
  vuetify,
  render: h => h(App)
}).$mount('#app')

그 다음 vue.config.js 에서 runtimeCompiler를 true 로 설정합니다.

module.exports = {
  runtimeCompiler: true
}

◆ 사용 방법

props 나 event, class 는 vuetify에서 제공하는 것과 동일하게 입력 가능합니다. 단, event의 경우 <template> 에는 component event 가 호출하는 method를 v-on:method명="method명" 과 같이 입력합니다.

  <template>
    <v-iterator :dynamicArg="screen" :data="$data" @btnClick="btnClick">
  </template>
  <script>
    export default {
      data: () => ({
        screen: {
          items: [
            { component: 'container', fluid: true, items: [
              { component: 'btn', color: 'grey lighten-3', itemtext: 'Block Button', style: 'width: 100%;', 
                evnts: [{ event: 'click', method: 'btnClick'}]
              },
              { component: 'card', flat: true, items: [
                { component: 'card-text', itemtext: 'textData' }
              ]}
            ]}
          ]
        },
        textData: 'Button clicked!',
        clickedCount: 0
      }),
      methods: {
        btnClick () {
          this.clickedCount++
          this.textData = String(this.textData).concat(': ', this.clickedCount)
        }
      }
    }
  </script>

 

* 지원 component 명 (축약어 포함)

  agGrid: 'AG Grid',
  agd: 'AG Grid',
  alert: 'Alert',
  alt: 'Alert',
  appBar: 'AppBar',
  appbar: 'AppBar',
  apb: 'AppBar',
  autocomplete: 'Autocomplete',
  aut: 'Autocomplete',
  avatar: 'Avatar',
  ava: 'Avatar',
  badge: 'Badge',
  bge: 'Badge',
  banner: 'Banner',
  bnr: 'Banner',
  bottomNavigation: 'BottomNavigation',
  bnavi: 'BottomNavigation',
  btmnav: 'BottomNavigation',
  bottomSheet: 'BottomSheet',
  bsheet: 'BottomSheet',
  btmsht: 'BottomSheet',
  breadcrumbs: 'Breadcrumbs',
  bread: 'Breadcrumbs',
  brd: 'Breadcrumbs',
  breadcrumbsItem: 'BreadcrumbsItem',
  breadItem: 'BreadcrumbsItem',
  breaditm: 'BreadcrumbsItem',
  brditm: 'BreadcrumbsItem',
  button: 'Button',
  btn: 'Button',
  buttonToggle: 'ButtonToggle',
  btnToggle: 'ButtonToggle',
  btntog: 'ButtonToggle',
  card: 'Card',
  crd: 'Card', 
  cardTitle: 'CardTitle',
  crdtle: 'CardTitle',
  cardSubtitle: 'CardSubtitle',
  crdsut: 'CardSubitle',
  cardText: 'CardText',
  crdtxt: 'CardText',
  cardActions: 'CardActions',
  crdact: 'CardActions',
  calendar: 'Calendar',
  caledr: 'Calendar',
  cal: 'Calendar',
  carousel: 'Carousel',
  car: 'Carousel',
  carouselItem: 'CarouselItem',
  caritm: 'CarouselItem',
  carouselTranstion: 'CarouselTranstion',
  cartrn: 'CarouselTranstion',
  carouselReverseTranstion: 'CarouselReverseTranstion',
  carrevtrn: 'CarouselReverseTranstion',
  chip: 'Chip',
  chp: 'Chip',
  chipGroup: 'ChipGroup',
  chpgrp: 'ChipGroup',
  checkbox: 'Checkbox',
  chk: 'Checkbox',
  col: 'Col',
  combobox: 'Combobox',
  com: 'Combobox',
  container: 'Container',
  contai: 'Container',
  currency: 'Currency',
  cur: 'Currency',
  dataIterator: 'DataIterator',
  dtaIte: 'DataIterator',
  dataFooter: 'DataFooter',
  dtafoo: 'DataFooter',
  datePicker: 'DatePicker',
  date: 'DatePicker',
  dte: 'DatePicker',
  dtepik: 'DatePicker',
  dialog: 'Dialog',
  dia: 'Dialog',
  dialogTranstion: 'DialogTranstion',
  diatrn: 'DialogTranstion',
  dialogTopTranstion: 'DialogTopTranstion',
  diatoptrn: 'DialogTopTranstion',
  dialogBottomTranstion: 'DialogBottomTranstion',
  diabottrn: 'DialogBottomTranstion',
  divider: 'Divider',
  dvd: 'Divider',
  div: 'Div',
  expansionPanels: 'ExpansionPanels',
  expanels: 'ExpansionPanels',
  exppans: 'ExpansionPanels',
  expansionPanel: 'ExpansionPanel',
  expanel: 'ExpansionPanel',
  exppan: 'ExpansionPanel',
  expansionPanelHeader: 'ExpansionPanelHeader',
  expanelh: 'ExpansionPanelHeader',
  exppanhdr: 'ExpansionPanelHeader',
  expansionPanelContent: 'ExpansionPanelContent',
  expanelc: 'ExpansionPanelContent',
  exppancon: 'ExpansionPanelContent',
  expandTransition: 'ExpandTransition',
  exptrn: 'ExpandTransition',
  fadeTransition: 'FadeTransition',
  fadtrn: 'FadeTransition',
  fileInput: 'FileInput',
  file: 'FileInput',
  fle: 'FileInput',
  form: 'Form',
  frm: 'Form',
  footer: 'Footer',
  fter: 'Footer',
  ftr: 'Footer',
  hover: 'Hover',
  hovr: 'Hover',
  hvr: 'Hover',
  icon: 'Icon', 
  icn: 'Icon',
  image: 'Image', 
  img: 'Image',
  input: 'Input',
  in: 'Input',
  inp: 'Input',
  item: 'Item',
  itm: 'Item',
  itemGroup: 'ItemGroup',
  itmgrp: 'ItemGroup',
  lazy: 'Lazy',
  laz: 'Lazy',
  list: 'List',
  lst: 'List',
  listGroup: 'ListGroup',
  lstgrp: 'ListGroup',
  listItem: 'ListItem',
  lstitm: 'ListItem',
  listItemTitle: 'ListItemTitle',
  lstitmtle: 'ListItemTitle',
  listItemSubtitle: 'ListItemSubtitle',
  lstitmsut: 'ListItemSubtitle',
  listItemAction: 'ListItemAction',
  lstitmact: 'ListItemAction',
  listItemActionText: 'ListItemActionText',
  lstitmacttxt: 'ListItemActionText',
  listItemAvatar: 'ListItemAvatar',
  lstitmava: 'ListItemAvatar',
  listItemContent: 'ListItemContent',
  lstitmcon: 'ListItemContent',
  listItemIcon: 'ListItemIcon',
  lstitmicn: 'ListItemIcon',
  listItemGroup: 'ListItemGroup',
  lstitmgrp: 'ListItemGroup',
  menu: 'Menu',
  meu: 'Menu',
  navigationDrawer: 'NavigationDrawer',
  navdrw: 'NavigationDrawer',
  navidrw: 'NavigationDrawer',
  overflow: 'OverflowButton',
  overbtn: 'OverflowButton',
  overlay: 'Overlay',
  ovl: 'Overlay',
  pagination: 'Pagination',
  page: 'Pagination',
  pag: 'Pagination',
  parallax: 'Parallax',
  par: 'Parallax',
  pax: 'Parallax',
  progressLinear: 'ProgressLinear',
  progressL: 'ProgressLinear',
  prglin: 'ProgressLinear',
  progressCircular: 'ProgressCircular',
  progressC: 'ProgressCircular',
  prgcir: 'ProgressCircular',
  radio: 'Radio',
  rdo: 'Radio',
  radioGroup: 'RadioGroup',
  radiogrp: 'RadioGroup',
  rdogrp: 'RadioGroup',
  rating: 'Rating',
  rate: 'Rating',
  rat: 'Rating',
  range: 'RangeSlider',
  rangeSlider: 'RangeSlider',
  rng: 'RangeSlider',
  rngsld: 'RangeSlider',
  responsive: 'Responsive',
  res: 'Responsive',
  row: 'Row',
  scrollXTransition: 'ScrollXTransition',
  scrxtrn: 'ScrollXTransition',
  scrollYTransition: 'ScrollYTransition',
  scrytrn: 'ScrollYTransition',
  select: 'Select',
  slt: 'Select',
  sheet: 'Sheet',
  sht: 'Sheet',
  simpleTable: 'SimpleTable',
  simtbl: 'SimpleTable',
  skeletonLoader: 'SkeletonLoader',
  skeleton: 'SkeletonLoader',
  skelod: 'SkeletonLoader',
  slideItem: 'SlideItem',
  sliitm: 'SlideItem',
  slideGroup: 'SlideGroup',
  sligrp: 'SlideGroup',
  slider: 'Slider',
  sld: 'Slider',
  snackbar: 'Snackbar',
  sba: 'Snackbar',
  spacer: 'Spacer',
  spc: 'Spacer',
  speedDial: 'SpeedDial',
  spddil: 'SpeedDial',
  stepper: 'Stepper',
  stp: 'Stepper',
  stepperContent: 'StepperContent',
  stpcon: 'StepperContent',
  stepperHeader: 'StepperHeader',
  stphdr: 'StepperHeader',
  stepperItems: 'StepperItems',
  stpitm: 'StepperItems',
  stepperStep: 'StepperStep',
  stpstp: 'StepperStep',
  subheader: 'Subheader',
  subhdr: 'Subheader',
  switch: 'Switch',
  swc: 'Switch',
  tabItem: 'TabItem',
  tabitm: 'TabItem',
  tabReverseTransition: 'TabReverseTransition',
  tabrevtrn: 'TabReverseTransition',
  tabTransition: 'TabTransition',
  tabtrn: 'TabTransition',
  tabs: 'Tabs',
  tab: 'Tab',
  tabsItems: 'TabsItems',
  tabsitms: 'TabsItems',
  text: 'Text',
  txt: 'Text',
  textarea: 'Textarea',
  texta: 'Textarea',
  txta: 'Textarea',
  textField: 'Textfield',
  textfield: 'Textfield',
  txtfld: 'Textfield',
  toolbar: 'Toolbar',
  tlb: 'Toolbar',
  toolbarItems: 'ToolbarItems',
  tlbitms: 'ToolbarItems',
  timePicker: 'TimePicker',
  time: 'TimePicker',
  tim: 'TimePicker',
  treeview: 'Treeview',
  tre: 'Treeview',
  tree: 'Treeview',
  window: 'Window',
  win: 'Window',
  windowItem: 'WindowItem',
  winitm: 'WindowItem'

* event 처리

아래의 예제를 참고하세요. 화면 정의에서는 evnts array를 사용하여 발생 event와 연결되는 method, 지정 value 를 지정하고, <template> 에서 method명을 v-on으로 지정해 줍니다.

<template>
  <v-iterator :dynamicArg="screen" :data="$data" @btnClick="btnClick">
</tempalte>
<script>
  export default {
    data: () => ({
      screen: {
        items: [
          { component: 'container', fluid: true, items: [
            { component: 'btn', color: 'grey lighten-3', itemtext: 'Block Button', style: 'width: 100%;', 
              evnts: [{ event: 'click', method: 'btnClick', value: '10' }]
            },
            { component: 'card', flat: true, items: [
              { component: 'card-text', itemtext: 'textData' }
            ]}
          ]}
        ]
      },
      textData: 'Button clicked!',
      clickedCount: 0
    }),
    methods: {
      btnClick (param) {
        this.addCount(param)
      },
      addCount (param) {
        if (param) this.clickedCount =+ param
        else this.clickedCount++
        this.textData = String('Button clicked!').concat(': ', this.clickedCount)
      }
    }
  }
</script>

추가적인 사용법인 다음을 참고해주세요.

www.npmjs.com/package/v-iterator

v-iterator

Easy iterator component for vuetify 2.x

www.npmjs.com

v-iterator-doc.biznent.com/

 

v-iterator-doc

 

v-iterator-doc.biznent.com

감사합니다.

댓글