v-iterator

Easy iterator component for vuetify 2.x

Usage no npm install needed!

<script type="module">
  import vIterator from 'https://cdn.skypack.dev/v-iterator';
</script>

README

v-iterator

Composition of repeatedly used vue UI framework's components. It's declare and controlled by data, you don't have to declear each component at 'template'.

version 1.0.1

  • support vuetify.

version 1.0.6

  • support AG Grid vue.

version 1.0.8

  • suppert AG Chart vue.

version 1.1.0

  • support element-ui.

version 1.1.1

  • bug fix at statement 'if': boolean value does not work that is included in array.

version 1.1.2

  • bug fix : rendering with v-card-subtitle

version 1.1.3

  • minior update: add currency at element-ui

installation

install the npm package:

npm i v-iterator --save

Dependency

Required: Lodash

Optional: Vuetify 2.X . AG-Grid . AG-Chart . Element-Ui

Get started

NPM (ES moduels) / installing the package and Vuetify 2.x from scratch:

vuetify.js:

// src/plugins/vuetify.js

import Vue from 'vue'
import Vuetify from 'vuetify/lib'

Vue.use(Vuetify)

const opts = {}

export default new Vuetify(opts)

element.js

// src/plugins/element.js
import Vue from 'vue'
import ElementUI from 'element-ui'
import lang from 'element-ui/lib/locale/lang/en'
import locale from 'element-ui/lib/locale'
import 'element-ui/lib/theme-chalk/index.css'
locale.use(lang)

Vue.use(ElementUI)

Registry globally in your main.js:

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

// only when using vuetify
import vuetify from '@/plugins/vuetify'

// only when using element-ui
import 'element-ui'

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

// only when using ag-grid
import { AgGridVue } from 'ag-grid-vue'
Vue.component('ag-grid-vue', AgGridVue)
import 'ag-grid-community/dist/styles/ag-grid.css'
import 'ag-grid-community/dist/styles/ag-theme-balham.css'

// only when using ag-chart
import { AgChartsVue } from 'ag-charts-vue'
Vue.component('ag-charts-vue', AgChartsVue)

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

Add runtime compiler option at 'vue.config.js'

module.exports = {
  runtimeCompiler: true
}

Add externals at vue.config.js:

module.exports = {
  configureWebpack: {
    externals: {
      'element-ui': 'element-ui', // if you don't use element-ui, add this line
      'vuetify/lib': 'vuetify/lib' // if you don't use vuetify, add this line
    }
  }
}

How to use

Api is basically same with original component

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

custom attribute 'framework'

  • From version 1.1.0, element-ui framework is supported. So that define framework, this attribute is added.
  • value: vuetify (short key: v), element (short key: el)
  • default: vuetify
  • to change default value: set 'VUE_APP_FRAMEWORK' at your .env file
VUE_APP_FRAMEWORK=element

included vuetify component

  • Almost vuetify components are supported.

component name

  agGrid: 'AG Grid',
  agd: 'AG Grid',
  agChart: 'AG Chart',

  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

Please set method name that related with event at v-on of v-iterator.

<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'}]
            },
            { component: 'card', flat: true, items: [
              { component: 'card-text', itemtext: 'textData' }
            ]}
          ]}
        ]
      },
      textData: 'Button clicked!',
      clickedCount: 0
    }),
    methods: {
      btnClick () {
        this.addCount()
      },
      addCount () {
        this.clickedCount++
        this.textData = String(this.textData).concat(': ', this.clickedCount)
      }
    }
  }
</script>

Slot

If you want to use slot of vuetify component, set like below

use slot without return value of slot

{ 
  component: 'btn', loading: true, itemtext: 'custom loader', 
  slots: [
    { name: 'loader', items: [{ component: 'text', class: 'ma-0 pa-0', itemtext: 'Loading...' }] }
  ]
}

use slot with return value of slot

{
  component: 'carousel', showArrowsOnHover: true
  slots: [
    { name: 'prev', type: 'data', slotDataName: 'slot', items: [
      { component: 'btn', attrs: 'slot.attrs', on: 'slot.on', color: 'success', itemtext: 'previous' }
    ]}
  ]
}

Etc

i18n

If you want to use i18n code at lable or text, just use code name like this.

  {
    component: 'btn', label: 'common.confirm'
  }

or

  {
    component: 'btn', label: this.$t('common.confirm')
  }

reference

When you call child component's function at vuetify, need reference like this.

