[Vuejs]-How to generate computed props on the fly while accessing the Vue instance?

0👍

Thanks to Vue’s Discord Cathrine and skirtle folks, I achieved to get it working!

Here is the thread and here is the SFC example that helped me, especially this code

created () {
  const magicIsShown = computed(() => this.isShown === true || this.isShown === 'true')

  Object.defineProperty(this, 'magicIsShown', {
    get () {
      return magicIsShown.value
    }
  })
}

Using Object.defineProperty(this... is helping keeping the whole state reactive and the computed(() => can reference some other prop (which I am looking at in my case).

Using a JS object could be doable but I have to have it done from the template (it’s a lower barrier to entry).

Still, here is the solution I came up with as a global mixin imported in every component.

// helper functions
const proceedIfStringlean = (propName) => /^(is|can|has|show)+.*/.test(propName)

const stringleanCase = (string) => 'stringlean' + string[0].toUpperCase() + string.slice(1)

const computeStringlean = (value) => {
  if (typeof value == 'string') {
    return value == 'true'
  }
  return value
}

// the actual mixin
const generateStringleans = {
  created() {
    for (const [key, _value] of Object.entries(this.$props)) {
      if (proceedIfStringlean(key)) {
        const stringleanComputed = computed(() => this[key])

        Object.defineProperty(this, stringleanCase(key), {
          get() {
            return computeStringlean(stringleanComputed.value)
          },
          // do not write any `set()` here because this is just an overlay
        })
      }
    }
  },
}

This will scan every .vue component, get the passed props and if those are prefixed with either is|can|has|show, will create a duplicated counter-part with a prefix of stringlean + pass the initial prop into a method (computeStringlean in my case).

Works great, there is no devtools support as expected since we’re wiring it directly in vanilla JS.

1👍

You could use the setup() hook, which receives props as its first argument. Pass the props argument to createDynamicPropsWithTheContext, and spread the result in setup()‘s return (like you had done previously in the computed option):

import { createDynamicPropsWithTheContext } from './props-utils'

export default {
  ⋮
  setup(props) {
    return {
      ...createDynamicPropsWithTheContext(props),
    }
  }
}

demo

0👍

If the whole thing is for avoiding using a :, then you might want to consider using a simple object (or array of objects) as data source. You could just iterate over a list and bind the data to the components generated. In this scenario the only : used are in the objects

const comps = [{
    "can-submit": false,
    "show-modal": true,
    "something-else": false,
  },
  {
    "can-submit": true,
    "show-modal": true,
    "something-else": false,
  },
  {
    "can-submit": false,
    "show-modal": true,
    "something-else": true,
  },
]

const CustomComponent = {
  setup(props, { attrs }) {
    return {
      attrs
    }
  },
  template: `
    <div
      v-bind="attrs"
    >{{ attrs }}</div>
  `
}

const vm = Vue.createApp({
  setup() {
    return {
      comps
    }
  },
  template: `
    <custom-component
      v-for="(item, i) in comps"
      v-bind="item"
    ></custom-component>
  `

})

vm.component('CustomComponent', CustomComponent)

vm.mount('#app')
<script src="https://unpkg.com/vue@3"></script>

<div id="app">{{ message }}</div>

Leave a comment