original vuetify

<template>
  <v-form ref="form" v-model="valid" lazy-validation>
    <v-text-field v-model="text" label="text"/>
    <v-btn @click="save">Validate</v-btn>
  </v-form>
</template>
<script>
export default {
  data () {
    return {
      text: '',
      valid: true
    }
  },
  methods: {
    save () {
      this.$refs.form.validate()
    }
  }
}
</script>

v-iterator

<template>
  <v-iterator ref="iterator", :dynamicArg="screen" :data="data" @save="save"/>
</template>
<script>
export default {
  data () {
    return {
      screen: {
        items: [
          { component: 'form', ref: 'form', model: 'valid', 'lazy-validation': true, 
            items: [
              { component: 'text-field', model: 'text', label: 'text' },
              { component: 'btn', itemtext: 'Validate', evnts: [{event: 'click', method: 'save'}]}
            ]
          }
        ]
      },
      data: {
        text: '',
        valid: true
      }
    }
  },
  methods: {
    save () {
      this.$refs.iterator.getRef('form').validate()
    }
  }
}
</script>

v-for

v-iterator use prop name 'itemsfor' instead of 'v-for'.

original vuetify

<template>
  <v-sheet>
    <v-tabs dark show-arrows>
      <v-tab v-for="i in 30" :key="i">
        Item {{ i }}
      </v-tab>
    </v-tabs>
  </v-sheet>
</template>

v-iterator

<template>
  <v-iterator :dynamicArg="screen">
</template>
<script>
export default {
  data () {
    return {
      screen: {
        items: [
          { component: 'sheet', items: [
            { component: 'tabs', dark: true, 'show-arrows': true,
              itemsfor: 30, subItemName: 'i', items: [
                { component: 'tab', itemtext: {value: i, func: e => `Item ${e}`}}
              ]
            }
          ]}
        ]
      }
    }
  }
}
</script>

v-if

v-if is replaced by 'if' array. All object in 'if' array is connected by 'and' condition.

vuetify

<template>
  <v-text-field v-model="name" label="Name"/>
  <v-text-field v-model="email" label="e-mail"/>
  <v-checkbox v-model="agree" label="Agree">
  <v-btn v-if="name && email && agree === true">Submit</b-btn>
  <v-btn v-else>Clear</v-btn>
</template>
<script>
export default {
 ....
}
</script>

v-iterator

<template>
  <v-iterator :dynamicArg="screen" :data="data">
</template>
<script>
export default {
  data () {
    return {
      screen: {
        items: [
          { component: 'text-field', model: 'name', label: 'Name' },
          { component: 'text-field', model: 'email', label: 'E-mail' },
          { component: 'checkbox', model: 'agree', label: 'Agree' },
          { component: 'btn', if: [{target: 'name'}, {target: 'email'}, {target: 'agree', value: true}], itemtext: 'Submit' },
          { component: 'btn', if: [{target: '_self', value: ({name, email, agree}) => name && email && agree === true, ne: true}], itemtext: 'Clear' }
        ]
      },
      data: {
        name: null,
        email: null,
        agree: false
      }
    }
  }
}
</script>

or

data () {
  return {
    screen: {
      items: [
        { component: 'text-field', model: 'name', label: 'Name' },
        { component: 'text-field', model: 'email', label: 'E-mail' },
        { component: 'checkbox', model: 'agree', label: 'Agree' },
        { component: 'btn', itemtext: this.btnText}
      ]
    },
    data: {
      name: null,
      email: null,
      agree: false
    }
  }
},
computed: {
  btnText () {
    return this.data.name && this.data.email && this.data.agree === true ? 'Submit' : 'Clear'
  }
}

or

data () {
  return {
    screen: {
      items: [
        { component: 'text-field', model: 'name', label: 'Name' },
        { component: 'text-field', model: 'email', label: 'E-mail' },
        { component: 'checkbox', model: 'agree', label: 'Agree' },
        { component: 'btn', itemtext: 'btnText' }
      ]
    },
    data: {
      name: null,
      email: null,
      agree: false,
      btnText: 'Clear'
    }
  }
},
watch: {
  'data.name' () {
    this.getBtnText()
  },
  'data.email' () {
    this.getBtnText()
  },
  'data.agree' () {
    this.getBtnText()
  }
},
methods: {
  getBtnText () {
    this.data.btnText = this.data.name && this.data.email && this.data.agree === true ? 'Submit' : 'Clear'
  }
}